public Geometry[] readMeshPrimitives(int meshIndex) throws IOException {
        Geometry[] geomArray = (Geometry[]) fetchFromCache("meshes", meshIndex, Object.class);
        if (geomArray == null) {                
            JsonObject meshData = meshes.get(meshIndex).getAsJsonObject();
            JsonArray primitives = meshData.getAsJsonArray("primitives");
            assertNotNull(primitives, "Can't find any primitives in mesh " + meshIndex);
            String name = getAsString(meshData, "name");

            geomArray = new Geometry[primitives.size()];
            int index = 0;
            for (JsonElement primitive : primitives) {
                JsonObject meshObject = primitive.getAsJsonObject();
                Mesh mesh = new Mesh();
                addToCache("mesh", 0, mesh, 1);
                Integer mode = getAsInteger(meshObject, "mode");
                mesh.setMode(getMeshMode(mode));
                Integer indices = getAsInteger(meshObject, "indices");
                if (indices != null) {
                    mesh.setBuffer(readAccessorData(indices, new VertexBufferPopulator(VertexBuffer.Type.Index)));
                }
                JsonObject attributes = meshObject.getAsJsonObject("attributes");
                assertNotNull(attributes, "No attributes defined for mesh " + mesh);

                boolean useVertexColors = false;

                skinBuffers.clear();

                for (Map.Entry<String, JsonElement> entry : attributes.entrySet()) {
                    // special case for joints and weights buffer.
                    // If there are more than 4 bones per vertex, there might be several of them
                    // we need to read them all and to keep only the 4 that have the most weight on the vertex.
                    String bufferType = entry.getKey();
                    if (bufferType.startsWith("JOINTS")) {
                        SkinBuffers buffs = getSkinBuffers(bufferType);
                        SkinBuffers buffer
                                = readAccessorData(entry.getValue().getAsInt(), new JointArrayPopulator());
                        buffs.joints = buffer.joints;
                        buffs.componentSize = buffer.componentSize;
                    } else if (bufferType.startsWith("WEIGHTS")) {
                        SkinBuffers buffs = getSkinBuffers(bufferType);
                        buffs.weights = readAccessorData(entry.getValue().getAsInt(), new FloatArrayPopulator());
                    } else {
                        VertexBuffer vb = readAccessorData(entry.getValue().getAsInt(),
                                new VertexBufferPopulator(getVertexBufferType(bufferType)));
                        if (vb != null) {
                            mesh.setBuffer(vb);
                        }
                    }
                    // if the color buffer is used, we will need to enable vertex colors on the material
                    if (bufferType.startsWith("COLOR")) {
                        useVertexColors = true;
                    }
                }
                handleSkinningBuffers(mesh, skinBuffers);

                if (mesh.getBuffer(VertexBuffer.Type.BoneIndex) != null) {
                    // the mesh has some skinning, let's create needed buffers for HW skinning
                    // creating empty buffers for HW skinning
                    // the buffers will be set up if ever used.
                    VertexBuffer weightsHW = new VertexBuffer(VertexBuffer.Type.HWBoneWeight);
                    VertexBuffer indicesHW = new VertexBuffer(VertexBuffer.Type.HWBoneIndex);
                    // setting usage to cpuOnly so that the buffer is not sent empty to the GPU
                    indicesHW.setUsage(VertexBuffer.Usage.CpuOnly);
                    weightsHW.setUsage(VertexBuffer.Usage.CpuOnly);
                    mesh.setBuffer(weightsHW);
                    mesh.setBuffer(indicesHW);
                    mesh.generateBindPose();
                }

                // Read morph target names
                LinkedList<String> targetNames = new LinkedList<>();
                if (meshData.has("extras") && meshData.getAsJsonObject("extras").has("targetNames")) {
                    JsonArray targetNamesJson = meshData.getAsJsonObject("extras").getAsJsonArray("targetNames");
                    for (JsonElement target : targetNamesJson) {
                        targetNames.add(target.getAsString());
                    }
                }

                // Read morph targets
                JsonArray targets = meshObject.getAsJsonArray("targets");
                if (targets != null) {
                    for (JsonElement target : targets) {
                        MorphTarget morphTarget = new MorphTarget();
                        if (targetNames.size() > 0) {
                            morphTarget.setName(targetNames.pop());
                        }
                        for (Map.Entry<String, JsonElement> entry : target.getAsJsonObject().entrySet()) {
                            String bufferType = entry.getKey();
                            VertexBuffer.Type type = getVertexBufferType(bufferType);
                            VertexBuffer vb = readAccessorData(entry.getValue().getAsInt(),
                                    new VertexBufferPopulator(type));
                            if (vb != null) {
                                morphTarget.setBuffer(type, (FloatBuffer) vb.getData());
                            }
                        }
                        mesh.addMorphTarget(morphTarget);
                    }
                }

                // Read mesh extras
                mesh = customContentManager.readExtensionAndExtras("primitive", meshObject, mesh);
                Geometry geom = new Geometry(null, mesh);

                Integer materialIndex = getAsInteger(meshObject, "material");
                if (materialIndex == null) {
                    geom.setMaterial(defaultMat);
                } else {
                    useNormalsFlag = false;
                    geom.setMaterial(readMaterial(materialIndex));
                    if (geom.getMaterial().getAdditionalRenderState().getBlendMode()
                            == RenderState.BlendMode.Alpha) {
                        // Alpha blending is enabled for this material. Let's place the geom in the transparent bucket.
                        geom.setQueueBucket(RenderQueue.Bucket.Transparent);
                    }
                    if (useNormalsFlag && mesh.getBuffer(VertexBuffer.Type.Tangent) == null) {
                        // No tangent buffer, but there is a normal map, we have to generate them using MikktSpace
                        MikktspaceTangentGenerator.generate(geom);
                    }
                }

                if (useVertexColors) {
                    geom.getMaterial().setBoolean("UseVertexColor", useVertexColors);
                }

                geom.setName(name + "_" + index);
                
                geom.updateModelBound();
                geomArray[index] = geom;
                index++;
            }

            geomArray = customContentManager.readExtensionAndExtras("mesh", meshData, geomArray);

            addToCache("meshes", meshIndex, geomArray, meshes.size());
        }
        // cloning the geoms.
        Geometry[] geoms = new Geometry[geomArray.length];
        for (int i = 0; i < geoms.length; i++) {
            geoms[i] = geomArray[i].clone(false);
        }
        return geoms;
    }
GLTF 2.0 loader
Created by Nehon on 07/08/2017.
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java