public GX_DisplayList(HSD_POBJ pobj) { var attrs = pobj.Attributes; if (attrs[attrs.Length - 1].AttributeName != GXAttribName.GX_VA_NULL) { throw new System.FormatException("Attribute buffer does not end with null vertex attribute"); } Attributes.AddRange(attrs); Open(pobj.DisplayListBuffer); Vertices.AddRange(GX_VertexAccessor.GetDecodedVertices(this, pobj)); if (pobj.EnvelopeWeights != null) { Envelopes.AddRange(pobj.EnvelopeWeights); } }
/// <summary> /// Reads the vertex buffer into a more accessable format : <see cref="GXVertex"/> /// </summary> /// <param name="DisplayList">Display list belonging to given PBOJ</param> /// <param name="Polygon"><see cref="HSD_POBJ"/> the the display list belong to</param> /// <returns>Array of <see cref="GXVertex"/></returns> public static GX_Shape[] GetShapeSet(GX_DisplayList DisplayList, HSD_POBJ Polygon, int shapeset) { // Create Vertex List List <GX_Shape> Vertices = new List <GX_Shape>(); // Read through the Display Lists foreach (GX_PrimitiveGroup pg in DisplayList.Primitives) { var v = GetDecodedVertices(pg, DisplayList.Attributes, Polygon, shapeset); foreach (var gv in v) { Vertices.Add(new GX_Shape() { POS = gv.POS, NRM = gv.NRM }); } } return(Vertices.ToArray()); }
public GX_DisplayList(HSD_POBJ pobj, GX_Attribute[] attrs = null) { // read attributes if (attrs == null) { attrs = pobj.ToGXAttributes(); } if (attrs[attrs.Length - 1].AttributeName != GXAttribName.GX_VA_NULL) { System.Diagnostics.Debug.WriteLine("Attribute buffer does not end with null vertex attribute"); } Attributes.AddRange(attrs); // read display list buffer Open(pobj.DisplayListBuffer); // load vertices Vertices.AddRange(GX_VertexAccessor.GetDecodedVertices(this, pobj)); // load shape sets if they exist if (pobj.ShapeSet != null) { for (int i = 0; i < pobj.ShapeSet.VertexIndices.Count; i++) { ShapeSets.Add(GX_VertexAccessor.GetShapeSet(this, pobj, i)); } } // get envelopse if (pobj.EnvelopeWeights != null) { Envelopes.AddRange(pobj.EnvelopeWeights); } }
/// <summary> /// /// </summary> /// <returns></returns> private void ProcessMesh(IOScene scene, IOMesh mesh, HSD_JOBJ rootnode) { HSD_JOBJ parent = rootnode; HashSet <HSD_JOBJ> nodes = new HashSet <HSD_JOBJ>(); foreach (var j in rootnode.BreathFirstList) { nodes.Add(j); } if (mesh.ParentBone != null && _cache.NameToJOBJ.ContainsKey(mesh.ParentBone.Name)) { parent = _cache.NameToJOBJ[mesh.ParentBone.Name]; } HSD_DOBJ root = null; HSD_DOBJ prev = null; //var skeleton = rootnode.BreathFirstList; Console.WriteLine("Processing " + mesh.Name); bool singleBinded = mesh.Name.Contains("SINGLE"); foreach (var poly in mesh.Polygons) { // Skip Empty Polygon if (poly.Indicies.Count == 0) { continue; } // convert to triangles poly.ToTriangles(mesh); if (poly.PrimitiveType != IOPrimitive.TRIANGLE) { continue; } // Generate DOBJ HSD_DOBJ dobj = new HSD_DOBJ(); if (Settings.ImportMeshNames) { dobj.ClassName = mesh.Name; } if (root == null) { root = dobj; } else { prev.Next = dobj; } prev = dobj; // generate material var material = scene.Materials.Find(e => e.Name == poly.MaterialName); dobj.Mobj = GenerateMaterial(material); Console.WriteLine(mesh.Name + " " + material?.Name); // reflective mobjs do not use uvs var hasReflection = false; // bump maps need tangents and bitangents var hasBump = false; // Assess needed attributes based on the material MOBJ if (mesh.Name.Contains("REFLECTIVE")) { hasReflection = true; } #if DEBUG if (Settings.MetalModel) { hasReflection = true; } #endif if (mesh.Name.Contains("BUMP")) { hasBump = true; } if (dobj.Mobj.Textures != null) { foreach (var t in dobj.Mobj.Textures.List) { if (t.Flags.HasFlag(TOBJ_FLAGS.COORD_REFLECTION)) { hasReflection = true; } if (t.Flags.HasFlag(TOBJ_FLAGS.BUMP)) { hasBump = true; } } } // assess attributes List <GXAttribName> Attributes = new List <GXAttribName>(); if (mesh.HasEnvelopes() && Settings.ImportRigging && !singleBinded) { Attributes.Add(GXAttribName.GX_VA_PNMTXIDX); if (hasReflection) { Attributes.Add(GXAttribName.GX_VA_TEX0MTXIDX); if (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 1) { Attributes.Add(GXAttribName.GX_VA_TEX1MTXIDX); } #if DEBUG if (Settings.MetalModel && !Attributes.Contains(GXAttribName.GX_VA_TEX1MTXIDX)) { Attributes.Add(GXAttribName.GX_VA_TEX1MTXIDX); } #endif } } Attributes.Add(GXAttribName.GX_VA_POS); if (hasBump) { Attributes.Add(GXAttribName.GX_VA_NBT); } else if (mesh.HasNormals && Settings.ImportNormals) { Attributes.Add(GXAttribName.GX_VA_NRM); } if (mesh.HasColorSet(0) && Settings.ImportVertexColor) { Attributes.Add(GXAttribName.GX_VA_CLR0); } if (mesh.HasColorSet(1) && Settings.ImportVertexColor) { Attributes.Add(GXAttribName.GX_VA_CLR1); } if (mesh.HasUVSet(0) && !hasReflection) { Attributes.Add(GXAttribName.GX_VA_TEX0); } if ((mesh.HasUVSet(1) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 1)) && !hasReflection) { Attributes.Add(GXAttribName.GX_VA_TEX1); } if ((mesh.HasUVSet(2) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 2)) && !hasReflection) { Attributes.Add(GXAttribName.GX_VA_TEX2); } if ((mesh.HasUVSet(3) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 3)) && !hasReflection) { Attributes.Add(GXAttribName.GX_VA_TEX3); } if ((mesh.HasUVSet(4) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 4)) && !hasReflection) { Attributes.Add(GXAttribName.GX_VA_TEX4); } if ((mesh.HasUVSet(5) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 5)) && !hasReflection) { Attributes.Add(GXAttribName.GX_VA_TEX5); } if ((mesh.HasUVSet(6) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 6)) && !hasReflection) { Attributes.Add(GXAttribName.GX_VA_TEX6); } if ((mesh.HasUVSet(7) || (dobj.Mobj.Textures != null && dobj.Mobj.Textures.List.Count > 7)) && !hasReflection) { Attributes.Add(GXAttribName.GX_VA_TEX7); } var vertices = new List <GX_Vertex>(); var jobjList = new List <HSD_JOBJ[]>(); var weightList = new List <float[]>(); foreach (var face in poly.Indicies) { var v = mesh.Vertices[face]; GX_Vertex vertex = new GX_Vertex(); var tkvert = new Vector3(v.Position.X, v.Position.Y, v.Position.Z); var tknrm = new Vector3(v.Normal.X, v.Normal.Y, v.Normal.Z); var tktan = new Vector3(v.Tangent.X, v.Tangent.Y, v.Tangent.Z); var tkbitan = new Vector3(v.Binormal.X, v.Binormal.Y, v.Binormal.Z); var parentTransform = _cache.jobjToWorldTransform[parent].Inverted(); if (_cache.jobjToWorldTransform[parent] != Matrix4.Identity) { tkvert = Vector3.TransformPosition(tkvert, parentTransform); tknrm = Vector3.TransformNormal(tknrm, parentTransform).Normalized(); tktan = Vector3.TransformNormal(tktan, parentTransform).Normalized(); tkbitan = Vector3.TransformNormal(tkbitan, parentTransform).Normalized(); } if (mesh.HasEnvelopes() && Settings.ImportRigging) { // create weighting lists List <float> weight = new List <float>(); List <HSD_JOBJ> bones = new List <HSD_JOBJ>(); if (v.Envelope.Weights.Count == 0) { weight.Add(1); bones.Add(rootnode); } if (v.Envelope.Weights.Count > 4) { throw new Exception($"Too many weights! {v.Envelope.Weights.Count} in {mesh.Name}"); } foreach (var bw in v.Envelope.Weights) { // check if skeleton actually contains bone if (_cache.NameToJOBJ.ContainsKey(bw.BoneName) && nodes.Contains(_cache.NameToJOBJ[bw.BoneName])) { // add envelope bones.Add(_cache.NameToJOBJ[bw.BoneName]); weight.Add(bw.Weight); // indicate enveloped jobjs if (!_cache.EnvelopedJOBJs.Contains(_cache.NameToJOBJ[bw.BoneName])) { _cache.EnvelopedJOBJs.Add(_cache.NameToJOBJ[bw.BoneName]); } } else { throw new Exception($"Bone not found \"{bw.BoneName}\" Weight: {bw.Weight} in {mesh.Name}"); } } jobjList.Add(bones.ToArray()); weightList.Add(weight.ToArray()); // invert single binds if (v.Envelope.Weights.Count == 1) { var inv = _cache.jobjToWorldTransform[_cache.NameToJOBJ[v.Envelope.Weights[0].BoneName]].Inverted(); tkvert = Vector3.TransformPosition(tkvert, inv); tknrm = Vector3.TransformNormal(tknrm, inv).Normalized(); tktan = Vector3.TransformNormal(tknrm, inv).Normalized(); tkbitan = Vector3.TransformNormal(tknrm, inv).Normalized(); } } vertex.POS = GXTranslator.fromVector3(tkvert); vertex.NRM = GXTranslator.fromVector3(tknrm.Normalized()); vertex.TAN = GXTranslator.fromVector3(tktan); vertex.BITAN = GXTranslator.fromVector3(tkbitan); if (Settings.InvertNormals) { vertex.NRM.X *= -1; vertex.NRM.Y *= -1; vertex.NRM.Z *= -1; vertex.TAN.X *= -1; vertex.TAN.Y *= -1; vertex.TAN.Z *= -1; vertex.BITAN.X *= -1; vertex.BITAN.Y *= -1; vertex.BITAN.Z *= -1; } if (mesh.HasUVSet(0)) { vertex.TEX0 = new GXVector2(v.UVs[0].X, v.UVs[0].Y); } if (mesh.HasUVSet(1)) { vertex.TEX1 = new GXVector2(v.UVs[1].X, v.UVs[1].Y); } if (mesh.HasUVSet(2)) { vertex.TEX2 = new GXVector2(v.UVs[2].X, v.UVs[2].Y); } if (mesh.HasUVSet(3)) { vertex.TEX3 = new GXVector2(v.UVs[3].X, v.UVs[3].Y); } if (mesh.HasUVSet(4)) { vertex.TEX4 = new GXVector2(v.UVs[4].X, v.UVs[4].Y); } if (mesh.HasUVSet(5)) { vertex.TEX5 = new GXVector2(v.UVs[5].X, v.UVs[5].Y); } if (mesh.HasUVSet(6)) { vertex.TEX6 = new GXVector2(v.UVs[6].X, v.UVs[6].Y); } if (mesh.HasUVSet(7)) { vertex.TEX7 = new GXVector2(v.UVs[7].X, v.UVs[7].Y); } if (mesh.HasColorSet(0)) { vertex.CLR0 = new GXColor4( v.Colors[0].X * (Settings.MultiplyVertexColorBy2 ? 2 : 1), v.Colors[0].Y * (Settings.MultiplyVertexColorBy2 ? 2 : 1), v.Colors[0].Z * (Settings.MultiplyVertexColorBy2 ? 2 : 1), Settings.ImportVertexAlpha ? v.Colors[0].W : 1); } if (mesh.HasColorSet(1)) { vertex.CLR1 = new GXColor4( v.Colors[1].X * (Settings.MultiplyVertexColorBy2 ? 2 : 1), v.Colors[1].Y * (Settings.MultiplyVertexColorBy2 ? 2 : 1), v.Colors[1].Z * (Settings.MultiplyVertexColorBy2 ? 2 : 1), Settings.ImportVertexAlpha ? v.Colors[1].W : 1); } vertices.Add(vertex); } // generate pobjs HSD_POBJ pobj = null; if (mesh.HasEnvelopes() && Settings.ImportRigging && !singleBinded) { pobj = _cache.POBJGen.CreatePOBJsFromTriangleList(vertices, Attributes.ToArray(), jobjList, weightList); } else { pobj = _cache.POBJGen.CreatePOBJsFromTriangleList(vertices, Attributes.ToArray(), null); } if (singleBinded && jobjList.Count > 0 && jobjList[0].Length > 0) { parent = jobjList[0][0]; } if (pobj != null) { if (dobj.Pobj == null) { dobj.Pobj = pobj; } else { dobj.Pobj.Add(pobj); } } } if (parent.Dobj == null) { parent.Dobj = root; } else { parent.Dobj.Add(root); } }
/// <summary> /// /// </summary> /// <param name="triList"></param> /// <param name="attrGroup"></param> /// <param name="weights"></param> public HSD_POBJ CreatePOBJsFromTriangleList(List <GX_Vertex> triList, GXAttribName[] attributes, List <HSD_Envelope> weights) { TriangleConverter.TriangleConverter converter = new TriangleConverter.TriangleConverter(UseTriangleStrips, 32, 3, true); int pointCount, faceCount; HSD_JOBJ singleBind = null; bool HasPositionNormalMatrix = attributes.Contains(GXAttribName.GX_VA_PNMTXIDX); // Optimize single bind /*if (weights != null && weights.Count == 1 && weights[0].EnvelopeCount == 1) * { * singleBind = weights[0].GetJOBJAt(0); * var al = attributes.ToList(); * al.Remove(GXAttribName.GX_VA_PNMTXIDX); * attributes = al.ToArray(); * }*/ var groups = converter.GroupPrimitives(triList.ToArray(), out pointCount, out faceCount); HSD_POBJ rootPOBJ = null; HSD_POBJ prevPOBJ = null; foreach (var g in groups) { var jobjweights = new List <HSD_Envelope>(); var pmidToNewID = new Dictionary <ushort, ushort>(); if (HasPositionNormalMatrix == true && weights.Count > 0) { foreach (var n in g._nodes) { pmidToNewID.Add(n, (ushort)(jobjweights.Count * 3)); jobjweights.Add(weights[n / 3]); } } GX_DisplayList newdl = new GX_DisplayList(); foreach (var t in g._triangles) { var newVert = new List <GX_Vertex>(); for (int p = 0; p < t.Points.Count; p++) { var point = t.Points[p]; if (HasPositionNormalMatrix) { point.PNMTXIDX = pmidToNewID[point.PNMTXIDX]; point.TEX0MTXIDX = (ushort)(point.PNMTXIDX + 30); point.TEX1MTXIDX = point.TEX0MTXIDX; } t.Points[p] = point; newVert.Add(point); } newdl.Primitives.Add(Compress(GXPrimitiveType.Triangles, newVert.ToArray(), attributes)); } foreach (var t in g._tristrips) { var newVert = new List <GX_Vertex>(); for (int p = 0; p < t.Points.Count; p++) { var point = t.Points[p]; if (HasPositionNormalMatrix) { point.PNMTXIDX = pmidToNewID[point.PNMTXIDX]; point.TEX0MTXIDX = (ushort)(point.PNMTXIDX + 30); point.TEX1MTXIDX = point.TEX0MTXIDX; } t.Points[p] = point; newVert.Add(point); } newdl.Primitives.Add(Compress(GXPrimitiveType.TriangleStrip, newVert.ToArray(), attributes)); } if (singleBind == null) { newdl.Envelopes = jobjweights; } var newpobj = new HSD_POBJ(); CreatedPOBJs.Add(newpobj); if (singleBind != null) { newpobj.SingleBoundJOBJ = singleBind; } pobjToDisplayList.Add(newpobj, newdl); pobjToAttributes.Add(newpobj, attributes); if (prevPOBJ == null) { rootPOBJ = newpobj; } else { prevPOBJ.Next = newpobj; } prevPOBJ = newpobj; } //Console.WriteLine(rootPOBJ.List.Count + " POBJs generated"); return(rootPOBJ); }
/// <summary> /// Gets decoded vertices for <see cref="HSD_POBJ"/> /// </summary> /// <param name="Polygon"></param> /// <returns></returns> public static GXVertex[] GetDecodedVertices(HSD_POBJ Polygon) { return(GetDecodedVertices(GetDisplayList(Polygon), Polygon)); }
/// <summary> /// Returns display list for <see cref="HSD_POBJ"/> /// </summary> /// <param name="Polygon"></param> /// <returns></returns> public static GXDisplayList GetDisplayList(HSD_POBJ Polygon) { return(new GXDisplayList(Polygon.DisplayListBuffer, Polygon.VertexAttributes)); }
/// <summary> /// /// </summary> /// <returns></returns> private static HSD_DOBJ GetMeshes(ProcessingCache cache, ModelImportSettings settings, Scene scene, Node node) { HSD_DOBJ root = null; HSD_DOBJ prev = null; Console.WriteLine("Processing " + node.Name); foreach (int index in node.MeshIndices) { Mesh mesh = scene.Meshes[index]; var material = scene.Materials[mesh.MaterialIndex]; Console.WriteLine(mesh.Name + " " + material.Name); // Generate DOBJ HSD_DOBJ dobj = new HSD_DOBJ(); // hack to make dobjs merged by texture if (settings.ImportTexture && settings.ForceMergeObjects == ForceGroupModes.Texture && material.HasTextureDiffuse && cache.TextureToDOBJ.ContainsKey(material.TextureDiffuse.FilePath)) { dobj = cache.TextureToDOBJ[material.TextureDiffuse.FilePath]; } else { if (root == null) { root = dobj; } else { prev.Next = dobj; } prev = dobj; dobj.Mobj = GenerateMaterial(cache, settings, material); if (settings.ForceMergeObjects == ForceGroupModes.Texture && material.HasTextureDiffuse && settings.ImportTexture) { cache.TextureToDOBJ.Add(material.TextureDiffuse.FilePath, dobj); } } if (root != null && settings.ForceMergeObjects == ForceGroupModes.MeshGroup) { dobj = root; } // Assessment if (!mesh.HasFaces) { continue; } // Assess needed attributes based on the material MOBJ // reflective mobjs do not use uvs var hasReflection = false; // bump maps need tangents and bitangents var hasBump = false; if (dobj.Mobj.Textures != null) { foreach (var t in dobj.Mobj.Textures.List) { if (t.Flags.HasFlag(TOBJ_FLAGS.COORD_REFLECTION)) { hasReflection = true; } if (t.Flags.HasFlag(TOBJ_FLAGS.BUMP)) { hasBump = true; } } } List <GXAttribName> Attributes = new List <GXAttribName>(); // todo: rigging List <HSD_JOBJ>[] jobjs = new List <HSD_JOBJ> [mesh.Vertices.Count]; List <float>[] weights = new List <float> [mesh.Vertices.Count]; if (mesh.HasBones) { Attributes.Add(GXAttribName.GX_VA_PNMTXIDX); foreach (var v in mesh.Bones) { var jobj = cache.NameToJOBJ[v.Name]; if (!cache.EnvelopedJOBJs.Contains(jobj)) { cache.EnvelopedJOBJs.Add(jobj); } if (v.HasVertexWeights) { foreach (var vw in v.VertexWeights) { if (jobjs[vw.VertexID] == null) { jobjs[vw.VertexID] = new List <HSD_JOBJ>(); } if (weights[vw.VertexID] == null) { weights[vw.VertexID] = new List <float>(); } if (vw.Weight > 0) { jobjs[vw.VertexID].Add(jobj); weights[vw.VertexID].Add(vw.Weight); } } } } } if (hasReflection) { Attributes.Add(GXAttribName.GX_VA_TEX0MTXIDX); } if (mesh.HasVertices) { Attributes.Add(GXAttribName.GX_VA_POS); } if (mesh.HasVertexColors(0) && settings.ShadingType == ShadingType.VertexColor) { Attributes.Add(GXAttribName.GX_VA_CLR0); } //if (mesh.HasVertexColors(1) && settings.ImportVertexColors) // Attributes.Add(GXAttribName.GX_VA_CLR1); if (!hasBump && mesh.HasNormals && settings.ShadingType == ShadingType.Material) { Attributes.Add(GXAttribName.GX_VA_NRM); } if (hasBump) { Attributes.Add(GXAttribName.GX_VA_NBT); } if (mesh.HasTextureCoords(0) && !hasReflection) { Attributes.Add(GXAttribName.GX_VA_TEX0); } //if (mesh.HasTextureCoords(1)) // Attributes.Add(GXAttribName.GX_VA_TEX1); var vertices = new List <GX_Vertex>(); var jobjList = new List <HSD_JOBJ[]>(vertices.Count); var wList = new List <float[]>(vertices.Count); foreach (var face in mesh.Faces) { PrimitiveType faceMode; switch (face.IndexCount) { case 1: faceMode = PrimitiveType.Point; break; case 2: faceMode = PrimitiveType.Line; break; case 3: faceMode = PrimitiveType.Triangle; break; default: faceMode = PrimitiveType.Polygon; break; } if (faceMode != PrimitiveType.Triangle) { continue; //throw new NotSupportedException($"Non triangle primitive types not supported at this time {faceMode}"); } for (int i = 0; i < face.IndexCount; i++) { int indicie = face.Indices[i]; GX_Vertex vertex = new GX_Vertex(); if (mesh.HasBones) { jobjList.Add(jobjs[indicie].ToArray()); wList.Add(weights[indicie].ToArray()); // Single Binds Get Inverted var tkvert = new Vector3(mesh.Vertices[indicie].X, mesh.Vertices[indicie].Y, mesh.Vertices[indicie].Z) * settings.Scale; var tknrm = new Vector3(mesh.Normals[indicie].X, mesh.Normals[indicie].Y, mesh.Normals[indicie].Z); Vector3 tktan = Vector3.Zero; Vector3 tkbitan = Vector3.Zero; if (mesh.HasTangentBasis) { tktan = new Vector3(mesh.Tangents[indicie].X, mesh.Tangents[indicie].Y, mesh.Tangents[indicie].Z); tkbitan = new Vector3(mesh.BiTangents[indicie].X, mesh.BiTangents[indicie].Y, mesh.BiTangents[indicie].Z); } if (jobjs[indicie].Count == 1 || weights[indicie][0] == 1) { tkvert = Vector3.TransformPosition(tkvert, cache.jobjToInverseTransform[jobjs[indicie][0]]); tknrm = Vector3.TransformNormal(tknrm, cache.jobjToInverseTransform[jobjs[indicie][0]]); if (mesh.HasTangentBasis) { tktan = Vector3.TransformNormal(tktan, cache.jobjToInverseTransform[jobjs[indicie][0]]); tkbitan = Vector3.TransformNormal(tkbitan, cache.jobjToInverseTransform[jobjs[indicie][0]]); } } vertex.POS = GXTranslator.fromVector3(tkvert); vertex.NRM = GXTranslator.fromVector3(tknrm); vertex.TAN = GXTranslator.fromVector3(tktan); vertex.BITAN = GXTranslator.fromVector3(tkbitan); } else { if (mesh.HasVertices) { vertex.POS = new GXVector3(mesh.Vertices[indicie].X * settings.Scale, mesh.Vertices[indicie].Y * settings.Scale, mesh.Vertices[indicie].Z * settings.Scale); } if (mesh.HasNormals) { vertex.NRM = new GXVector3(mesh.Normals[indicie].X, mesh.Normals[indicie].Y, mesh.Normals[indicie].Z); } if (mesh.HasTangentBasis) { vertex.TAN = new GXVector3(mesh.Tangents[indicie].X, mesh.Tangents[indicie].Y, mesh.Tangents[indicie].Z); vertex.BITAN = new GXVector3(mesh.BiTangents[indicie].X, mesh.BiTangents[indicie].Y, mesh.BiTangents[indicie].Z); } } if (settings.InvertNormals) { vertex.NRM.X *= -1; vertex.NRM.Y *= -1; vertex.NRM.Z *= -1; } if (mesh.HasTextureCoords(0)) { vertex.TEX0 = new GXVector2( mesh.TextureCoordinateChannels[0][indicie].X, mesh.TextureCoordinateChannels[0][indicie].Y); } if (mesh.HasTextureCoords(1)) { vertex.TEX1 = new GXVector2( mesh.TextureCoordinateChannels[1][indicie].X, mesh.TextureCoordinateChannels[1][indicie].Y); } if (mesh.HasVertexColors(0)) { vertex.CLR0 = new GXColor4( mesh.VertexColorChannels[0][indicie].R, mesh.VertexColorChannels[0][indicie].G, mesh.VertexColorChannels[0][indicie].B, settings.ImportVertexAlpha ? mesh.VertexColorChannels[0][indicie].A : 1); } if (mesh.HasVertexColors(1)) { vertex.CLR0 = new GXColor4( mesh.VertexColorChannels[1][indicie].R, mesh.VertexColorChannels[1][indicie].G, mesh.VertexColorChannels[1][indicie].B, settings.ImportVertexAlpha ? mesh.VertexColorChannels[1][indicie].A : 1); } vertices.Add(vertex); } } HSD_POBJ pobj = null; if (mesh.HasBones) { pobj = cache.POBJGen.CreatePOBJsFromTriangleList(vertices, Attributes.ToArray(), jobjList, wList); } else { pobj = cache.POBJGen.CreatePOBJsFromTriangleList(vertices, Attributes.ToArray(), null); } if (pobj != null) { if (dobj.Pobj == null) { dobj.Pobj = pobj; } else { dobj.Pobj.Add(pobj); } } } return(root); }
/// <summary> /// /// </summary> /// <param name="pobj"></param> private Mesh ProcessPOBJ(HSD_POBJ pobj, HSD_JOBJ parent, HSD_JOBJ singleBind) { Mesh m = new Mesh(); m.Name = "pobj"; m.PrimitiveType = PrimitiveType.Triangle; m.MaterialIndex = 0; var dl = pobj.ToDisplayList(); var envelopes = pobj.EnvelopeWeights; var parentTransform = Matrix4.Identity; if (parent != null) { parentTransform = WorldTransforms[jobjToIndex[parent]]; } var singleBindTransform = Matrix4.Identity; if (singleBind != null) { singleBindTransform = WorldTransforms[jobjToIndex[singleBind]]; } Dictionary <HSD_JOBJ, Bone> jobjToBone = new Dictionary <HSD_JOBJ, Bone>(); if (envelopes != null) { foreach (var jobj in Jobjs) { var bone = new Bone(); bone.Name = JobjNodes[jobjToIndex[jobj]].Name; bone.OffsetMatrix = Matrix4ToMatrix4x4(WorldTransforms[jobjToIndex[jobj]].Inverted()); m.Bones.Add(bone); jobjToBone.Add(jobj, bone); } } /*foreach (var en in envelopes) * { * foreach (var jobj in en.JOBJs) * { * if (!jobjToBone.ContainsKey(jobj)) * { * var bone = new Bone(); * bone.Name = JobjNodes[jobjToIndex[jobj]].Name; * bone.OffsetMatrix = Matrix4ToMatrix4x4(WorldTransforms[jobjToIndex[jobj]].Inverted()); * m.Bones.Add(bone); * jobjToBone.Add(jobj, bone); * } * } * }*/ if (singleBind != null && !jobjToBone.ContainsKey(singleBind)) { var bone = new Bone(); bone.Name = JobjNodes[jobjToIndex[singleBind]].Name; bone.OffsetMatrix = Matrix4ToMatrix4x4(WorldTransforms[jobjToIndex[singleBind]].Inverted()); m.Bones.Add(bone); jobjToBone.Add(singleBind, bone); } int offset = 0; var vIndex = -1; foreach (var prim in dl.Primitives) { var verts = dl.Vertices.GetRange(offset, prim.Count); offset += prim.Count; switch (prim.PrimitiveType) { case GXPrimitiveType.Quads: verts = TriangleConverter.QuadToList(verts); break; case GXPrimitiveType.TriangleStrip: verts = TriangleConverter.StripToList(verts); break; case GXPrimitiveType.Triangles: break; default: Console.WriteLine(prim.PrimitiveType); break; } for (int i = m.VertexCount; i < m.VertexCount + verts.Count; i += 3) { var f = new Face(); f.Indices.Add(i); f.Indices.Add(i + 1); f.Indices.Add(i + 2); m.Faces.Add(f); } foreach (var v in verts) { vIndex++; if (singleBind != null) { var vertexWeight = new VertexWeight(); vertexWeight.VertexID = vIndex; vertexWeight.Weight = 1; jobjToBone[singleBind].VertexWeights.Add(vertexWeight); } Matrix4 weight = Matrix4.Identity; foreach (var a in pobj.Attributes) { switch (a.AttributeName) { case GXAttribName.GX_VA_PNMTXIDX: var en = envelopes[v.PNMTXIDX / 3]; for (int w = 0; w < en.EnvelopeCount; w++) { var vertexWeight = new VertexWeight(); vertexWeight.VertexID = vIndex; vertexWeight.Weight = en.Weights[w]; jobjToBone[en.JOBJs[w]].VertexWeights.Add(vertexWeight); } if (en.EnvelopeCount == 1 && jobjToIndex[parent] == 0) { weight = WorldTransforms[jobjToIndex[en.JOBJs[0]]]; } break; case GXAttribName.GX_VA_POS: var vert = Vector3.TransformPosition(GXTranslator.toVector3(v.POS), parentTransform); vert = Vector3.TransformPosition(vert, weight); vert = Vector3.TransformPosition(vert, singleBindTransform); m.Vertices.Add(new Vector3D(vert.X, vert.Y, vert.Z)); break; case GXAttribName.GX_VA_NRM: var nrm = Vector3.TransformNormal(GXTranslator.toVector3(v.NRM), parentTransform); nrm = Vector3.TransformNormal(nrm, weight); nrm = Vector3.TransformNormal(nrm, singleBindTransform); m.Normals.Add(new Vector3D(nrm.X, nrm.Y, nrm.Z)); break; case GXAttribName.GX_VA_CLR0: m.VertexColorChannels[0].Add(new Color4D(v.CLR0.R, v.CLR0.G, v.CLR0.B, v.CLR0.A)); break; case GXAttribName.GX_VA_CLR1: m.VertexColorChannels[1].Add(new Color4D(v.CLR1.R, v.CLR1.G, v.CLR1.B, v.CLR1.A)); break; case GXAttribName.GX_VA_TEX0: m.TextureCoordinateChannels[0].Add(new Vector3D(v.TEX0.X, v.TEX0.Y, 1)); break; case GXAttribName.GX_VA_TEX1: m.TextureCoordinateChannels[1].Add(new Vector3D(v.TEX1.X, v.TEX1.Y, 1)); break; case GXAttribName.GX_VA_TEX2: m.TextureCoordinateChannels[2].Add(new Vector3D(v.TEX2.X, v.TEX2.Y, 1)); break; case GXAttribName.GX_VA_TEX3: m.TextureCoordinateChannels[3].Add(new Vector3D(v.TEX3.X, v.TEX3.Y, 1)); break; case GXAttribName.GX_VA_TEX4: m.TextureCoordinateChannels[4].Add(new Vector3D(v.TEX4.X, v.TEX4.Y, 1)); break; case GXAttribName.GX_VA_TEX5: m.TextureCoordinateChannels[5].Add(new Vector3D(v.TEX5.X, v.TEX5.Y, 1)); break; case GXAttribName.GX_VA_TEX6: m.TextureCoordinateChannels[6].Add(new Vector3D(v.TEX6.X, v.TEX6.Y, 1)); break; case GXAttribName.GX_VA_TEX7: m.TextureCoordinateChannels[7].Add(new Vector3D(v.TEX7.X, v.TEX7.Y, 1)); break; } } } } return(m); }
/// <summary> /// /// </summary> /// <param name="PrimitiveGroup"></param> /// <param name="Attributes"></param> /// <returns></returns> private static GX_Vertex[] GetDecodedVertices(GX_PrimitiveGroup PrimitiveGroup, List <GX_Attribute> Attributes, HSD_POBJ Polygon, int shapeset) { // Create Vertex List List <GX_Vertex> Vertices = new List <GX_Vertex>(); // Decode foreach (GX_IndexGroup ig in PrimitiveGroup.Indices) { GX_Vertex Vertex = new GX_Vertex(); for (int i = 0; i < Attributes.Count; i++) { var attribute = Attributes[i]; int index = ig.Indices[i]; float[] f = new float[4]; // check if index is in range of buffer if (attribute.AttributeType != GXAttribType.GX_DIRECT && attribute.Buffer != null && index >= attribute.Count) { System.Diagnostics.Debug.WriteLine($"Warning: Attribute index out of range {index} >= {attribute.Count}"); continue; } // check if data is direct if (attribute.AttributeType != GXAttribType.GX_DIRECT) { f = attribute.GetDecodedDataAt(index); } switch (attribute.AttributeName) { case GXAttribName.GX_VA_NULL: break; case GXAttribName.GX_VA_PNMTXIDX: if (attribute.AttributeType == GXAttribType.GX_DIRECT) { Vertex.PNMTXIDX = (ushort)index; } break; case GXAttribName.GX_VA_TEX0MTXIDX: if (attribute.AttributeType == GXAttribType.GX_DIRECT) { Vertex.TEX0MTXIDX = (ushort)index; } break; case GXAttribName.GX_VA_TEX1MTXIDX: if (attribute.AttributeType == GXAttribType.GX_DIRECT) { Vertex.TEX1MTXIDX = (ushort)index; } break; case GXAttribName.GX_VA_POS: if (attribute.AttributeType != GXAttribType.GX_DIRECT) { if (Polygon.ShapeSet != null) { var ss = Polygon.ShapeSet.VertexIndices[shapeset]; f = attribute.GetDecodedDataAt(ss[index]); } if (f.Length > 0) { Vertex.POS.X = f[0]; } if (f.Length > 1) { Vertex.POS.Y = f[1]; } if (f.Length > 2) { Vertex.POS.Z = f[2]; } } break; case GXAttribName.GX_VA_NRM: if (attribute.AttributeType != GXAttribType.GX_DIRECT) { if (Polygon.ShapeSet != null) { var ss = Polygon.ShapeSet.NormalIndicies[shapeset]; f = attribute.GetDecodedDataAt(ss[index]); } Vertex.NRM.X = f[0]; Vertex.NRM.Y = f[1]; Vertex.NRM.Z = f[2]; } break; case GXAttribName.GX_VA_NBT: if (attribute.AttributeType != GXAttribType.GX_DIRECT) { Vertex.NRM.X = f[0]; Vertex.NRM.Y = f[1]; Vertex.NRM.Z = f[2]; Vertex.BITAN.X = f[3]; Vertex.BITAN.Y = f[4]; Vertex.BITAN.Z = f[5]; Vertex.TAN.X = f[6]; Vertex.TAN.Y = f[7]; Vertex.TAN.Z = f[8]; } break; case GXAttribName.GX_VA_TEX0: if (attribute.AttributeType != GXAttribType.GX_DIRECT) { Vertex.TEX0.X = f[0]; Vertex.TEX0.Y = f[1]; } break; case GXAttribName.GX_VA_TEX1: if (attribute.AttributeType != GXAttribType.GX_DIRECT) { Vertex.TEX1.X = f[0]; Vertex.TEX1.Y = f[1]; } break; case GXAttribName.GX_VA_TEX2: if (attribute.AttributeType != GXAttribType.GX_DIRECT) { Vertex.TEX2.X = f[0]; Vertex.TEX2.Y = f[1]; } break; case GXAttribName.GX_VA_TEX3: if (attribute.AttributeType != GXAttribType.GX_DIRECT) { Vertex.TEX3.X = f[0]; Vertex.TEX3.Y = f[1]; } break; case GXAttribName.GX_VA_TEX4: if (attribute.AttributeType != GXAttribType.GX_DIRECT) { Vertex.TEX4.X = f[0]; Vertex.TEX4.Y = f[1]; } break; case GXAttribName.GX_VA_TEX5: if (attribute.AttributeType != GXAttribType.GX_DIRECT) { Vertex.TEX5.X = f[0]; Vertex.TEX5.Y = f[1]; } break; case GXAttribName.GX_VA_TEX6: if (attribute.AttributeType != GXAttribType.GX_DIRECT) { Vertex.TEX6.X = f[0]; Vertex.TEX6.Y = f[1]; } break; case GXAttribName.GX_VA_TEX7: if (attribute.AttributeType != GXAttribType.GX_DIRECT) { Vertex.TEX7.X = f[0]; Vertex.TEX7.Y = f[1]; } break; case GXAttribName.GX_VA_CLR0: if (attribute.AttributeType == GXAttribType.GX_DIRECT) { Vertex.CLR0.R = ig.Clr0[0] / 255f; Vertex.CLR0.G = ig.Clr0[1] / 255f; Vertex.CLR0.B = ig.Clr0[2] / 255f; Vertex.CLR0.A = ig.Clr0[3] / 255f; } else { Vertex.CLR0.R = f[0]; Vertex.CLR0.G = f[1]; Vertex.CLR0.B = f[2]; Vertex.CLR0.A = f[3]; } break; case GXAttribName.GX_VA_CLR1: if (attribute.AttributeType == GXAttribType.GX_DIRECT) { Vertex.CLR1.R = ig.Clr1[0] / 255f; Vertex.CLR1.G = ig.Clr1[1] / 255f; Vertex.CLR1.B = ig.Clr1[2] / 255f; Vertex.CLR1.A = ig.Clr1[3] / 255f; } else { Vertex.CLR1.R = f[0]; Vertex.CLR1.G = f[1]; Vertex.CLR1.B = f[2]; Vertex.CLR1.A = f[3]; } break; default: Console.WriteLine("To be implemented: " + attribute.AttributeName); break; } } Vertices.Add(Vertex); } return(Vertices.ToArray()); }
/// <summary> /// /// </summary> /// <param name="triList"></param> /// <param name="attrGroup"></param> /// <param name="weights"></param> public HSD_POBJ CreatePOBJ(List <GXVertex> triList, HSD_AttributeGroup attrGroup, List <HSD_JOBJWeight> weights) { TriangleConverter.TriangleConverter converter = new TriangleConverter.TriangleConverter(true, 100, 3, true); int pointCount, faceCount; var groups = converter.GroupPrimitives(triList.ToArray(), out pointCount, out faceCount); HSD_POBJ rootPOBJ = null; HSD_POBJ prevPOBJ = null; foreach (var g in groups) { var jobjweights = new List <HSD_JOBJWeight>(); var pmidToNewID = new Dictionary <ushort, ushort>(); foreach (var n in g._nodes) { pmidToNewID.Add(n, (ushort)(jobjweights.Count * 3)); jobjweights.Add(weights[n / 3]); } GXDisplayList newdl = new GXDisplayList(); foreach (var t in g._triangles) { var newVert = new List <GXVertex>(); for (int p = 0; p < t.Points.Count; p++) { var point = t.Points[p]; point.PMXID = pmidToNewID[point.PMXID]; t.Points[p] = point; newVert.Add(point); //Console.WriteLine(t.points[p].PMID + " " + point.PMXID + " " + pmidToNewID[point.PMXID] + " " + jobjweights.Count); } newdl.Primitives.Add(Compress(GXPrimitiveType.Triangles, newVert.ToArray(), attrGroup)); } foreach (var t in g._tristrips) { var newVert = new List <GXVertex>(); for (int p = 0; p < t.Points.Count; p++) { //Console.WriteLine(t.Points[p].PMXID + " " + g._nodes.Count); var point = t.Points[p]; point.PMXID = pmidToNewID[point.PMXID]; t.Points[p] = point; newVert.Add(point); } newdl.Primitives.Add(Compress(GXPrimitiveType.TriangleStrip, newVert.ToArray(), attrGroup)); } HSD_PointerArray <HSD_JOBJWeight> bindWeights = new HSD_PointerArray <HSD_JOBJWeight>(); bindWeights.Elements = jobjweights.ToArray(); var newpobj = new HSD_POBJ(); newpobj.Flags = POBJ_FLAG.ENVELOPE; newpobj.BindGroups = bindWeights; newpobj.VertexAttributes = attrGroup; newpobj.DisplayListBuffer = newdl.ToBuffer(attrGroup); if (prevPOBJ == null) { rootPOBJ = newpobj; } else { prevPOBJ.Next = newpobj; } prevPOBJ = newpobj; } return(rootPOBJ); }