Search in sources :

Example 1 with FbxCluster

use of com.jme3.scene.plugins.fbx.anim.FbxCluster in project jmonkeyengine by jMonkeyEngine.

the class SceneLoader method loadDeformer.

private FbxObject loadDeformer(FbxElement element) {
    String type = (String) element.properties.get(2);
    switch(type) {
        case "Skin":
            FbxSkin skinData = new FbxSkin(this, element);
            skinMap.put(skinData.id, skinData);
            return skinData;
        case "Cluster":
            FbxCluster clusterData = new FbxCluster(this, element);
            return clusterData;
    }
    return null;
}
Also used : FbxCluster(com.jme3.scene.plugins.fbx.objects.FbxCluster) FbxSkin(com.jme3.scene.plugins.fbx.objects.FbxSkin)

Example 2 with FbxCluster

use of com.jme3.scene.plugins.fbx.anim.FbxCluster in project jmonkeyengine by jMonkeyEngine.

the class FbxMesh method applyCluster.

public void applyCluster(FbxCluster cluster) {
    if (boneIndices == null) {
        boneIndices = new ArrayList[positions.length];
        boneWeights = new ArrayList[positions.length];
    }
    FbxLimbNode limb = cluster.getLimb();
    Bone bone = limb.getJmeBone();
    Skeleton skeleton = limb.getSkeletonHolder().getJmeSkeleton();
    int boneIndex = skeleton.getBoneIndex(bone);
    int[] positionIndices = cluster.getVertexIndices();
    double[] weights = cluster.getWeights();
    for (int i = 0; i < positionIndices.length; i++) {
        int positionIndex = positionIndices[i];
        float boneWeight = (float) weights[i];
        ArrayList<Integer> boneIndicesForVertex = boneIndices[positionIndex];
        ArrayList<Float> boneWeightsForVertex = boneWeights[positionIndex];
        if (boneIndicesForVertex == null) {
            boneIndicesForVertex = new ArrayList<Integer>();
            boneWeightsForVertex = new ArrayList<Float>();
            boneIndices[positionIndex] = boneIndicesForVertex;
            boneWeights[positionIndex] = boneWeightsForVertex;
        }
        boneIndicesForVertex.add(boneIndex);
        boneWeightsForVertex.add(boneWeight);
    }
}
Also used : Skeleton(com.jme3.animation.Skeleton) Bone(com.jme3.animation.Bone) FbxLimbNode(com.jme3.scene.plugins.fbx.anim.FbxLimbNode)

Example 3 with FbxCluster

use of com.jme3.scene.plugins.fbx.anim.FbxCluster in project jmonkeyengine by jMonkeyEngine.

the class FbxMesh method toJmeObject.

@Override
protected IntMap<Mesh> toJmeObject() {
    // Load clusters from SkinDeformer
    if (skinDeformer != null) {
        for (FbxCluster cluster : skinDeformer.getJmeObject()) {
            applyCluster(cluster);
        }
    }
    IrMesh irMesh = toIRMesh();
    // Trim bone weights to 4 weights per vertex.
    IrUtils.trimBoneWeights(irMesh);
    // Convert tangents / binormals to tangents with parity.
    IrUtils.toTangentsWithParity(irMesh);
    // Triangulate quads.
    IrUtils.triangulate(irMesh);
    // Split meshes by material indices.
    IntMap<IrMesh> irMeshes = IrUtils.splitByMaterial(irMesh);
    // Create a jME3 Mesh for each material index.
    IntMap<Mesh> jmeMeshes = new IntMap<Mesh>();
    for (IntMap.Entry<IrMesh> irMeshEntry : irMeshes) {
        Mesh jmeMesh = IrUtils.convertIrMeshToJmeMesh(irMeshEntry.getValue());
        jmeMeshes.put(irMeshEntry.getKey(), jmeMesh);
    }
    if (jmeMeshes.size() == 0) {
        // When will this actually happen? Not sure.
        logger.log(Level.WARNING, "Empty FBX mesh found (unusual).");
    }
    // It makes sense only if the mesh uses a single material!
    if (jmeMeshes.containsKey(-1) && jmeMeshes.size() > 1) {
        logger.log(Level.WARNING, "Mesh has polygons with no material " + "indices (unusual) - they will use material index 0.");
    }
    return jmeMeshes;
}
Also used : FbxCluster(com.jme3.scene.plugins.fbx.anim.FbxCluster) IntMap(com.jme3.util.IntMap) IrMesh(com.jme3.scene.plugins.IrMesh) Mesh(com.jme3.scene.Mesh) IrMesh(com.jme3.scene.plugins.IrMesh)

