private void LinkSkinningData(DRW1 draw_matrix_data, EVP1 envelope_data) { foreach (Shape shape in Shapes) { int vertex_index = 0; shape.VertexDescription.EnableAttribute(ShaderAttributeIds.SkinIndices); shape.VertexDescription.EnableAttribute(ShaderAttributeIds.SkinWeights); foreach (MatrixGroup group in shape.MatrixGroups) { foreach (MeshVertexIndex vertex in group.Indices) { int draw_mat_index = vertex.PosMtxIndex >= 0 ? group.MatrixDataTable.MatrixTable[vertex.PosMtxIndex] : 0; int transform_index = draw_matrix_data.TransformIndexTable[draw_mat_index]; // If this vertex is influenced by mulltiple bones, we build vec4s containing the indices and weights // for each bone that influences it. if (draw_matrix_data.IsPartiallyWeighted[draw_mat_index]) { EVP1.Envelope envelope = envelope_data.Envelopes[transform_index]; // Because we're using vec4s for the skinning data, any vertex with more than 4 bones influencing it // is invalid to us. We're going to assume this never happens, but in the event that it does, this assert // will tell us. Trace.Assert(envelope.NumBones <= 4); Vector4 indices = new Vector4(0, 0, 0, 0); Vector4 weights = new Vector4(0, 0, 0, 0); for (int i = 0; i < envelope.NumBones; i++) { indices[i] = envelope.BoneIndexes[i]; weights[i] = envelope.BoneWeights[i]; } shape.VertexData.SkinIndices.Add(indices); shape.VertexData.SkinWeights.Add(weights); } // If the vertex only has a single bone influencing it, we need to do some extra work. The vertex is stored in // bone space instead of bind space. We need to transform the vertex into bind space so we can work with it more easily. else { Vector4 pos_as_vec4 = new Vector4(shape.VertexData.Position[vertex_index], 1.0f); shape.VertexData.Position[vertex_index] = Vector4.Transform(pos_as_vec4, envelope_data.InverseBindPose[transform_index].Inverted()).Xyz; Vector4 nrm_as_vec4 = new Vector4(shape.VertexData.Normal[vertex_index], 0.0f); Matrix4 nrm_mat = envelope_data.InverseBindPose[transform_index]; nrm_mat.Transpose(); shape.VertexData.Normal[vertex_index] = Vector4.Transform(nrm_as_vec4, nrm_mat).Xyz; shape.VertexData.SkinIndices.Add(new Vector4(transform_index, 0, 0, 0)); shape.VertexData.SkinWeights.Add(new Vector4(1, 0, 0, 0)); } vertex_index++; } } } }
public void Render(Matrix4 viewMatrix, Matrix4 projectionMatrix, Matrix4 modelMatrix) { m_viewMatrix = viewMatrix; m_projMatrix = projectionMatrix; m_modelMatrix = modelMatrix; IList <SkeletonJoint> boneList = (m_currentBoneAnimation != null) ? JNT1Tag.AnimatedJoints : JNT1Tag.BindJoints; Matrix4[] boneTransforms = new Matrix4[boneList.Count]; ApplyBonePositionsToAnimationTransforms(boneList, boneTransforms); // Assume that all bone animations constantly invalidate the skinning. if (m_currentBoneAnimation != null) { m_skinningInvalid = true; } // We'll only transform the position and normal vertices if skinning has been invalidated. if (m_skinningInvalid) { foreach (var shape in SHP1Tag.Shapes) { var transformedPositions = new List <Vector3>(shape.VertexData.Position.Count); var transformedNormals = new List <Vector3>(shape.VertexData.Normal.Count); //List<WLinearColor> colorOverride = new List<WLinearColor>(); for (int i = 0; i < shape.VertexData.Position.Count; i++) { // This is relative to the vertex's original packet's matrix table. ushort posMtxIndex = (ushort)(shape.VertexData.PositionMatrixIndexes[i]); // We need to calculate which packet data table that is. int originalPacketIndex = 0; for (int p = 0; p < shape.MatrixDataTable.Count; p++) { if (i >= shape.MatrixDataTable[p].FirstRelevantVertexIndex && i < shape.MatrixDataTable[p].LastRelevantVertexIndex) { originalPacketIndex = p; break; } } // Now that we know which packet this vertex belongs to, we can get the index from it. // If the Matrix Table index is 0xFFFF then it means "use previous", and we have to // continue backwards until it is no longer 0xFFFF. ushort matrixTableIndex; do { matrixTableIndex = shape.MatrixDataTable[originalPacketIndex].MatrixTable[posMtxIndex]; originalPacketIndex--; } while (matrixTableIndex == 0xFFFF); bool isPartiallyWeighted = DRW1Tag.IsPartiallyWeighted[matrixTableIndex]; ushort indexFromDRW1 = DRW1Tag.TransformIndexTable[matrixTableIndex]; Matrix4 finalMatrix = Matrix4.Zero; if (isPartiallyWeighted) { EVP1.Envelope envelope = EVP1Tag.Envelopes[indexFromDRW1]; for (int b = 0; b < envelope.NumBones; b++) { Matrix4 sm1 = EVP1Tag.InverseBindPose[envelope.BoneIndexes[b]]; Matrix4 sm2 = boneTransforms[envelope.BoneIndexes[b]]; finalMatrix = finalMatrix + Matrix4.Mult(Matrix4.Mult(sm1, sm2), envelope.BoneWeights[b]); } } else { // If the vertex is not weighted then we use a 1:1 movement with the bone matrix. finalMatrix = boneTransforms[indexFromDRW1]; } // Multiply the data from the model file by the finalMatrix to put it in the correct (skinned) position transformedPositions.Add(Vector3.Transform(shape.VertexData.Position[i], finalMatrix)); if (shape.VertexData.Normal.Count > 0) { Vector3 transformedNormal = Vector3.TransformNormal(shape.VertexData.Normal[i], finalMatrix); transformedNormals.Add(transformedNormal); } //colorOverride.Add(isPartiallyWeighted ? WLinearColor.Black : WLinearColor.White); } // Re-upload to the GPU. shape.OverrideVertPos = transformedPositions; //shape.VertexData.Color0 = colorOverride; if (transformedNormals.Count > 0) { shape.OverrideNormals = transformedNormals; } shape.UploadBuffersToGPU(true); } m_skinningInvalid = false; } //if (WInput.GetKeyDown(System.Windows.Input.Key.O)) // m_shapeIndex--; //if (WInput.GetKeyUp(System.Windows.Input.Key.P)) // m_shapeIndex++; m_shapeIndex = WMath.Clamp(m_shapeIndex, 0, SHP1Tag.ShapeCount - 1); RenderMeshRecursive(INF1Tag.HierarchyRoot); // We're going to restore some semblance of state after rendering ourselves, as models often modify weird and arbitrary GX values. GL.Enable(EnableCap.Blend); GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); GL.Enable(EnableCap.CullFace); GL.CullFace(CullFaceMode.Back); GL.Enable(EnableCap.DepthTest); GL.DepthMask(true); GL.Enable(EnableCap.Dither); }