private void InitSkinInfo(Assimp.Mesh mesh, AssimpSceneContainer container) { var boneIDs = new uvec4[mesh.VertexCount]; var boneWeights = new vec4[mesh.VertexCount]; AllBoneInfos allBones = container.GetAllBoneInfos(); Dictionary <string, uint> nameIndexDict = allBones.nameIndexDict; for (int i = 0; i < mesh.BoneCount; i++) { Assimp.Bone bone = mesh.Bones[i]; // bones that influence this mesh. uint boneIndex = nameIndexDict[bone.Name]; for (int j = 0; j < bone.VertexWeightCount; j++) { Assimp.VertexWeight vertexWeight = bone.VertexWeights[j]; uint vertexID = vertexWeight.VertexID; for (int t = 0; t < 4; t++) { if (boneWeights[vertexID][t] == 0.0f) // fill in x y z w. { boneIDs[vertexID][t] = boneIndex; boneWeights[vertexID][t] = vertexWeight.Weight; break; } } } } this.boneIDs = boneIDs; this.boneWeights = boneWeights; }
private static void ConvertWeights(List <Node> nodes, Dictionary <int, Assimp.Bone> aiBoneLookup, NodeWeight[][] weights, Assimp.Mesh aiMesh, int vertexBaseIndex) { for (int i = 0; i < weights.Length; i++) { foreach (var nodeWeight in weights[i]) { if (nodeWeight.Weight == 0f) { continue; } if (!aiBoneLookup.TryGetValue(nodeWeight.NodeIndex, out var aiBone)) { var node = nodes[nodeWeight.NodeIndex]; aiMesh.Bones.Add(aiBoneLookup[nodeWeight.NodeIndex] = aiBone = new Assimp.Bone { Name = FormatNodeName(node, nodeWeight.NodeIndex), OffsetMatrix = node.WorldTransform.Inverted().ToAssimp() }); } aiBone.VertexWeights.Add(new Assimp.VertexWeight(vertexBaseIndex + i, nodeWeight.Weight)); } } }
private static void AssignFauxWeights(Assimp.Mesh aiMesh, Node node, int nodeIndex) { var aiBone = new Assimp.Bone { Name = FormatNodeName(node, nodeIndex), OffsetMatrix = node.WorldTransform.Inverted().ToAssimp() }; for (int i = 0; i < aiMesh.VertexCount; i++) { aiBone.VertexWeights.Add(new Assimp.VertexWeight(i, 1f)); } aiMesh.Bones.Add(aiBone); }
private AllBoneInfos InitBonesInfo(Assimp.Scene aiScene) { List <BoneInfo> boneInfos = new List <BoneInfo>(); var nameIndexDict = new Dictionary <string, uint>(); for (int i = 0; i < aiScene.MeshCount; i++) { Assimp.Mesh mesh = aiScene.Meshes[i]; for (int j = 0; j < mesh.BoneCount; j++) { Assimp.Bone bone = mesh.Bones[j]; string boneName = bone.Name; if (!nameIndexDict.ContainsKey(boneName)) { var boneInfo = new BoneInfo(bone); boneInfos.Add(boneInfo); nameIndexDict.Add(boneName, (uint)(boneInfos.Count - 1)); } } } return(new AllBoneInfos(boneInfos.ToArray(), nameIndexDict)); }
public static Assimp.Scene CreateAssimpScene(this IGeometryModel model, Assimp.AssimpContext context, string formatId) { var scale = ModelViewerPlugin.Settings.GeometryScale; //either Assimp or collada has issues when there is a name conflict const string bonePrefix = "~"; const string geomPrefix = "-"; const string scenPrefix = "$"; var scene = new Assimp.Scene(); scene.RootNode = new Assimp.Node($"{scenPrefix}{model.Name}"); //Assimp is Y-up in inches by default - this forces it to export as Z-up in meters scene.RootNode.Transform = (CoordinateSystem.HaloCEX * ModelViewerPlugin.Settings.AssimpScale).ToAssimp4x4(); #region Nodes var allNodes = new List <Assimp.Node>(); foreach (var node in model.Nodes) { var result = new Assimp.Node($"{bonePrefix}{node.Name}"); var q = new System.Numerics.Quaternion(node.Rotation.X, node.Rotation.Y, node.Rotation.Z, node.Rotation.W); var mat = System.Numerics.Matrix4x4.CreateFromQuaternion(q); mat.Translation = new System.Numerics.Vector3(node.Position.X * scale, node.Position.Y * scale, node.Position.Z * scale); result.Transform = mat.ToAssimp4x4(); allNodes.Add(result); } for (int i = 0; i < model.Nodes.Count; i++) { var node = model.Nodes[i]; if (node.ParentIndex >= 0) { allNodes[node.ParentIndex].Children.Add(allNodes[i]); } else { scene.RootNode.Children.Add(allNodes[i]); } } #endregion var meshLookup = new List <int>(); #region Meshes for (int i = 0; i < model.Meshes.Count; i++) { var geom = model.Meshes[i]; if (geom.Submeshes.Count == 0) { meshLookup.Add(-1); continue; } meshLookup.Add(scene.MeshCount); foreach (var sub in geom.Submeshes) { var m = new Assimp.Mesh($"mesh{i:D3}"); var indices = geom.Indicies.Skip(sub.IndexStart).Take(sub.IndexLength); var minIndex = indices.Min(); var maxIndex = indices.Max(); var vertCount = maxIndex - minIndex + 1; if (geom.IndexFormat == IndexFormat.TriangleStrip) { indices = indices.Unstrip(); } indices = indices.Select(x => x - minIndex); var vertices = geom.Vertices.Skip(minIndex).Take(vertCount); if (geom.BoundsIndex >= 0) { vertices = vertices.Select(v => (IVertex) new CompressedVertex(v, model.Bounds[geom.BoundsIndex.Value])); } int vIndex = -1; var boneLookup = new Dictionary <int, Assimp.Bone>(); foreach (var v in vertices) { vIndex++; if (v.Position.Count > 0) { m.Vertices.Add(v.Position[0].ToAssimp3D(scale)); //some Halo shaders use position W as the colour alpha - add it to a colour channel to preserve it //also assimp appears to have issues exporting obj when a colour channel exists so only do this for collada if (formatId == "collada" && v.Color.Count == 0 && !float.IsNaN(v.Position[0].W)) { m.VertexColorChannels[0].Add(new Assimp.Color4D { R = v.Position[0].W }); } } if (v.Normal.Count > 0) { m.Normals.Add(v.Normal[0].ToAssimp3D()); } if (v.TexCoords.Count > 0) { m.TextureCoordinateChannels[0].Add(v.TexCoords[0].ToAssimpUV()); } if (geom.VertexWeights == VertexWeights.None && !geom.NodeIndex.HasValue) { continue; } #region Vertex Weights var weights = new List <Tuple <int, float> >(4); if (geom.NodeIndex.HasValue) { weights.Add(Tuple.Create <int, float>(geom.NodeIndex.Value, 1)); } else if (geom.VertexWeights == VertexWeights.Skinned) { var ind = v.BlendIndices[0]; var wt = v.BlendWeight[0]; if (wt.X > 0) { weights.Add(Tuple.Create((int)ind.X, wt.X)); } if (wt.Y > 0) { weights.Add(Tuple.Create((int)ind.Y, wt.Y)); } if (wt.Z > 0) { weights.Add(Tuple.Create((int)ind.Z, wt.Z)); } if (wt.W > 0) { weights.Add(Tuple.Create((int)ind.W, wt.W)); } } foreach (var val in weights) { Assimp.Bone b; if (boneLookup.ContainsKey(val.Item1)) { b = boneLookup[val.Item1]; } else { var t = model.Nodes[val.Item1].OffsetTransform; t.M41 *= scale; t.M42 *= scale; t.M43 *= scale; b = new Assimp.Bone { Name = bonePrefix + model.Nodes[val.Item1].Name, OffsetMatrix = t.ToAssimp4x4() }; m.Bones.Add(b); boneLookup.Add(val.Item1, b); } b.VertexWeights.Add(new Assimp.VertexWeight(vIndex, val.Item2)); } #endregion } m.SetIndices(indices.ToArray(), 3); m.MaterialIndex = sub.MaterialIndex; scene.Meshes.Add(m); } } #endregion #region Regions foreach (var reg in model.Regions) { var regNode = new Assimp.Node($"{geomPrefix}{reg.Name}"); foreach (var perm in reg.Permutations) { var meshStart = meshLookup[perm.MeshIndex]; if (meshStart < 0) { continue; } var permNode = new Assimp.Node($"{geomPrefix}{perm.Name}"); if (perm.TransformScale != 1 || !perm.Transform.IsIdentity) { permNode.Transform = Assimp.Matrix4x4.FromScaling(new Assimp.Vector3D(perm.TransformScale)) * perm.Transform.ToAssimp4x4(scale); } var meshCount = Enumerable.Range(perm.MeshIndex, perm.MeshCount).Sum(i => model.Meshes[i].Submeshes.Count); permNode.MeshIndices.AddRange(Enumerable.Range(meshStart, meshCount)); regNode.Children.Add(permNode); } if (regNode.ChildCount > 0) { scene.RootNode.Children.Add(regNode); } } #endregion #region Materials foreach (var mat in model.Materials) { var m = new Assimp.Material { Name = mat?.Name ?? "unused" }; //prevent max from making every material super shiny m.ColorEmissive = m.ColorReflective = m.ColorSpecular = new Assimp.Color4D(0, 0, 0, 1); m.ColorDiffuse = m.ColorTransparent = new Assimp.Color4D(1); //max only seems to care about diffuse var dif = mat?.Submaterials.FirstOrDefault(s => s.Usage == MaterialUsage.Diffuse); if (dif != null) { var suffix = dif.Bitmap.SubmapCount > 1 ? "[0]" : string.Empty; var filePath = $"{dif.Bitmap.Name}{suffix}.{ModelViewerPlugin.Settings.MaterialExtension}"; //collada spec says it requires URI formatting, and Assimp doesn't do it for us //for some reason "new Uri(filePath, UriKind.Relative)" doesnt change the slashes, have to use absolute uri if (formatId == FormatId.Collada) { filePath = new Uri("X:\\", UriKind.Absolute).MakeRelativeUri(new Uri(System.IO.Path.Combine("X:\\", filePath))).ToString(); } m.TextureDiffuse = new Assimp.TextureSlot { BlendFactor = 1, FilePath = filePath, TextureType = Assimp.TextureType.Diffuse }; } scene.Materials.Add(m); } #endregion return(scene); }
public override Assimp.Node assimpExport(ref Assimp.Scene scn, ref Dictionary <int, int> meshImportStatus) { Assimp.Mesh amesh = new Assimp.Mesh(); Assimp.Node node; amesh.Name = name; int meshHash = meshVao.GetHashCode(); //TESTING if (scn.MeshCount > 20) { node = base.assimpExport(ref scn, ref meshImportStatus); return(node); } if (!meshImportStatus.ContainsKey(meshHash)) //if (false) { meshImportStatus[meshHash] = scn.MeshCount; int vertcount = metaData.vertrend_graphics - metaData.vertrstart_graphics + 1; MemoryStream vms = new MemoryStream(gobject.meshDataDict[metaData.Hash].vs_buffer); MemoryStream ims = new MemoryStream(gobject.meshDataDict[metaData.Hash].is_buffer); BinaryReader vbr = new BinaryReader(vms); BinaryReader ibr = new BinaryReader(ims); //Initialize Texture Component Channels if (gobject.bufInfo[1] != null) { List <Assimp.Vector3D> textureChannel = new List <Assimp.Vector3D>(); amesh.TextureCoordinateChannels.Append(textureChannel); amesh.UVComponentCount[0] = 2; } //Generate bones only for the joints related to the mesh Dictionary <int, Assimp.Bone> localJointDict = new Dictionary <int, Assimp.Bone>(); //Export Bone Structure if (Skinned) //if (false) { for (int i = 0; i < meshVao.BoneRemapIndicesCount; i++) { int joint_id = meshVao.BoneRemapIndices[i]; //Fetch name Joint relJoint = null; foreach (Joint jnt in parentScene.jointDict.Values) { if (jnt.jointIndex == joint_id) { relJoint = jnt; break; } } //Generate bone Assimp.Bone b = new Assimp.Bone(); if (relJoint != null) { b.Name = relJoint.name; b.OffsetMatrix = MathUtils.convertMatrix(relJoint.invBMat); } localJointDict[i] = b; amesh.Bones.Add(b); } } //Write geometry info vbr.BaseStream.Seek(0, SeekOrigin.Begin); for (int i = 0; i < vertcount; i++) { Assimp.Vector3D v, vN; for (int j = 0; j < gobject.bufInfo.Count; j++) { bufInfo buf = gobject.bufInfo[j]; if (buf is null) { continue; } switch (buf.semantic) { case 0: //vPosition { switch (buf.type) { case VertexAttribPointerType.HalfFloat: uint v1 = vbr.ReadUInt16(); uint v2 = vbr.ReadUInt16(); uint v3 = vbr.ReadUInt16(); uint v4 = vbr.ReadUInt16(); //Transform vector with worldMatrix v = new Assimp.Vector3D(Utils.Half.decompress(v1), Utils.Half.decompress(v2), Utils.Half.decompress(v3)); break; case VertexAttribPointerType.Float: //This is used in my custom vbos float f1 = vbr.ReadSingle(); float f2 = vbr.ReadSingle(); float f3 = vbr.ReadSingle(); //Transform vector with worldMatrix v = new Assimp.Vector3D(f1, f2, f3); break; default: throw new Exception("Unimplemented Vertex Type"); } amesh.Vertices.Add(v); break; } case 1: //uvPosition { Assimp.Vector3D uv; uint v1 = vbr.ReadUInt16(); uint v2 = vbr.ReadUInt16(); uint v3 = vbr.ReadUInt16(); uint v4 = vbr.ReadUInt16(); //uint v4 = Convert.ToUInt16(vbr.ReadUInt16()); uv = new Assimp.Vector3D(Utils.Half.decompress(v1), Utils.Half.decompress(v2), 0.0f); amesh.TextureCoordinateChannels[0].Add(uv); //Add directly to the first channel break; } case 2: //nPosition case 3: //tPosition { switch (buf.type) { case (VertexAttribPointerType.Float): float f1, f2, f3; f1 = vbr.ReadSingle(); f2 = vbr.ReadSingle(); f3 = vbr.ReadSingle(); vN = new Assimp.Vector3D(f1, f2, f3); break; case (VertexAttribPointerType.HalfFloat): uint v1, v2, v3; v1 = vbr.ReadUInt16(); v2 = vbr.ReadUInt16(); v3 = vbr.ReadUInt16(); vN = new Assimp.Vector3D(Utils.Half.decompress(v1), Utils.Half.decompress(v2), Utils.Half.decompress(v3)); break; case (VertexAttribPointerType.Int2101010Rev): int i1, i2, i3; uint value; byte[] a32 = new byte[4]; a32 = vbr.ReadBytes(4); value = BitConverter.ToUInt32(a32, 0); //Convert Values i1 = _2sComplement.toInt((value >> 00) & 0x3FF, 10); i2 = _2sComplement.toInt((value >> 10) & 0x3FF, 10); i3 = _2sComplement.toInt((value >> 20) & 0x3FF, 10); //int i4 = _2sComplement.toInt((value >> 30) & 0x003, 10); float norm = (float)Math.Sqrt(i1 * i1 + i2 * i2 + i3 * i3); vN = new Assimp.Vector3D(Convert.ToSingle(i1) / norm, Convert.ToSingle(i2) / norm, Convert.ToSingle(i3) / norm); //Debug.WriteLine(vN); break; default: throw new Exception("UNIMPLEMENTED NORMAL TYPE. PLEASE REPORT"); } if (j == 2) { amesh.Normals.Add(vN); } else if (j == 3) { amesh.Tangents.Add(vN); amesh.BiTangents.Add(new Assimp.Vector3D(0.0f, 0.0f, 1.0f)); } break; } case 4: //bPosition vbr.ReadBytes(4); // skip break; case 5: //BlendIndices + BlendWeights { int[] joint_ids = new int[4]; float[] weights = new float[4]; for (int k = 0; k < 4; k++) { joint_ids[k] = vbr.ReadByte(); } for (int k = 0; k < 4; k++) { weights[k] = Utils.Half.decompress(vbr.ReadUInt16()); } if (Skinned) //if (false) { for (int k = 0; k < 4; k++) { int joint_id = joint_ids[k]; Assimp.VertexWeight vw = new Assimp.VertexWeight(); vw.VertexID = i; vw.Weight = weights[k]; localJointDict[joint_id].VertexWeights.Add(vw); } } break; } case 6: break; //Handled by 5 default: { throw new Exception("UNIMPLEMENTED BUF Info. PLEASE REPORT"); break; } } } } //Export Faces //Get indices ibr.BaseStream.Seek(0, SeekOrigin.Begin); bool start = false; int fstart = 0; for (int i = 0; i < metaData.batchcount / 3; i++) { int f1, f2, f3; //NEXT models assume that all gstream meshes have uint16 indices f1 = ibr.ReadUInt16(); f2 = ibr.ReadUInt16(); f3 = ibr.ReadUInt16(); if (!start && this.type != TYPES.COLLISION) { fstart = f1; start = true; } else if (!start && this.type == TYPES.COLLISION) { fstart = 0; start = true; } int f11, f22, f33; f11 = f1 - fstart; f22 = f2 - fstart; f33 = f3 - fstart; Assimp.Face face = new Assimp.Face(); face.Indices.Add(f11); face.Indices.Add(f22); face.Indices.Add(f33); amesh.Faces.Add(face); } scn.Meshes.Add(amesh); } node = base.assimpExport(ref scn, ref meshImportStatus); node.MeshIndices.Add(meshImportStatus[meshHash]); return(node); }
protected void ConvertNodes(Node node, Assimp.Node aiParentNode, ref Matrix4x4 parentNodeWorldTransform) { while (node != null) { ++NodeIndex; Assimp.Node aiNode = null; if (!RemoveNodes) { // Only convert the node if we're actually keeping them. aiNode = new Assimp.Node(FormatNodeName(NodeIndex), aiParentNode) { Transform = ToAssimp(node.Transform) }; aiParentNode.Children.Add(aiNode); } var nodeWorldTransform = node.Transform * parentNodeWorldTransform; var geometry = node.Geometry; if (geometry != null) { var meshCountBefore = Scene.Meshes.Count; ConvertGeometry(geometry, ref nodeWorldTransform); var meshCountAfter = Scene.Meshes.Count; var addedMeshCount = meshCountAfter - meshCountBefore; Debug.Assert(addedMeshCount >= 0); var attachMeshesToParentNode = AttachMeshesToParentNode && aiNode != null; // Add 'mesh' nodes for the newly added meshes for (int i = 0; i < addedMeshCount; i++) { var aiMeshIndex = meshCountBefore + i; var aiMesh = Scene.Meshes[aiMeshIndex]; if (attachMeshesToParentNode) { aiNode.MeshIndices.Add(aiMeshIndex); } else { var aiMeshNode = new Assimp.Node(FormatMeshName(aiMeshIndex)) { Transform = ToAssimp(nodeWorldTransform) }; aiMeshNode.MeshIndices.Add(aiMeshIndex); Scene.RootNode.Children.Add(aiMeshNode); if (!RemoveNodes && !aiMesh.HasBones) { // Add weights to keep the animation hierarchy intact var aiBone = new Assimp.Bone { Name = aiNode.Name, }; for (int j = 0; j < aiMesh.VertexCount; j++) { aiBone.VertexWeights.Add(new Assimp.VertexWeight(j, 1f)); } aiMesh.Bones.Add(aiBone); } } } } if (node.Child != null) { ConvertNodes(node.Child, aiNode, ref nodeWorldTransform); } node = node.Sibling; } }
public static Assimp.Scene ToAssimpScene(RwClumpNode clumpNode) { // Scene var aiScene = new Assimp.Scene(); // RootNode var rootFrame = clumpNode.FrameList[0]; var aiRootNode = new Assimp.Node("RootNode", null); aiRootNode.Transform = new Assimp.Matrix4x4(rootFrame.Transform.M11, rootFrame.Transform.M21, rootFrame.Transform.M31, rootFrame.Transform.M41, rootFrame.Transform.M12, rootFrame.Transform.M22, rootFrame.Transform.M32, rootFrame.Transform.M42, rootFrame.Transform.M13, rootFrame.Transform.M23, rootFrame.Transform.M33, rootFrame.Transform.M43, rootFrame.Transform.M14, rootFrame.Transform.M24, rootFrame.Transform.M34, rootFrame.Transform.M44); aiScene.RootNode = aiRootNode; for (int i = 1; i < clumpNode.FrameList.Count; i++) { var frame = clumpNode.FrameList[i]; var frameName = "_" + frame.HAnimFrameExtensionNode.NameId; Assimp.Node aiParentNode = null; if (frame.Parent != null) { string parentName = "RootNode"; if (frame.Parent.HasHAnimExtension) { parentName = "_" + frame.Parent.HAnimFrameExtensionNode.NameId; } aiParentNode = aiRootNode.FindNode(parentName); } var aiNode = new Assimp.Node(frameName, aiParentNode); aiNode.Transform = new Assimp.Matrix4x4(frame.Transform.M11, frame.Transform.M21, frame.Transform.M31, frame.Transform.M41, frame.Transform.M12, frame.Transform.M22, frame.Transform.M32, frame.Transform.M42, frame.Transform.M13, frame.Transform.M23, frame.Transform.M33, frame.Transform.M43, frame.Transform.M14, frame.Transform.M24, frame.Transform.M34, frame.Transform.M44); aiParentNode.Children.Add(aiNode); } // Meshes, Materials for (int atomicIndex = 0; atomicIndex < clumpNode.Atomics.Count; atomicIndex++) { var atomic = clumpNode.Atomics[atomicIndex]; var geometry = clumpNode.GeometryList[atomic.GeometryIndex]; var frame = clumpNode.FrameList[atomic.FrameIndex]; var aiNodeName = $"Atomic{atomicIndex}"; var aiNode = new Assimp.Node(aiNodeName, aiScene.RootNode); var frameWorldTransform = frame.WorldTransform; aiNode.Transform = new Assimp.Matrix4x4(frameWorldTransform.M11, frameWorldTransform.M21, frameWorldTransform.M31, frameWorldTransform.M41, frameWorldTransform.M12, frameWorldTransform.M22, frameWorldTransform.M32, frameWorldTransform.M42, frameWorldTransform.M13, frameWorldTransform.M23, frameWorldTransform.M33, frameWorldTransform.M43, frameWorldTransform.M14, frameWorldTransform.M24, frameWorldTransform.M34, frameWorldTransform.M44); aiScene.RootNode.Children.Add(aiNode); bool hasVertexWeights = geometry.SkinNode != null; for (int meshIndex = 0; meshIndex < geometry.MeshListNode.MaterialMeshes.Length; meshIndex++) { var mesh = geometry.MeshListNode.MaterialMeshes[meshIndex]; var aiMesh = new Assimp.Mesh($"Atomic{atomicIndex}_Geometry{atomic.GeometryIndex}_Mesh{meshIndex}", Assimp.PrimitiveType.Triangle); // get triangle list indices int[] indices; if (geometry.MeshListNode.PrimitiveType == RwPrimitiveType.TriangleList) { indices = mesh.Indices; } else { indices = MeshUtilities.ToTriangleList(mesh.Indices, false); } // Faces for (int i = 0; i < indices.Length; i += 3) { var faceIndices = new[] { i, i + 1, i + 2 }; var aiFace = new Assimp.Face(faceIndices); aiMesh.Faces.Add(aiFace); } // TextureCoordinateChannels, VertexColorChannels, Vertices, MaterialIndex, Normals for (int triIdx = 0; triIdx < indices.Length; triIdx += 3) { for (int triVertIdx = 0; triVertIdx < 3; triVertIdx++) { int vertexIndex = indices[triIdx + triVertIdx]; // TextureCoordinateChannels if (geometry.HasTextureCoordinates) { for (int channelIdx = 0; channelIdx < geometry.TextureCoordinateChannelCount; channelIdx++) { var textureCoordinate = geometry.TextureCoordinateChannels[channelIdx][vertexIndex]; var aiTextureCoordinate = new Assimp.Vector3D(textureCoordinate.X, textureCoordinate.Y, 0f); aiMesh.TextureCoordinateChannels[channelIdx].Add(aiTextureCoordinate); } } // VertexColorChannels if (geometry.HasColors) { var color = geometry.Colors[vertexIndex]; var aiColor = new Assimp.Color4D(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f); aiMesh.VertexColorChannels[0].Add(aiColor); } // Vertices if (geometry.HasVertices) { var vertex = geometry.Vertices[vertexIndex]; var aiVertex = new Assimp.Vector3D(vertex.X, vertex.Y, vertex.Z); aiMesh.Vertices.Add(aiVertex); } // Normals if (geometry.HasNormals) { var normal = geometry.Normals[vertexIndex]; var aiNormal = new Assimp.Vector3D(normal.X, normal.Y, normal.Z); aiMesh.Normals.Add(aiNormal); } } } // Bones if (hasVertexWeights) { var skinNode = geometry.SkinNode; var aiBoneMap = new Dictionary <int, Assimp.Bone>(); for (int i = 0; i < indices.Length; i++) { var vertexIndex = indices[i]; int realVertexIndex = i; for (int j = 0; j < 4; j++) { var boneIndex = skinNode.VertexBoneIndices[vertexIndex][j]; var boneWeight = skinNode.VertexBoneWeights[vertexIndex][j]; if (boneWeight == 0.0f) { continue; } if (!aiBoneMap.Keys.Contains(boneIndex)) { var aiBone = new Assimp.Bone(); var boneFrame = clumpNode.FrameList.GetFrameByHierarchyIndex(boneIndex); aiBone.Name = boneFrame.HasHAnimExtension ? "_" + boneFrame.HAnimFrameExtensionNode.NameId : "RootNode"; aiBone.VertexWeights.Add(new Assimp.VertexWeight(realVertexIndex, boneWeight)); Matrix4x4.Invert(frame.WorldTransform, out Matrix4x4 invertedFrameWorldTransform); Matrix4x4.Invert(boneFrame.WorldTransform * invertedFrameWorldTransform, out Matrix4x4 offsetMatrix); aiBone.OffsetMatrix = new Assimp.Matrix4x4(offsetMatrix.M11, offsetMatrix.M21, offsetMatrix.M31, offsetMatrix.M41, offsetMatrix.M12, offsetMatrix.M22, offsetMatrix.M32, offsetMatrix.M42, offsetMatrix.M13, offsetMatrix.M23, offsetMatrix.M33, offsetMatrix.M43, offsetMatrix.M14, offsetMatrix.M24, offsetMatrix.M34, offsetMatrix.M44); aiBoneMap[boneIndex] = aiBone; } if (!aiBoneMap[boneIndex].VertexWeights.Any(x => x.VertexID == realVertexIndex)) { aiBoneMap[boneIndex].VertexWeights.Add(new Assimp.VertexWeight(realVertexIndex, boneWeight)); } } } aiMesh.Bones.AddRange(aiBoneMap.Values); } else { var aiBone = new Assimp.Bone(); // Name aiBone.Name = frame.HasHAnimExtension ? "_" + frame.HAnimFrameExtensionNode.NameId : "RootNode"; // VertexWeights for (int i = 0; i < aiMesh.Vertices.Count; i++) { var aiVertexWeight = new Assimp.VertexWeight(i, 1f); aiBone.VertexWeights.Add(aiVertexWeight); } // OffsetMatrix /* * Matrix4x4.Invert( frame.WorldTransform, out Matrix4x4 offsetMatrix ); * aiBone.OffsetMatrix = new Assimp.Matrix4x4( offsetMatrix.M11, offsetMatrix.M21, offsetMatrix.M31, offsetMatrix.M41, * offsetMatrix.M12, offsetMatrix.M22, offsetMatrix.M32, offsetMatrix.M42, * offsetMatrix.M13, offsetMatrix.M23, offsetMatrix.M33, offsetMatrix.M43, * offsetMatrix.M14, offsetMatrix.M24, offsetMatrix.M34, offsetMatrix.M44 ); */ aiBone.OffsetMatrix = Assimp.Matrix4x4.Identity; aiMesh.Bones.Add(aiBone); } var material = geometry.Materials[mesh.MaterialIndex]; var aiMaterial = new Assimp.Material(); if (material.IsTextured) { // TextureDiffuse var texture = material.TextureReferenceNode; aiMaterial.TextureDiffuse = new Assimp.TextureSlot( texture.Name + ".png", Assimp.TextureType.Diffuse, 0, Assimp.TextureMapping.FromUV, 0, 0, Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0); } // Name aiMaterial.Name = material.Name ?? $"Geometry{atomic.GeometryIndex}_Material{mesh.MaterialIndex}"; if (material.IsTextured && material.Name == null) { aiMaterial.Name = material.TextureReferenceNode.Name; } aiMaterial.ShadingMode = Assimp.ShadingMode.Phong; // Add mesh to meshes aiScene.Meshes.Add(aiMesh); // Add material to materials aiScene.Materials.Add(aiMaterial); // MaterialIndex aiMesh.MaterialIndex = aiScene.Materials.Count - 1; // Add mesh index to node aiNode.MeshIndices.Add(aiScene.Meshes.Count - 1); } } return(aiScene); }
public BoneInfo(Assimp.Bone bone) { this.bone = bone; this.finalTransformation = mat4.identity(); }
public static Assimp.Scene AssimpPRMExport(string filePath, PRMModel prm) { Assimp.Scene aiScene = new Assimp.Scene(); //Create an array to hold references to these since Assimp lacks a way to grab these by order or id //We don't need the nodo count in this since they can't be parents Assimp.Node[] boneArray = new Assimp.Node[2]; //Set up root node var aiRootNode = new Assimp.Node("RootNode", null); aiRootNode.Transform = Assimp.Matrix4x4.Identity; boneArray[0] = aiRootNode; aiScene.RootNode = aiRootNode; //Set up single child node var aiNode = new Assimp.Node(Path.GetFileNameWithoutExtension(filePath) + "_node", aiRootNode); //Use inverse bind matrix as base //Get local transform aiNode.Transform = aiRootNode.Transform; aiRootNode.Children.Add(aiNode); boneArray[1] = aiNode; //Mesh string aiMeshName = Path.GetFileNameWithoutExtension(filePath); var aiMesh = new Assimp.Mesh(aiMeshName, Assimp.PrimitiveType.Triangle); //Vertex face data - PSO2 Actually doesn't do this, it just has per vertex data so we can just map a vertice's data to each face using it //It may actually be possible to add this to the previous loop, but my reference didn't so I'm doing it in a separate loop for safety //Reference: https://github.com/TGEnigma/Amicitia/blob/master/Source/AmicitiaLibrary/Graphics/RenderWare/RWClumpNode.cs for (int vertId = 0; vertId < prm.vertices.Count; vertId++) { var prmVert = prm.vertices[vertId]; var pos = prmVert.pos * 100; aiMesh.Vertices.Add(new Assimp.Vector3D(pos.X, pos.Y, pos.Z)); var nrm = prmVert.normal; aiMesh.Normals.Add(new Assimp.Vector3D(nrm.X, nrm.Y, nrm.Z)); //Vert colors are bgra var rawClr = prmVert.color; var clr = new Assimp.Color4D(clrToFloat(rawClr[2]), clrToFloat(rawClr[1]), clrToFloat(rawClr[0]), clrToFloat(rawClr[3])); aiMesh.VertexColorChannels[0].Add(clr); var uv1 = prmVert.uv1; var aiUV1 = new Assimp.Vector3D(uv1.X, uv1.Y, 0f); aiMesh.TextureCoordinateChannels[0].Add(aiUV1); var uv2 = prmVert.uv2; var aiUV2 = new Assimp.Vector3D(uv2.X, uv2.Y, 0f); aiMesh.TextureCoordinateChannels[1].Add(aiUV2); } //Handle rigid meshes { var aiBone = new Assimp.Bone(); var aqnBone = boneArray[0]; // Name aiBone.Name = aiNode.Name; // VertexWeights for (int i = 0; i < aiMesh.Vertices.Count; i++) { var aiVertexWeight = new Assimp.VertexWeight(i, 1f); aiBone.VertexWeights.Add(aiVertexWeight); } aiBone.OffsetMatrix = Assimp.Matrix4x4.Identity; aiMesh.Bones.Add(aiBone); } //Faces foreach (var face in prm.faces) { aiMesh.Faces.Add(new Assimp.Face(new int[] { (int)face.X, (int)face.Y, (int)face.Z })); } //Material Assimp.Material mate = new Assimp.Material(); mate.ColorDiffuse = new Assimp.Color4D(1, 1, 1, 1); mate.Name = aiMeshName + "_material"; mate.ShadingMode = Assimp.ShadingMode.Phong; var meshNodeName = Path.GetFileNameWithoutExtension(filePath); // Add mesh to meshes aiScene.Meshes.Add(aiMesh); // Add material to materials aiScene.Materials.Add(mate); // MaterialIndex aiMesh.MaterialIndex = aiScene.Materials.Count - 1; // Set up mesh node and add this mesh's index to it (This tells assimp to export it as a mesh for various formats) var meshNode = new Assimp.Node(meshNodeName, aiScene.RootNode); meshNode.Transform = Assimp.Matrix4x4.Identity; aiScene.RootNode.Children.Add(meshNode); meshNode.MeshIndices.Add(aiScene.Meshes.Count - 1); return(aiScene); }
public static Assimp.Scene AssimpExport(string filePath, AquaObject aqp, AquaNode aqn) { if (aqp is NGSAquaObject) { //NGS aqps will give lots of isolated vertices if we don't handle them //Since we're not actually altering the data so much as rearranging references, we can just do this aqp = aqp.Clone(); aqp.splitVSETPerMesh(); } Assimp.Scene aiScene = new Assimp.Scene(); //Create an array to hold references to these since Assimp lacks a way to grab these by order or id //We don't need the nodo count in this since they can't be parents Assimp.Node[] boneArray = new Assimp.Node[aqn.nodeList.Count]; //Set up root node var root = aqn.nodeList[0]; var aiRootNode = new Assimp.Node("RootNode", null); aiRootNode.Transform = Assimp.Matrix4x4.Identity; aiScene.RootNode = aiRootNode; //Assign bones for (int i = 0; i < aqn.nodeList.Count; i++) { var bn = aqn.nodeList[i]; Assimp.Node parentNode; var parentTfm = Matrix4x4.Identity; if (bn.parentId == -1) { parentNode = aiRootNode; } else { parentNode = boneArray[bn.parentId]; var pn = aqn.nodeList[bn.parentId]; parentTfm = new Matrix4x4(pn.m1.X, pn.m1.Y, pn.m1.Z, pn.m1.W, pn.m2.X, pn.m2.Y, pn.m2.Z, pn.m2.W, pn.m3.X, pn.m3.Y, pn.m3.Z, pn.m3.W, pn.m4.X * 100, pn.m4.Y * 100, pn.m4.Z * 100, pn.m4.W); } var aiNode = new Assimp.Node($"({i})" + bn.boneName.GetString(), parentNode); //Use inverse bind matrix as base var bnMat = new Matrix4x4(bn.m1.X, bn.m1.Y, bn.m1.Z, bn.m1.W, bn.m2.X, bn.m2.Y, bn.m2.Z, bn.m2.W, bn.m3.X, bn.m3.Y, bn.m3.Z, bn.m3.W, bn.m4.X * 100, bn.m4.Y * 100, bn.m4.Z * 100, bn.m4.W); Matrix4x4.Invert(bnMat, out bnMat); //Get local transform aiNode.Transform = GetAssimpMat4(bnMat * parentTfm); parentNode.Children.Add(aiNode); boneArray[i] = aiNode; } foreach (AquaNode.NODO bn in aqn.nodoList) { var parentNodo = boneArray[bn.parentId]; var aiNode = new Assimp.Node(bn.boneName.GetString(), parentNodo); //NODOs are a bit more primitive. We need to generate the matrix for these ones. var matrix = Assimp.Matrix4x4.Identity; var rotation = Assimp.Matrix4x4.FromRotationX(bn.eulRot.X) * Assimp.Matrix4x4.FromRotationY(bn.eulRot.Y) * Assimp.Matrix4x4.FromRotationZ(bn.eulRot.Z); matrix *= rotation; matrix *= Assimp.Matrix4x4.FromTranslation(new Assimp.Vector3D(bn.pos.X * 100, bn.pos.Y * 100, bn.pos.Z * 100)); aiNode.Transform = matrix; parentNodo.Children.Add(aiNode); } //Assign meshes and materials foreach (AquaObject.MESH msh in aqp.meshList) { var vtxl = aqp.vtxlList[msh.vsetIndex]; //Mesh var aiMeshName = string.Format("mesh[{4}]_{0}_{1}_{2}_{3}_mesh", msh.mateIndex, msh.rendIndex, msh.shadIndex, msh.tsetIndex, aiScene.Meshes.Count); bool hasVertexWeights = aqp.vtxlList[msh.vsetIndex].vertWeightIndices.Count > 0; var aiMesh = new Assimp.Mesh(aiMeshName, Assimp.PrimitiveType.Triangle); //Vertex face data - PSO2 Actually doesn't do this, it just has per vertex data so we can just map a vertice's data to each face using it //It may actually be possible to add this to the previous loop, but my reference didn't so I'm doing it in a separate loop for safety //Reference: https://github.com/TGEnigma/Amicitia/blob/master/Source/AmicitiaLibrary/Graphics/RenderWare/RWClumpNode.cs //UVs will have dummied data to ensure that if the game arbitrarily writes them, they will still be exported back in the same order for (int vertId = 0; vertId < vtxl.vertPositions.Count; vertId++) { if (vtxl.vertPositions.Count > 0) { var pos = vtxl.vertPositions[vertId] * 100; aiMesh.Vertices.Add(new Assimp.Vector3D(pos.X, pos.Y, pos.Z)); } if (vtxl.vertNormals.Count > 0) { var nrm = vtxl.vertNormals[vertId]; aiMesh.Normals.Add(new Assimp.Vector3D(nrm.X, nrm.Y, nrm.Z)); } if (vtxl.vertColors.Count > 0) { //Vert colors are bgra var rawClr = vtxl.vertColors[vertId]; var clr = new Assimp.Color4D(clrToFloat(rawClr[2]), clrToFloat(rawClr[1]), clrToFloat(rawClr[0]), clrToFloat(rawClr[3])); aiMesh.VertexColorChannels[0].Add(clr); } if (vtxl.vertColor2s.Count > 0) { //Vert colors are bgra var rawClr = vtxl.vertColor2s[vertId]; var clr = new Assimp.Color4D(clrToFloat(rawClr[2]), clrToFloat(rawClr[1]), clrToFloat(rawClr[0]), clrToFloat(rawClr[3])); aiMesh.VertexColorChannels[1].Add(clr); } if (vtxl.uv1List.Count > 0) { var textureCoordinate = vtxl.uv1List[vertId]; var aiTextureCoordinate = new Assimp.Vector3D(textureCoordinate.X, textureCoordinate.Y, 0f); aiMesh.TextureCoordinateChannels[0].Add(aiTextureCoordinate); } else { var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f); aiMesh.TextureCoordinateChannels[0].Add(aiTextureCoordinate); } if (vtxl.uv2List.Count > 0) { var textureCoordinate = vtxl.uv2List[vertId]; var aiTextureCoordinate = new Assimp.Vector3D(textureCoordinate.X, textureCoordinate.Y, 0f); aiMesh.TextureCoordinateChannels[1].Add(aiTextureCoordinate); } else { var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f); aiMesh.TextureCoordinateChannels[1].Add(aiTextureCoordinate); } if (vtxl.uv3List.Count > 0) { var textureCoordinate = vtxl.uv3List[vertId]; var aiTextureCoordinate = new Assimp.Vector3D(textureCoordinate.X, textureCoordinate.Y, 0f); aiMesh.TextureCoordinateChannels[2].Add(aiTextureCoordinate); } else { var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f); aiMesh.TextureCoordinateChannels[2].Add(aiTextureCoordinate); } if (vtxl.uv4List.Count > 0) { var textureCoordinate = vtxl.uv4List[vertId]; var aiTextureCoordinate = new Assimp.Vector3D(textureCoordinate.X, textureCoordinate.Y, 0f); aiMesh.TextureCoordinateChannels[3].Add(aiTextureCoordinate); } else { var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f); aiMesh.TextureCoordinateChannels[3].Add(aiTextureCoordinate); } if (vtxl.vert0x22.Count > 0) { var textureCoordinate = vtxl.vert0x22[vertId]; var aiTextureCoordinate = new Assimp.Vector3D(uvShortToFloat(textureCoordinate[0]), uvShortToFloat(textureCoordinate[1]), 0f); aiMesh.TextureCoordinateChannels[4].Add(aiTextureCoordinate); } else { var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f); aiMesh.TextureCoordinateChannels[4].Add(aiTextureCoordinate); } if (vtxl.vert0x23.Count > 0) { var textureCoordinate = vtxl.vert0x23[vertId]; var aiTextureCoordinate = new Assimp.Vector3D(uvShortToFloat(textureCoordinate[0]), uvShortToFloat(textureCoordinate[1]), 0f); aiMesh.TextureCoordinateChannels[5].Add(aiTextureCoordinate); } else { var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f); aiMesh.TextureCoordinateChannels[5].Add(aiTextureCoordinate); } if (vtxl.vert0x24.Count > 0) { var textureCoordinate = vtxl.vert0x24[vertId]; var aiTextureCoordinate = new Assimp.Vector3D(uvShortToFloat(textureCoordinate[0]), uvShortToFloat(textureCoordinate[1]), 0f); aiMesh.TextureCoordinateChannels[6].Add(aiTextureCoordinate); } else { var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f); aiMesh.TextureCoordinateChannels[6].Add(aiTextureCoordinate); } if (vtxl.vert0x25.Count > 0) { var textureCoordinate = vtxl.vert0x25[vertId]; var aiTextureCoordinate = new Assimp.Vector3D(uvShortToFloat(textureCoordinate[0]), uvShortToFloat(textureCoordinate[1]), 0f); aiMesh.TextureCoordinateChannels[7].Add(aiTextureCoordinate); } else { var aiTextureCoordinate = new Assimp.Vector3D(0, 0, 0f); aiMesh.TextureCoordinateChannels[7].Add(aiTextureCoordinate); } } //Assimp Bones - Assimp likes to store vertex weights in bones and bones references in meshes if (hasVertexWeights) { //Get bone palette List <uint> bonePalette; if (aqp.objc.bonePaletteOffset > 0) { bonePalette = aqp.bonePalette; } else { bonePalette = new List <uint>(); for (int bn = 0; bn < vtxl.bonePalette.Count; bn++) { bonePalette.Add(vtxl.bonePalette[bn]); } } var aiBoneMap = new Dictionary <int, Assimp.Bone>(); //Iterate through vertices for (int vertId = 0; vertId < vtxl.vertWeightIndices.Count; vertId++) { var boneIndices = vtxl.vertWeightIndices[vertId]; var boneWeights = Vector4ToFloatArray(vtxl.vertWeights[vertId]); //Iterate through weights for (int wt = 0; wt < 4; wt++) { var boneIndex = boneIndices[wt]; var boneWeight = boneWeights[wt]; if (boneWeight == 0.0f) { continue; } if (!aiBoneMap.Keys.Contains(boneIndex)) { var aiBone = new Assimp.Bone(); var aqnBone = boneArray[bonePalette[boneIndex]]; var rawBone = aqn.nodeList[(int)bonePalette[boneIndex]]; aiBone.Name = $"({bonePalette[boneIndex]})" + rawBone.boneName.GetString(); aiBone.VertexWeights.Add(new Assimp.VertexWeight(vertId, boneWeight)); var invTransform = new Assimp.Matrix4x4(rawBone.m1.X, rawBone.m2.X, rawBone.m3.X, rawBone.m4.X, rawBone.m1.Y, rawBone.m2.Y, rawBone.m3.Y, rawBone.m4.Y, rawBone.m1.Z, rawBone.m2.Z, rawBone.m3.Z, rawBone.m4.Z, rawBone.m1.W, rawBone.m2.W, rawBone.m3.W, rawBone.m4.W); aiBone.OffsetMatrix = invTransform; aiBoneMap[boneIndex] = aiBone; } if (!aiBoneMap[boneIndex].VertexWeights.Any(x => x.VertexID == vertId)) { aiBoneMap[boneIndex].VertexWeights.Add(new Assimp.VertexWeight(vertId, boneWeight)); } } } //Add the bones to the mesh aiMesh.Bones.AddRange(aiBoneMap.Values); } else //Handle rigid meshes { var aiBone = new Assimp.Bone(); var aqnBone = boneArray[msh.baseMeshNodeId]; // Name aiBone.Name = aqnBone.Name; // VertexWeights for (int i = 0; i < aiMesh.Vertices.Count; i++) { var aiVertexWeight = new Assimp.VertexWeight(i, 1f); aiBone.VertexWeights.Add(aiVertexWeight); } aiBone.OffsetMatrix = Assimp.Matrix4x4.Identity; aiMesh.Bones.Add(aiBone); } //Faces foreach (var face in aqp.strips[msh.vsetIndex].GetTriangles(true)) { aiMesh.Faces.Add(new Assimp.Face(new int[] { (int)face.X, (int)face.Y, (int)face.Z })); } //Material var mat = aqp.mateList[msh.mateIndex]; var shaderSet = AquaObjectMethods.GetShaderNames(aqp, msh.shadIndex); var textureSet = AquaObjectMethods.GetTexListNames(aqp, msh.tsetIndex); Assimp.Material mate = new Assimp.Material(); mate.ColorDiffuse = new Assimp.Color4D(mat.diffuseRGBA.X, mat.diffuseRGBA.Y, mat.diffuseRGBA.Z, mat.diffuseRGBA.W); if (mat.alphaType.GetString().Equals("add")) { mate.BlendMode = Assimp.BlendMode.Additive; } mate.Name = "|[]{}~`!@#$%^&*;:'\"?><,./(" + shaderSet[0] + "," + shaderSet[1] + ")" + "{" + mat.alphaType.GetString() + "}" + mat.matName.GetString(); //Set textures - PSO2 Texture slots are NOT consistent and depend entirely on the selected shader. As such, slots will be somewhat arbitrary after albedo/diffuse for (int i = 0; i < textureSet.Count; i++) { switch (i) { case 0: mate.TextureDiffuse = new Assimp.TextureSlot( textureSet[i], Assimp.TextureType.Diffuse, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0, Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0); break; case 1: mate.TextureSpecular = new Assimp.TextureSlot( textureSet[i], Assimp.TextureType.Specular, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0, Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0); break; case 2: mate.TextureNormal = new Assimp.TextureSlot( textureSet[i], Assimp.TextureType.Normals, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0, Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0); break; case 3: mate.TextureLightMap = new Assimp.TextureSlot( textureSet[i], Assimp.TextureType.Lightmap, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0, Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0); break; case 4: mate.TextureDisplacement = new Assimp.TextureSlot( textureSet[i], Assimp.TextureType.Displacement, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0, Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0); break; case 5: mate.TextureOpacity = new Assimp.TextureSlot( textureSet[i], Assimp.TextureType.Opacity, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0, Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0); break; case 6: mate.TextureHeight = new Assimp.TextureSlot( textureSet[i], Assimp.TextureType.Height, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0, Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0); break; case 7: mate.TextureEmissive = new Assimp.TextureSlot( textureSet[i], Assimp.TextureType.Emissive, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0, Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0); break; case 8: mate.TextureAmbient = new Assimp.TextureSlot( textureSet[i], Assimp.TextureType.Ambient, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0, Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0); break; case 9: mate.TextureReflection = new Assimp.TextureSlot( textureSet[i], Assimp.TextureType.Reflection, i, Assimp.TextureMapping.FromUV, aqp.tstaList[aqp.tsetList[msh.tsetIndex].tstaTexIDs[i]].modelUVSet, 0, Assimp.TextureOperation.Add, Assimp.TextureWrapMode.Wrap, Assimp.TextureWrapMode.Wrap, 0); break; default: break; } } mate.ShadingMode = Assimp.ShadingMode.Phong; var meshNodeName = string.Format("mesh[{4}]_{0}_{1}_{2}_{3}#{4}#{5}", msh.mateIndex, msh.rendIndex, msh.shadIndex, msh.tsetIndex, aiScene.Meshes.Count, msh.baseMeshNodeId, msh.baseMeshDummyId); // Add mesh to meshes aiScene.Meshes.Add(aiMesh); // Add material to materials aiScene.Materials.Add(mate); // MaterialIndex aiMesh.MaterialIndex = aiScene.Materials.Count - 1; // Set up mesh node and add this mesh's index to it (This tells assimp to export it as a mesh for various formats) var meshNode = new Assimp.Node(meshNodeName, aiScene.RootNode); meshNode.Transform = Assimp.Matrix4x4.Identity; aiScene.RootNode.Children.Add(meshNode); meshNode.MeshIndices.Add(aiScene.Meshes.Count - 1); } return(aiScene); }
private void AddSkinnedMesh(Assimp.Node node, GameObject parent, Material[] mats, CombineInstance[] combine) { SkinnedMeshRenderer meshRenderer = parent.AddComponent <SkinnedMeshRenderer>(); List <List <Assimp.Bone> > meshBones = new List <List <Assimp.Bone> >(); Dictionary <int, List <BoneWeight1> > VertexBonesWeights = new Dictionary <int, List <BoneWeight1> >(); List <Transform[]> bonesArray = new List <Transform[]>(); List <Matrix4x4[]> bindPoses = new List <Matrix4x4[]>(); //Debug.Log(node.Name + " / " + parent.name); int previousVertexCount = 0; for (int iMesh = 0; iMesh < node.MeshIndices.Count; iMesh++) { Assimp.Mesh currentMesh = scene.Meshes[node.MeshIndices[iMesh]]; meshBones.Add(currentMesh.Bones); bonesArray.Add(new Transform[currentMesh.BoneCount]); bindPoses.Add(new Matrix4x4[currentMesh.BoneCount]); for (int iBone = 0; iBone < bonesArray[iMesh].Length; iBone++) { Assimp.Bone currentBones = meshBones[iMesh][iBone]; if (!bones.ContainsKey(currentBones.Name)) { Debug.Log("missing bone " + currentBones.Name); continue; } bonesArray[iMesh][iBone] = bones[currentBones.Name]; bindPoses[iMesh][iBone] = bonesArray[iMesh][iBone].worldToLocalMatrix; for (int iVertex = 0; iVertex < currentBones.VertexWeightCount; iVertex++) { int vertexIndex = currentBones.VertexWeights[iVertex].VertexID + previousVertexCount; float weight = currentBones.VertexWeights[iVertex].Weight; BoneWeight1 boneWeight = new BoneWeight1() { boneIndex = iBone, weight = weight }; if (!VertexBonesWeights.ContainsKey(vertexIndex)) { VertexBonesWeights.Add(vertexIndex, new List <BoneWeight1>()); } VertexBonesWeights[vertexIndex].Add(boneWeight); } } previousVertexCount += currentMesh.VertexCount; } List <Assimp.Bone> meshBonesFlat = new List <Assimp.Bone>(); meshBones.ForEach(x => meshBonesFlat.AddRange(x)); List <Transform> bonesArrayFlat = new List <Transform>(); bonesArray.ForEach(x => bonesArrayFlat.AddRange(x)); List <Matrix4x4> bindPosesFlat = new List <Matrix4x4>(); bindPoses.ForEach(x => bindPosesFlat.AddRange(x)); List <BoneWeight> bonesWeightsFlat = new List <BoneWeight>(); byte[] bonesPerVertes = new byte[VertexBonesWeights.Count]; List <BoneWeight1> bw = new List <BoneWeight1>(); for (int i = 0; i < VertexBonesWeights.Count; i++) { bonesPerVertes[i] = (byte)VertexBonesWeights[i].Count; VertexBonesWeights[i].Sort((x, y) => { if (x == null) { if (y == null) { return(0); } else { return(-1); } } else { if (y == null) { return(1); } else { return(-x.weight.CompareTo(y.weight)); } } }); bw.AddRange(VertexBonesWeights[i]); } meshRenderer.bones = bonesArrayFlat.ToArray(); meshRenderer.sharedMesh = new Mesh(); meshRenderer.sharedMesh.bindposes = bindPosesFlat.ToArray(); meshRenderer.sharedMesh.CombineMeshes(combine, false); meshRenderer.sharedMesh.SetBoneWeights(new NativeArray <byte>(bonesPerVertes, Allocator.Temp), new NativeArray <BoneWeight1>(bw.ToArray(), Allocator.Temp)); meshRenderer.sharedMesh.name = meshes[node.MeshIndices[0]].name; meshRenderer.sharedMaterials = mats; meshRenderer.rootBone = rootBone; if (meshRenderer.sharedMesh.bounds.size.magnitude > meshSize.magnitude) { meshCenter = meshRenderer.bounds.center; meshSize = meshRenderer.bounds.size; bodyMesh = meshRenderer; } }
public static MeshGroup FromFbx(string filePath) { const float Scale = 1.0f; var assimp = new Assimp.AssimpContext(); var scene = assimp.ImportFile(filePath, Assimp.PostProcessSteps.PreTransformVertices); var BoneScene = assimp.ImportFile(filePath); var baseFilePath = Path.GetDirectoryName(filePath); TexList = new List <string>(); TextureData = new List <Tm2>(); BoneData = new List <Assimp.Bone>(); NodeData = new List <Assimp.Node>(); foreach (Assimp.Material mat in scene.Materials) { Stream str = null; var name = Path.GetFileName(mat.TextureDiffuse.FilePath); if (name != "" || name != null) { str = File.OpenRead(name); } if (str != null) { TexList.Add(Path.GetFileName(mat.TextureDiffuse.FilePath)); PngImage png = new PngImage(str); Tm2 tmImage = Tm2.Create(png); TextureData.Add(tmImage); } } Assimp.Bone rBone = new Assimp.Bone(); foreach (var m in BoneScene.Meshes) { foreach (var bn in m.Bones) { if (!BoneData.Contains(bn)) { BoneData.Add(bn); } } } NodeData.AddRange(BoneScene.RootNode.Children.ToList()); return(new MeshGroup() { MeshDescriptors = scene.Meshes .Select(x => { var vertices = new PositionColoredTextured[x.Vertices.Count]; for (var i = 0; i < vertices.Length; i++) { vertices[i].X = x.Vertices[i].X * Scale; vertices[i].Y = x.Vertices[i].Y * Scale; vertices[i].Z = x.Vertices[i].Z * Scale; vertices[i].Tu = x.TextureCoordinateChannels[0][i].X; vertices[i].Tv = 1.0f - x.TextureCoordinateChannels[0][i].Y; vertices[i].R = x.VertexColorChannels[0][i].R; vertices[i].G = x.VertexColorChannels[0][i].G; vertices[i].B = x.VertexColorChannels[0][i].B; vertices[i].A = x.VertexColorChannels[0][i].A; } return new MeshDescriptor { Vertices = vertices, Indices = x.GetIndices(), IsOpaque = true, TextureIndex = x.MaterialIndex }; }).ToList() }); }