Example 4 with FbxCluster

use of com.jme3.scene.plugins.fbx.anim.FbxCluster in project jmonkeyengine by jMonkeyEngine.

the class FbxNode method getPreferredParent.

/**
     * If this geometry node is deformed by a skeleton, this
     * returns the node containing the skeleton.
     * 
     * In jME3, a mesh can be deformed by a skeleton only if it is 
     * a child of the node containing the skeleton. However, this
     * is not a requirement in FBX, so we have to modify the scene graph
     * of the loaded model to adjust for this.
     * This happens automatically in 
     * {@link #createScene(com.jme3.scene.plugins.fbx.node.FbxNode)}.
     * 
     * @return The model this node would like to be a child of, or null
     * if no preferred parent.
     */
public FbxNode getPreferredParent() {
    if (!(nodeAttribute instanceof FbxMesh)) {
        return null;
    }
    FbxMesh fbxMesh = (FbxMesh) nodeAttribute;
    FbxSkinDeformer deformer = fbxMesh.getSkinDeformer();
    FbxNode preferredParent = null;
    if (deformer != null) {
        for (FbxCluster cluster : deformer.getJmeObject()) {
            FbxLimbNode limb = cluster.getLimb();
            if (preferredParent == null) {
                preferredParent = limb.getSkeletonHolder();
            } else if (preferredParent != limb.getSkeletonHolder()) {
                logger.log(Level.WARNING, "A mesh is being deformed by multiple skeletons. " + "Only one skeleton will work, ignoring other skeletons.");
            }
        }
    }
    return preferredParent;
}
Also used : FbxCluster(com.jme3.scene.plugins.fbx.anim.FbxCluster) FbxMesh(com.jme3.scene.plugins.fbx.mesh.FbxMesh) FbxSkinDeformer(com.jme3.scene.plugins.fbx.anim.FbxSkinDeformer) FbxLimbNode(com.jme3.scene.plugins.fbx.anim.FbxLimbNode)

Example 5 with FbxCluster

use of com.jme3.scene.plugins.fbx.anim.FbxCluster in project jmonkeyengine by jMonkeyEngine.

the class FbxSkin method generateBoneData.

