public static Onyx3D.Mesh ToOnyx3D(this Assimp.Mesh mesh) { Onyx3D.Mesh newMesh = new Onyx3D.Mesh(); newMesh.Indices = mesh.GetIndices(); for (int vi = 0; vi < mesh.VertexCount; ++vi) { Vertex newVertex = new Vertex(); newVertex.Position = mesh.Vertices[vi].ToOnyx3D(); if (mesh.HasTextureCoords(0)) { Assimp.Vector3D texCoord = mesh.TextureCoordinateChannels[0][vi]; newVertex.TexCoord = texCoord.ToOnyx3D().Xy; } if (mesh.HasNormals) { newVertex.Normal = mesh.Normals[vi].ToOnyx3D().Normalized(); if (mesh.HasTangentBasis) { newVertex.Bitangent = mesh.BiTangents[vi].ToOnyx3D().Normalized(); newVertex.Tangent = mesh.Tangents[vi].ToOnyx3D().Normalized(); } } newMesh.Vertices.Add(newVertex); } newMesh.GenerateVAO(); return(newMesh); }
private void SetAssimpNormalAttribute(Assimp.Mesh mesh) { if (!Attributes.CheckAttribute(GXVertexAttribute.Normal)) { Attributes.SetAttributeData(GXVertexAttribute.Normal, new List <Vector3>()); } }
private static Assimp.Mesh ConvertMeshType8(MeshType8 mesh, Node node, int nodeIndex) { var aiMesh = new Assimp.Mesh { MaterialIndex = mesh.MaterialIndex }; aiMesh.Faces.AddRange(mesh.Triangles.Select(x => new Assimp.Face(new int[] { x.A, x.B, x.C }))); foreach (var batch in mesh.Batches) { (var positions, var normals) = batch.Transform(node.WorldTransform); aiMesh.Vertices.AddRange(positions.ToAssimp()); if (normals != null) { aiMesh.Normals.AddRange(normals.ToAssimp()); } if (batch.TexCoords != null) { aiMesh.TextureCoordinateChannels[0].AddRange(batch.TexCoords.ToAssimp()); } } if (mesh.TexCoords2 != null) { aiMesh.TextureCoordinateChannels[1].AddRange(mesh.TexCoords2.ToAssimp()); } AssignFauxWeights(aiMesh, node, nodeIndex); return(aiMesh); }
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 void SetAssimpPositionAttribute(Assimp.Mesh mesh) { if (!Attributes.CheckAttribute(GXVertexAttribute.Position)) { Attributes.SetAttributeData(GXVertexAttribute.Position, new List <Vector3>()); } }
private void SetAssimpNormalAttribute(Assimp.Mesh mesh) { List <Vector3> tempList = new List <Vector3>(); for (int vec = 0; vec < mesh.Normals.Count; vec++) { tempList.Add(mesh.Normals[vec].ToOpenTKVector3()); } if (!Attributes.CheckAttribute(GXVertexAttribute.Normal)) { Attributes.SetAttributeData(GXVertexAttribute.Normal, tempList); } else { List <Vector3> attribData = (List <Vector3>)Attributes.GetAttributeData(GXVertexAttribute.Normal); foreach (Vector3 vec in tempList) { if (!attribData.Contains(vec)) { attribData.Add(vec); } } Attributes.SetAttributeData(GXVertexAttribute.Normal, attribData); } }
private static PartialModel AppendPartialMesh(Assimp.Mesh mesh) { var primitiveType = OpenTK.Graphics.OpenGL.PrimitiveType.Triangles; if (mesh.Faces[0].IndexCount == 4) { primitiveType = OpenTK.Graphics.OpenGL.PrimitiveType.Quads; } int curIdx = GeneralMeshBuffer.IndexCount; int curVert = GeneralMeshBuffer.VertexCount; GeneralMeshBuffer.LoadModelVertices(mesh, true); int idxCount = GeneralMeshBuffer.IndexCount - curIdx; var vertices = GeneralMeshBuffer.GetVertices().Skip(curVert); var vertexPositions = vertices.Select(x => x.Position).ToList(); var bounding = BBox.FromVertices(vertexPositions); var model = new PartialModel(GeneralMeshBuffer, curIdx, curVert, idxCount, primitiveType); model.BoundingBox = bounding; model.Vertices = vertexPositions; LoadedModels.Add(model); //model.LoadVertices(); //model.CalculateBoundingBox(); return(model); }
public _MeshPrimitiveDecoder(Assimp.Mesh mesh, TMaterial material) { _Source = mesh; _Material = material; _ColorCount = 0; if (mesh.HasVertexColors(0)) { _ColorCount = 1; } if (mesh.HasVertexColors(1)) { _ColorCount = 2; } _TexCoordCount = 0; if (mesh.HasTextureCoords(0)) { _TexCoordCount = 1; } if (mesh.HasTextureCoords(1)) { _TexCoordCount = 2; } if (mesh.HasBones) { var influences = new List <(int bone, float weight)> [_Source.VertexCount];
public void InitAssImp(Assimp.Scene aiRoot, Scene.Entity3D root) { if (aiRoot.HasAnimations == false) { return; } _skeleton = CreateBoneTree(aiRoot.RootNode, null); Console.WriteLine("Proc bones:" + _skeleton.Name + " C:" + _skeleton.Children.Count); foreach (Assimp.Mesh mesh in aiRoot.Meshes) { foreach (Assimp.Bone bone in mesh.Bones) { if (!_bonesByName.TryGetValue(bone.Name, out Bone found)) { continue; } bool skip = (from t in _bones let bname = bone.Name where t.Name == bname select t).Any(); if (skip) { continue; } found.Offset = ToTK(bone.OffsetMatrix); _bones.Add(found); _bonesToIndex[found.Name] = _bones.IndexOf(found); } Assimp.Mesh mesh1 = mesh; foreach (string bone in _bonesByName.Keys.Where(b => mesh1.Bones.All(b1 => b1.Name != b) && b.StartsWith("Bone"))) { _bonesByName[bone].Offset = _bonesByName[bone].Parent.Offset; _bones.Add(_bonesByName[bone]); _bonesToIndex[bone] = _bones.IndexOf(_bonesByName[bone]); } } ExtractAnimations(aiRoot); const float timestep = 1.0f / 30.0f; for (int i = 0; i < Animations.Count; i++) { SetAnimationIndex(i); float dt = 0.0f; for (float ticks = 0.0f; ticks < Animations[i].Duration; ticks += Animations[i].TicksPerSecond / 30.0f) { dt += timestep; Calculate(dt); List <OpenTK.Matrix4> trans = new List <OpenTK.Matrix4>(); for (int a = 0; a < _bones.Count; a++) { OpenTK.Matrix4 rotMat = _bones[a].Offset * _bones[a].GlobalTransform; trans.Add(rotMat); } Animations[i].Transforms.Add(trans); } } Console.WriteLine("Finished loading animations with " + _bones.Count + " bones"); }
public Drawable(Vector3 position, Assimp.Mesh assMesh) : base(position) { Position = position; Mesh = new Mesh(position, Vector3.Zero, assMesh); Drawables.Add(this); Renderer.InvalidateAll(); }
public Gizmo(Vector3 position, Assimp.Mesh assMesh) : base(position, assMesh) { Mesh = new Mesh(position, Vector3.Zero, assMesh) { DrawInFront = true }; AllowRotation = true; }
private bool addToVBO(Assimp.Mesh mesh, ref List <float> vboData) { if (getFlags(mesh) != m_currentFlags) { textBoxInfo.Text += Environment.NewLine + "Skipping mesh with invalid vertex data..."; return(false); } for (var i = 0; i < mesh.VertexCount; ++i) { var pos = mesh.Vertices[i]; vboData.Add(pos.X); vboData.Add(pos.Y); vboData.Add(pos.Z); if (mesh.HasVertexColors(0)) { var colour = mesh.VertexColorChannels[0][i]; vboData.Add(colour.R); vboData.Add(colour.G); vboData.Add(colour.B); } var normal = mesh.Normals[i]; vboData.Add(normal.X); vboData.Add(normal.Y); vboData.Add(normal.Z); if (mesh.HasTangentBasis) { var tan = mesh.Tangents[i]; var bitan = mesh.BiTangents[i]; vboData.Add(tan.X); vboData.Add(tan.Y); vboData.Add(tan.Z); vboData.Add(bitan.X); vboData.Add(bitan.Y); vboData.Add(bitan.Z); } if (mesh.HasTextureCoords(0)) { var uv = mesh.TextureCoordinateChannels[0][i]; vboData.Add(uv.X); vboData.Add(uv.Y); } if (mesh.HasTextureCoords(1)) { var uv = mesh.TextureCoordinateChannels[1][i]; vboData.Add(uv.X); vboData.Add(uv.Y); } } return(true); }
private static Geometry ConvertGeometry(Assimp.Mesh mesh, float scale, VertexLayout vertexLayout, VertexComponent[] vertexComponents, bool combineVB, bool combineIB, out BoundingBox meshBoundingBox) { VkPrimitiveTopology[] primitiveTopology = { VkPrimitiveTopology.PointList, VkPrimitiveTopology.PointList, VkPrimitiveTopology.LineList, VkPrimitiveTopology.LineList, VkPrimitiveTopology.TriangleList, }; if (!combineVB) { vertexBuffer.Clear(); } if (!combineIB) { indexBuffer.Clear(); } ConvertGeom(scale, (uint)vertexOffset, mesh, out meshBoundingBox, vertexComponents); var geometry = new Geometry { Name = mesh.Name, VertexLayout = vertexLayout }; if (!combineVB) { geometry.VertexBuffer = Buffer.Create(VkBufferUsageFlags.VertexBuffer, false, sizeof(float), vertexBuffer.Count, vertexBuffer.Data); } if (!combineIB) { geometry.IndexBuffer = Buffer.Create(VkBufferUsageFlags.IndexBuffer, false, sizeof(uint), indexBuffer.Count, indexBuffer.Data); } geometry.SetDrawRange(primitiveTopology[(int)mesh.PrimitiveType], indexOffset, (uint)mesh.FaceCount * 3, 0 /*vertexOffset*/); if (combineVB) { vertexOffset += mesh.VertexCount; } if (combineIB) { indexOffset += (uint)mesh.FaceCount * 3; } return(geometry); }
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); }
public Mesh(Assimp.Mesh mesh) { Assign(); bbox = new BoundingBox(Vector3.PositiveInfinity, Vector3.NegativeInfinity); GL.BindVertexArray(Vao); GL.EnableVertexAttribArray(0); GL.EnableVertexAttribArray(1); int[] indices = mesh.GetIndices(); GL.BindBuffer(BufferTarget.ElementArrayBuffer, Ebo); GL.BufferData(BufferTarget.ElementArrayBuffer, sizeof(int) * indices.Length, indices, BufferUsageHint.StaticDraw); iCount = indices.Length; // positions + normals float[] vertexData = new float[mesh.VertexCount * 6]; for (int i = 0; i < mesh.VertexCount; ++i) { vertexData[i * 6 + 0] = mesh.Vertices[i].X; vertexData[i * 6 + 1] = mesh.Vertices[i].Y; vertexData[i * 6 + 2] = mesh.Vertices[i].Z; bbox.MinPoint.X = Math.Min(bbox.MinPoint.X, mesh.Vertices[i].X); bbox.MinPoint.Y = Math.Min(bbox.MinPoint.Y, mesh.Vertices[i].Y); bbox.MinPoint.Z = Math.Min(bbox.MinPoint.Z, mesh.Vertices[i].Z); bbox.MaxPoint.X = Math.Max(bbox.MaxPoint.X, mesh.Vertices[i].X); bbox.MaxPoint.Y = Math.Max(bbox.MaxPoint.Y, mesh.Vertices[i].Y); bbox.MaxPoint.Z = Math.Max(bbox.MaxPoint.Z, mesh.Vertices[i].Z); try { vertexData[i * 6 + 3] = mesh.Normals[i].X; vertexData[i * 6 + 4] = mesh.Normals[i].Y; vertexData[i * 6 + 5] = mesh.Normals[i].Z; } catch (System.ArgumentOutOfRangeException) { System.Console.Error.WriteLine("Failed indexing mesh normals"); throw; } } GL.BindBuffer(BufferTarget.ArrayBuffer, Vbo); GL.BufferData(BufferTarget.ArrayBuffer, sizeof(float) * vertexData.Length, vertexData, BufferUsageHint.StaticDraw); GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 24, new IntPtr(0)); GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 24, new IntPtr(12)); vCount = mesh.VertexCount; }
private void addToIndexArray(Assimp.Mesh mesh, ref List <int>[] idxArray, int offset) { int idx = mesh.MaterialIndex; if (idxArray[idx] == null) { idxArray[idx] = new List <int>(); } for (var i = 0; i < mesh.FaceCount; ++i) { idxArray[idx].Add(mesh.Faces[i].Indices[0] + offset); idxArray[idx].Add(mesh.Faces[i].Indices[1] + offset); idxArray[idx].Add(mesh.Faces[i].Indices[2] + offset); } }
public static Vector2[] GetTexCoords(this Assimp.Mesh mesh) { var uvs = new Vector2[mesh.VertexCount]; for (int i = 0; i < mesh.VertexCount; i++) { if (i >= mesh.TextureCoordinateChannels[0].Count) { uvs[i] = Vector2.Zero; } else { uvs[i] = new Vector2(mesh.TextureCoordinateChannels[0][i].X, mesh.TextureCoordinateChannels[0][i].Y); } } return(uvs); }
public void LoadModelVertices(Assimp.Mesh mesh, bool append = false) { var verts = new List <VertVNT>(); bool isTextured = mesh.HasTextureCoords(0); for (int i = 0; i < mesh.VertexCount; i++) { verts.Add(new VertVNT() { Position = mesh.Vertices[i].ToGL(), Normal = mesh.Normals[i].ToGL(), TexCoord = isTextured ? mesh.TextureCoordinateChannels[0][i].ToGL().Xy : Vector2.Zero }); } if (append) { AppendVertices(verts.Select(x => x.Cast <T>())); } else { SetVertices(verts.Select(x => x.Cast <T>())); } var indices = new List <int>(); int indexPerFace = mesh.Faces[0].IndexCount; foreach (var face in mesh.Faces) { if (face.IndexCount == indexPerFace) { indices.AddRange(face.Indices); } } if (append) { AppendIndices(indices); } else { SetIndices(indices); } }
bool use_float_for_texcoords(Assimp.Mesh mesh, int texchannel) { // SuperBMD normally converts UV coords into signed 16 bit integers // with a fractional part of 8 bits so these are the minimum and maximum values that can be represented. float min = -(float)(1 << 15) / (float)(1 << 8); float max = (float)((1 << 15) - 1) / (float)(1 << 8); foreach (Assimp.Vector3D texcoord in mesh.TextureCoordinateChannels[texchannel]) { if (texcoord.X < min || texcoord.X > max || texcoord.Y < min || texcoord.Y > max) { // Texture coordinates exceed the maximum limit so we need // to use floats return(true); } } return(false); }
private static Assimp.Mesh ConvertMeshType4(MeshType4 mesh, Node node, int nodeIndex) { var aiMesh = new Assimp.Mesh { MaterialIndex = mesh.MaterialIndex }; aiMesh.Faces.AddRange(mesh.Triangles.Select(x => new Assimp.Face(new int[] { x.A, x.B, x.C }))); (var positions, var normals) = mesh.Transform(node.WorldTransform); aiMesh.Vertices.AddRange(positions.ToAssimp()); if (normals != null) { aiMesh.Normals.AddRange(normals.ToAssimp()); } AssignFauxWeights(aiMesh, node, nodeIndex); return(aiMesh); }
private void GetBound(Assimp.Mesh mesh, ref vec3 max, ref vec3 min, ref bool first) { foreach (var item in mesh.Vertices) { if (first) { max = new vec3(item.X, item.Y, item.Z); min = max; first = false; } else { if (max.x < item.X) { max.x = item.X; } if (max.y < item.Y) { max.y = item.Y; } if (max.z < item.Z) { max.z = item.Z; } if (item.X < min.x) { min.x = item.X; } if (item.Y < min.y) { min.y = item.Y; } if (item.Z < min.z) { min.z = item.Z; } } } }
private static IEnumerable <Assimp.Mesh> ConvertMeshType2(MeshType2 mesh, List <Node> nodes) { foreach (var batch in mesh.Batches) { var aiMesh = new Assimp.Mesh { MaterialIndex = mesh.MaterialIndex }; aiMesh.Faces.AddRange(batch.Triangles.Select(x => new Assimp.Face(new int[] { x.A, x.B, x.C }))); (var positions, var normals, var weights) = batch.Transform(nodes); aiMesh.Vertices.AddRange(positions.ToAssimp()); if (normals != null) { aiMesh.Normals.AddRange(normals.ToAssimp()); } ConvertWeights(nodes, new Dictionary <int, Assimp.Bone>(), weights, aiMesh, 0); if (batch.Colors != null) { aiMesh.VertexColorChannels[0].AddRange(batch.Colors.ToAssimp()); } if (batch.TexCoords != null) { aiMesh.TextureCoordinateChannels[0].AddRange(batch.TexCoords.ToAssimp()); } if (batch.TexCoords2 != null) { aiMesh.TextureCoordinateChannels[1].AddRange(batch.TexCoords2.ToAssimp()); } yield return(aiMesh); } }
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)); }
/// <summary> /// Transform the vertex positions and normals in the mesh by the transform of the root node. /// </summary> /// <param name="assimpMesh"></param> /// <param name="rootNode"></param> private static void TransformMeshVertices(Assimp.Mesh assimpMesh, Assimp.Node rootNode) { var worldTransform = ComputeWorldTransform(rootNode); var worldTransformInv = worldTransform; worldTransformInv.Transpose(); worldTransformInv.Inverse(); for (int j = 0; j < assimpMesh.Vertices.Count; j++) { var vector = assimpMesh.Vertices[j]; Assimp.Unmanaged.AssimpLibrary.Instance.TransformVecByMatrix4(ref vector, ref worldTransform); assimpMesh.Vertices[j] = vector; } for (int j = 0; j < assimpMesh.Normals.Count; j++) { var vector = assimpMesh.Normals[j]; Assimp.Unmanaged.AssimpLibrary.Instance .TransformVecByMatrix4(ref vector, ref worldTransformInv); assimpMesh.Normals[j] = vector; } }
private static Assimp.Mesh ConvertMeshType7(MeshType7 mesh, List <Node> nodes) { var aiBoneLookup = new Dictionary <int, Assimp.Bone>(); var aiMesh = new Assimp.Mesh { MaterialIndex = mesh.MaterialIndex }; aiMesh.Faces.AddRange(mesh.Triangles.Select(x => new Assimp.Face(new int[] { x.A, x.B, x.C }))); foreach (var batch in mesh.Batches) { (var positions, var normals, var weights) = batch.Transform(nodes); ConvertWeights(nodes, aiBoneLookup, weights, aiMesh, aiMesh.VertexCount); aiMesh.Vertices.AddRange(positions.ToAssimp()); if (normals != null) { aiMesh.Normals.AddRange(normals.ToAssimp()); } if (batch.TexCoords != null) { aiMesh.TextureCoordinateChannels[0].AddRange(batch.TexCoords.ToAssimp()); } } if (mesh.TexCoords2 != null) { aiMesh.TextureCoordinateChannels[1].AddRange(mesh.TexCoords2.ToAssimp()); } return(aiMesh); }
public bool ExportToFile(String fileName) { string name = Utility.CleanName(Name).TrimEnd(new char[] { '_' }); Assimp.Node rootNode = new Assimp.Node(name); List <Assimp.Material> materials = new List <Assimp.Material>(); List <Assimp.Mesh> meshes = new List <Assimp.Mesh>(); for (int modelIndex = 0; modelIndex < ModelCount; modelIndex++) { SRModel model = Models[modelIndex]; string modelName = name + "-" + modelIndex; Assimp.Node modelNode = new Assimp.Node(modelName); for (int groupIndex = 0; groupIndex < model.GroupCount; groupIndex++) { Tree group = model.Groups[groupIndex]; if (group == null) { continue; } string groupName = name + "-" + modelIndex + "-" + groupIndex; Assimp.Node groupNode = new Assimp.Node(groupName); for (int materialIndex = 0; materialIndex < model.MaterialCount; materialIndex++) { int totalPolygonCount = (int)group.mesh.polygonCount; List <int> polygonList = new List <int>(); for (int p = 0; p < totalPolygonCount; p++) { if (group.mesh.polygons[p].material.ID == materialIndex) { polygonList.Add(p); } } int polygonCount = polygonList.Count; if (polygonCount > 0) { #region Mesh string meshName = name + "-" + modelIndex + "-" + groupIndex + "-" + materialIndex; Assimp.Mesh mesh = new Assimp.Mesh(meshName); mesh.PrimitiveType = Assimp.PrimitiveType.Triangle; ref Polygon[] polygons = ref group.mesh.polygons; Vector[] positions = model.Positions; Vector[] normals = model.Normals; UInt32[] colors = model.Colours; UV[] uvs = model.UVs; int i = 0; for (int p = 0; p < polygonCount; p++) { ref Polygon polygon = ref polygons[polygonList[p]]; ref Vertex vert1 = ref polygon.v1; ref Vertex vert2 = ref polygon.v2; ref Vertex vert3 = ref polygon.v3; ref Vector pos1 = ref positions[vert1.positionID];
public AnimationModel(Assimp.Mesh mesh, AssimpSceneContainer container) { this.mesh = mesh; this.container = container; InitSkinInfo(mesh, container); }
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)); } } }
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 Assimp.Mesh ToAssimp(Hatzap.Models.Mesh mesh) { var verts = mesh.Vertices; var norms = mesh.Normals; var tangents = mesh.Tangents; var binormals = mesh.Binormals; var uv = mesh.UV; var colors = mesh.Colors; var amesh = new Assimp.Mesh(Assimp.PrimitiveType.Triangle); for (int i = 0; i < verts.Length; i++) { if (verts != null) amesh.Vertices.Add(new Assimp.Vector3D(verts[i].X, verts[i].Y, verts[i].Z)); if (norms != null) amesh.Normals.Add(new Assimp.Vector3D(norms[i].X, norms[i].Y, norms[i].Z)); if (tangents != null) amesh.Tangents.Add(new Assimp.Vector3D(tangents[i].X, tangents[i].Y, tangents[i].Z)); if (binormals != null) amesh.BiTangents.Add(new Assimp.Vector3D(binormals[i].X, binormals[i].Y, binormals[i].Z)); if (uv != null) { for (int j = 0; j < uv.Length; j++) { amesh.TextureCoordinateChannels[j].Add(new Assimp.Vector3D(uv[j][i].X, uv[j][i].Y, uv[j][i].Z)); } } if (colors != null) { for (int j = 0; j < uv.Length; j++) { amesh.VertexColorChannels[j].Add(new Assimp.Color4D(colors[j][i].X, colors[j][i].Y, colors[j][i].Z, colors[j][i].W)); } } } return amesh; }
public DrawableRotated(Vector3 position, Vector3 rotation, Assimp.Mesh assMesh) : base(position, rotation) { }
public static Assimp.Scene ToAssimpScene(RWScene scene) { Assimp.Scene aiScene = new Assimp.Scene(); int drawCallIdx = 0; int materialIdx = 0; int totalSplitIdx = 0; List<int> meshStartIndices = new List<int>(); foreach (RWDrawCall drawCall in scene.DrawCalls) { meshStartIndices.Add(totalSplitIdx); var mesh = scene.Meshes[drawCall.MeshIndex]; var node = scene.Nodes[drawCall.NodeIndex]; int splitIdx = 0; foreach (RWMeshMaterialSplit split in mesh.MaterialSplitData.MaterialSplits) { Assimp.Mesh aiMesh = new Assimp.Mesh(Assimp.PrimitiveType.Triangle); aiMesh.Name = string.Format("DrawCall{0}_Split{1}", drawCallIdx.ToString("00"), splitIdx.ToString("00")); aiMesh.MaterialIndex = split.MaterialIndex + materialIdx; // get split indices int[] indices = split.Indices; if (mesh.MaterialSplitData.PrimitiveType == RWPrimitiveType.TriangleStrip) indices = MeshUtilities.ToTriangleList(indices, true); // pos & nrm for (int i = 0; i < indices.Length; i++) { if (mesh.HasVertices) { var vert = Vector3.Transform(mesh.Vertices[indices[i]], node.WorldTransform); aiMesh.Vertices.Add(vert.ToAssimpVector3D()); } if (mesh.HasNormals) { var nrm = Vector3.TransformNormal(mesh.Normals[indices[i]], node.WorldTransform); aiMesh.Normals.Add(nrm.ToAssimpVector3D()); } } // tex coords if (mesh.HasTexCoords) { for (int i = 0; i < mesh.TextureCoordinateChannelCount; i++) { List<Assimp.Vector3D> texCoordChannel = new List<Assimp.Vector3D>(); for (int j = 0; j < indices.Length; j++) { texCoordChannel.Add(mesh.TextureCoordinateChannels[i][indices[j]].ToAssimpVector3D(0)); } aiMesh.TextureCoordinateChannels[i] = texCoordChannel; } } // colors if (mesh.HasColors) { List<Assimp.Color4D> vertColorChannel = new List<Assimp.Color4D>(); for (int i = 0; i < indices.Length; i++) { var color = mesh.Colors[indices[i]]; vertColorChannel.Add(new Assimp.Color4D(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f)); } aiMesh.VertexColorChannels[0] = vertColorChannel; } // generate temporary face indices int[] tempIndices = new int[aiMesh.VertexCount]; for (int i = 0; i < aiMesh.VertexCount; i++) tempIndices[i] = i; aiMesh.SetIndices(tempIndices, 3); // add the mesh to the list aiScene.Meshes.Add(aiMesh); splitIdx++; } totalSplitIdx += splitIdx; foreach (RWMaterial mat in mesh.Materials) { Assimp.Material aiMaterial = new Assimp.Material(); aiMaterial.AddProperty(new Assimp.MaterialProperty(Assimp.Unmanaged.AiMatKeys.NAME, "Material" + (materialIdx++).ToString("00"))); if (mat.IsTextured) { aiMaterial.AddProperty(new Assimp.MaterialProperty(Assimp.Unmanaged.AiMatKeys.TEXTURE_BASE, mat.TextureReference.ReferencedTextureName + ".png", Assimp.TextureType.Diffuse, 0)); } aiScene.Materials.Add(aiMaterial); } drawCallIdx++; } // store node lookup Dictionary<RWSceneNode, Assimp.Node> nodeLookup = new Dictionary<RWSceneNode, Assimp.Node>(); // first create the root node var rootNode = new Assimp.Node("SceneRoot"); rootNode.Transform = scene.Nodes[0].Transform.ToAssimpMatrix4x4(); nodeLookup.Add(scene.Nodes[0], rootNode); for (int i = 1; i < scene.Nodes.Count - 1; i++) { var node = scene.Nodes[i]; string name = node.BoneMetadata.BoneNameID.ToString(); var aiNode = new Assimp.Node(name); aiNode.Transform = node.Transform.ToAssimpMatrix4x4(); // get the associated meshes for this node var drawCalls = scene.DrawCalls.FindAll(dc => dc.NodeIndex == i); foreach (var drawCall in drawCalls) { for (int j = 0; j < scene.Meshes[drawCall.MeshIndex].MaterialCount; j++) { aiNode.MeshIndices.Add(meshStartIndices[scene.DrawCalls.IndexOf(drawCall)] + j); } } nodeLookup[node.Parent].Children.Add(aiNode); nodeLookup.Add(node, aiNode); } aiScene.RootNode = rootNode; return aiScene; }