private static void PopulateArray(float[] masterArray, AttributeArrayConfig from, List <float[]> to) { if (from.stride == 0) { throw new ClydeDataReadException("An AttributeArrayConfig has a stride of zero! This will result in an infinite loop."); } for (int index = 0; index < from.offset; index += from.stride) { float[] data = masterArray.Skip(index).Take(4).ToArray(); to.Add(data); } }
/// <summary> /// Translates the given <see cref="GeometryConfig"/> into a <see cref="Model3D"/>. /// </summary> /// <param name="geometry">The <see cref="GeometryConfig"/> storing the applicable data.</param> /// <param name="geometryIndex">A unique identifier generated by the caller of this method used to identify this geometry. If this ID is seen multiple times, the geometry won't be processed and instead the returned model will reference the existing data.</param> /// <param name="rootNode">If called by the <see cref="ArticulatedConfigHandler"/>, this is the root node of the associated <see cref="ArticulatedConfig"/>.</param> /// <returns></returns> public static Model3D GetGeometryInformation(GeometryConfig geometry, string geometryIndex, Node rootNode = null) { // stored // indexed stored // skinned indexed stored Model3D model = new Model3D(); MeshData existingMeshData = MeshData.MeshDataBindings.GetOrDefault(geometryIndex); // model.Mesh = existingMeshData; if (existingMeshData == null) { // Go from the most complex to the least complex, since (presumably) simpler classes can nest. // Let's pull the basic arrays from the geometry data. MeshData meshData = new MeshData(geometryIndex); Debug.WriteLine("Created a new MeshData. Index: " + geometryIndex); float[] vertices; float[] uvs; float[] normals; ushort[] indices; if (geometry is SkinnedIndexedStored skinnedIndexedStored) { vertices = skinnedIndexedStored.getFloatArray(false, skinnedIndexedStored.vertexArray); uvs = skinnedIndexedStored.getFloatArray(false, skinnedIndexedStored.texCoordArrays); normals = skinnedIndexedStored.getFloatArray(false, skinnedIndexedStored.normalArray); indices = GetFromShortBuffer(skinnedIndexedStored.indices); // Also need to handle skinning. if (skinnedIndexedStored.mode != Mode.TRIANGLES) { XanLogger.WriteLine("WARNING: This Articulated model may not export properly! Its mode isn't TRIANGLES, and other behaviors (e.g. TRIANGLESTRIP) haven't been coded in yet! The method used for TRIANGLES will be applied anyway just to try something.", color: Color.DarkGoldenrod); } AttributeArrayConfig[] allArrays = skinnedIndexedStored.vertexAttribArrays; AttributeArrayConfig boneIndicesAttr = GetArrayByName(allArrays, "boneIndices"); AttributeArrayConfig boneWeightsAttr = GetArrayByName(allArrays, "boneWeights"); ushort[] boneIndicesS = skinnedIndexedStored.getFloatArray(false, boneIndicesAttr).ToUshortArray(); float[] boneWeightsF = skinnedIndexedStored.getFloatArray(false, boneWeightsAttr); // Now let's consider this literally: indices and weights for bones are vertex *attribute* arrays. // So presumably this means that we iterate through the indices. // The vertex at vertices[index] is part of bone boneIndices[index]. The index returned by boneIndices is the index of a name. // A vertex can be in up to four groups at once, hence why these are in groups of quads. // If the bone group is 0, it should be ignored. // Apparently, this concept went way over my head in SK Animator Tools and it was a disaster. Part of why the code was so horrifying. // Now for ease in indexing, I'm going to bump all of the elements in the bone name array forward by 1, then set index 0 to null. string[] boneNames = new string[skinnedIndexedStored.bones.Length + 1]; boneNames[0] = null; for (int idx = 0; idx < boneNames.Length - 1; idx++) { boneNames[idx + 1] = skinnedIndexedStored.bones[idx]; } ushort[,] boneIndices = boneIndicesS.As2D(4); float[,] boneWeights = boneWeightsF.As2D(4); meshData.BoneIndicesNative = boneIndicesS; meshData.BoneWeightsNative = boneWeightsF; meshData.BoneNames = boneNames; meshData.BoneIndices = boneIndices; meshData.BoneWeights = boneWeights; meshData.HasBoneData = true; if (rootNode == null) { // The model supplied no root node. This can happen for implementations (e.g. StaticConfig) that has a GeometryConfig // that is skinned. // PRESUMABLY this means that it uses an external reference for its root node (e.g. the knight model has a common root) // But in my case, I really can't read that right now. meshData.UsesExternalRoot = true; } else { meshData.SetBones(rootNode); } } else if (geometry is IndexedStored indexedStored) { vertices = indexedStored.getFloatArray(false, indexedStored.vertexArray); uvs = indexedStored.getFloatArray(false, indexedStored.texCoordArrays); normals = indexedStored.getFloatArray(false, indexedStored.normalArray); indices = GetFromShortBuffer(indexedStored.indices); meshData.HasBoneData = false; } else if (geometry is Stored stored) { vertices = stored.getFloatArray(false, stored.vertexArray); uvs = stored.getFloatArray(false, stored.texCoordArrays); normals = stored.getFloatArray(false, stored.normalArray); indices = new ushort[vertices.Length]; for (ushort i = 0; i < indices.Length; i++) { indices[i] = i; } meshData.HasBoneData = false; } else { throw new InvalidOperationException("The GeometryConfig type is unknown! Type: " + geometry.getClass().getName()); } meshData.Vertices.SetFrom(Vector3.FromFloatArray(vertices)); meshData.UVs.SetFrom(Vector2.FromFloatArray(uvs)); meshData.Normals.SetFrom(Vector3.FromFloatArray(normals)); meshData.Indices.SetFrom(indices); meshData.ConstructGroups(); existingMeshData = meshData; } model.Mesh = existingMeshData; return(model); }
public BoneDataContainer(float[] masterArray, AttributeArrayConfig bIndices, AttributeArrayConfig bWeights) { PopulateArray(masterArray, bIndices, BoneIndices); PopulateArray(masterArray, bWeights, BoneWeights); }