private int generateBoneData(Mesh mesh, FbxMesh fbxMesh) {
    // Create bone buffers
    FloatBuffer boneWeightData = BufferUtils.createFloatBuffer(fbxMesh.vCount * 4);
    ByteBuffer boneIndicesData = BufferUtils.createByteBuffer(fbxMesh.vCount * 4);
    mesh.setBuffer(VertexBuffer.Type.BoneWeight, 4, boneWeightData);
    mesh.setBuffer(VertexBuffer.Type.BoneIndex, 4, boneIndicesData);
    mesh.getBuffer(VertexBuffer.Type.BoneWeight).setUsage(Usage.CpuOnly);
    mesh.getBuffer(VertexBuffer.Type.BoneIndex).setUsage(Usage.CpuOnly);
    VertexBuffer weightsHW = new VertexBuffer(Type.HWBoneWeight);
    VertexBuffer indicesHW = new VertexBuffer(Type.HWBoneIndex);
    // Setting usage to CpuOnly so that the buffer is not send empty to the GPU
    indicesHW.setUsage(Usage.CpuOnly);
    weightsHW.setUsage(Usage.CpuOnly);
    mesh.setBuffer(weightsHW);
    mesh.setBuffer(indicesHW);
    int bonesLimitExceeded = 0;
    // Accumulate skin bones influence into mesh buffers
    for (FbxNode limb : bones) {
        FbxCluster cluster = limb.skinToCluster.get(id);
        if (cluster == null || cluster.indexes == null || cluster.weights == null || cluster.indexes.length != cluster.weights.length)
            continue;
        if (limb.boneIndex > 255)
            throw new AssetLoadException("Bone index can't be packed into byte");
        for (int i = 0; i < cluster.indexes.length; ++i) {
            int vertexIndex = cluster.indexes[i];
            if (vertexIndex >= fbxMesh.reverseVertexMap.size())
                throw new AssetLoadException("Invalid skinning vertex index. Unexpected index lookup " + vertexIndex + " from " + fbxMesh.reverseVertexMap.size());
            List<Integer> dstVertices = fbxMesh.reverseVertexMap.get(vertexIndex);
            for (int j = 0; j < dstVertices.size(); ++j) {
                int v = dstVertices.get(j);
                // Append bone index and weight to vertex
                int offset;
                int smalestOffset = 0;
                float w = 0;
                float smalestW = Float.MAX_VALUE;
                for (offset = v * 4; offset < v * 4 + 4; ++offset) {
                    w = boneWeightData.get(offset);
                    if (w == 0)
                        break;
                    if (w < smalestW) {
                        smalestW = w;
                        smalestOffset = offset;
                    }
                }
                if (w == 0) {
                    boneWeightData.put(offset, (float) cluster.weights[i]);
                    boneIndicesData.put(offset, (byte) limb.boneIndex);
                } else {
                    if ((float) cluster.weights[i] > smalestW) {
                        // If current weight more than smallest, discard smallest
                        boneWeightData.put(smalestOffset, (float) cluster.weights[i]);
                        boneIndicesData.put(smalestOffset, (byte) limb.boneIndex);
                    }
                    bonesLimitExceeded++;
                }
            }
        }
    }
    if (bonesLimitExceeded > 0)
        scene.warning("Skinning support max 4 bone per vertex. Exceeding data of " + bonesLimitExceeded + " weights in mesh bones will be discarded");
    // Postprocess bones weights
    int maxWeightsPerVert = 0;
    boneWeightData.rewind();
    for (int v = 0; v < fbxMesh.vCount; v++) {
        float w0 = boneWeightData.get();
        float w1 = boneWeightData.get();
        float w2 = boneWeightData.get();
        float w3 = boneWeightData.get();
        if (w3 != 0) {
            maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
        } else if (w2 != 0) {
            maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
        } else if (w1 != 0) {
            maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
        } else if (w0 != 0) {
            maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
        }
        float sum = w0 + w1 + w2 + w3;
        if (sum != 1f) {
            // normalize weights
            float mult = (sum != 0) ? (1f / sum) : 0;
            boneWeightData.position(v * 4);
            boneWeightData.put(w0 * mult);
            boneWeightData.put(w1 * mult);
            boneWeightData.put(w2 * mult);
            boneWeightData.put(w3 * mult);
        }
    }
    return maxWeightsPerVert;
}
Also used : VertexBuffer(com.jme3.scene.VertexBuffer) FloatBuffer(java.nio.FloatBuffer) ByteBuffer(java.nio.ByteBuffer) AssetLoadException(com.jme3.asset.AssetLoadException)

Aggregations

FbxCluster (com.jme3.scene.plugins.fbx.anim.FbxCluster)2 FbxLimbNode (com.jme3.scene.plugins.fbx.anim.FbxLimbNode)2 Bone (com.jme3.animation.Bone)1 Skeleton (com.jme3.animation.Skeleton)1 AssetLoadException (com.jme3.asset.AssetLoadException)1 Mesh (com.jme3.scene.Mesh)1 VertexBuffer (com.jme3.scene.VertexBuffer)1 IrMesh (com.jme3.scene.plugins.IrMesh)1 FbxSkinDeformer (com.jme3.scene.plugins.fbx.anim.FbxSkinDeformer)1 FbxMesh (com.jme3.scene.plugins.fbx.mesh.FbxMesh)1 FbxCluster (com.jme3.scene.plugins.fbx.objects.FbxCluster)1 FbxSkin (com.jme3.scene.plugins.fbx.objects.FbxSkin)1 IntMap (com.jme3.util.IntMap)1 ByteBuffer (java.nio.ByteBuffer)1 FloatBuffer (java.nio.FloatBuffer)1