protected static void AddVertexToList(int boneID, Vector2?tex, Vector3?nrm, Vector3 pos, int pos_scale, Color clr, List <ModelBase.VertexDef> vertexList) { Vector3 scaledPos = Vector3.Multiply(pos, (1 << pos_scale)); ModelBase.VertexDef vertex = new ModelBase.VertexDef(scaledPos, tex, nrm, clr, boneID); vertexList.Add(vertex); }
Tuple<ModelBase.FaceListDef, List<int>> GetStripAndIndicesForStartingEvenEdge( TriangleLinked start, TriangleEdge startForwardEdge) { List<int> stripIndices = new List<int>(); List<TriangleRotation> stripRotations = new List<TriangleRotation>(); List<TriangleLinked> linked = GetLinked(start)[(int)startForwardEdge]; TriangleLinked bestNeighbour = DetermineBestNextNeighbour(start, linked, startForwardEdge); if (bestNeighbour == null) return new Tuple<ModelBase.FaceListDef, List<int>>(new ModelBase.FaceListDef(), new List<int>()); TriangleRotation startRotation = (TriangleRotation)((int)(startForwardEdge - TriangleEdge.Edge_BC + 3) % 3); TriangleLinked t = start; TriangleEdge currentEdge = startForwardEdge; TriangleRotation currentRotation = startRotation; bool even = true; int index_t = -1; while (t != null && !stripIndices.Contains((index_t = m_Triangles.IndexOf(t)))) { stripIndices.Add(index_t); stripRotations.Add(currentRotation); linked = GetLinked(t)[(int)currentEdge]; bestNeighbour = DetermineBestNextNeighbour(t, linked, currentEdge); t = bestNeighbour; even = !even; if (t != null) { // Determine rotation and the edge to be used to get the next face ModelBase.FaceDef triangleC_CW = new ModelBase.FaceDef(3); if (even) { triangleC_CW.m_Vertices[0] = t.m_Triangle.m_Vertices[0]; triangleC_CW.m_Vertices[1] = t.m_Triangle.m_Vertices[1]; triangleC_CW.m_Vertices[2] = t.m_Triangle.m_Vertices[2]; } else { triangleC_CW.m_Vertices[0] = t.m_Triangle.m_Vertices[2]; triangleC_CW.m_Vertices[1] = t.m_Triangle.m_Vertices[1]; triangleC_CW.m_Vertices[2] = t.m_Triangle.m_Vertices[0]; } // The edge of the vertices which match the preceding triangle's TriangleEdge linkBackEdge = TriangleEdge.Edge_AB; // The vertices which match the preceding triangle's ModelBase.VertexDef[] currentMatchedEdge = new ModelBase.VertexDef[2]; TriangleLinked previous = m_Triangles[stripIndices[stripIndices.Count - 1]]; currentMatchedEdge[0] = previous.m_Triangle.m_Vertices[(int)(currentEdge + 0) % 3]; currentMatchedEdge[1] = previous.m_Triangle.m_Vertices[(int)(currentEdge + 1) % 3]; // Find the edge in the current triangle which if odd has been made CW which matches // that from the preceding triangle. This will be set as the current triangle's first, // or 'AB' edge and the next edge (next two vertices) will be used to match the next // triangle. for (int i = 0; i < 3; i++) { ModelBase.VertexDef[] edge = new ModelBase.VertexDef[2]; edge[0] = triangleC_CW.m_Vertices[(i + 0) % 3]; edge[1] = triangleC_CW.m_Vertices[(i + 1) % 3]; if (edge.Except(currentMatchedEdge).Count() == 0) { linkBackEdge = (TriangleEdge)i; break; } } TriangleEdge nextEdgeNoC_CW = (TriangleEdge)((int)(linkBackEdge + 1) % 3); TriangleEdge nextEdge = nextEdgeNoC_CW; if (!even) { // If odd, nextEdgeNoC_CW points to the edge to be used if written CW, however // all triangles have been read in as CCW so need to get the corresponding edge // in CCW version. ModelBase.VertexDef[] nextEdgeNoC_CW_Vertices = new ModelBase.VertexDef[2]; nextEdgeNoC_CW_Vertices[0] = triangleC_CW.m_Vertices[(int)(nextEdgeNoC_CW + 0) % 3]; nextEdgeNoC_CW_Vertices[1] = triangleC_CW.m_Vertices[(int)(nextEdgeNoC_CW + 1) % 3]; for (int i = 0; i < 3; i++) { ModelBase.VertexDef[] ccwEdge = new ModelBase.VertexDef[2]; ccwEdge[0] = t.m_Triangle.m_Vertices[(i + 0) % 3]; ccwEdge[1] = t.m_Triangle.m_Vertices[(i + 1) % 3]; if (nextEdgeNoC_CW_Vertices.Except(ccwEdge).Count() == 0) { nextEdge = (TriangleEdge)i; break; } } } // Now we need to determine the required rotation of the current triangle so that for // even triangles the new vertex in at index 0 and for odd triangles it occurs at // index 2. ModelBase.VertexDef uniqueVertex = t.m_Triangle.m_Vertices.Except(previous.m_Triangle.m_Vertices).ElementAt(0); int uniqueVertexIndex = Array.IndexOf(t.m_Triangle.m_Vertices, uniqueVertex); TriangleRotation requiredRotation = (even) ? (TriangleRotation)((uniqueVertexIndex - 2 + 3) % 3) : (TriangleRotation)(uniqueVertexIndex); currentRotation = requiredRotation; currentEdge = nextEdge; // To best understand how this works, debug and step-through how the following model is handled: // // An example: // Faces as defined in model (all Counter-Clockwise (CCW)): // f 1 2 3 // f 4 1 3 // f 4 5 1 // Build strip from edge CA. // # 2 3 1 (LS) <- Need to Left Shift vertices so that CA is the second edge. // # 3 1 4 (4 1 3) <- For odd faces the new vertex must be at index [0] // No Rot required, link-back CW: AB, CW forward: AB + 1 = BC, // CCW forward: edge in CCW that contains vertices in (CW forward) = AB // The next triangle is the one that shares the CCW edge AB (vertices 1 and 4) // # 1 4 5 (RS) <- Even face the new vertex needs to be in index [2] so need to Right Shift vertices // Repeat steps as for above face but don't need to worry about converting between CCW and CW order } } ModelBase.FaceListDef tStrip = new ModelBase.FaceListDef(ModelBase.PolyListType.TriangleStrip); even = true; for (int i = 0; i < stripIndices.Count; i++) { TriangleRotation requiredRotation = (TriangleRotation)stripRotations[i]; ModelBase.FaceDef rotated = new ModelBase.FaceDef(3); rotated.m_Vertices[0] = m_Triangles[stripIndices[i]].m_Triangle.m_Vertices[((int)(0 + requiredRotation) % 3)]; rotated.m_Vertices[1] = m_Triangles[stripIndices[i]].m_Triangle.m_Vertices[((int)(1 + requiredRotation) % 3)]; rotated.m_Vertices[2] = m_Triangles[stripIndices[i]].m_Triangle.m_Vertices[((int)(2 + requiredRotation) % 3)]; tStrip.m_Faces.Add(rotated); even = !even; } return new Tuple<ModelBase.FaceListDef, List<int>>(tStrip, stripIndices); }
public override ModelBase LoadModel(float scale) { if (m_ModelFileName == null || "".Equals(m_ModelFileName)) { throw new SystemException("You must specify the filename of the model to load via the constructor before " + "calling LoadModel()"); } Stream fs = File.OpenRead(m_ModelFileName); StreamReader sr = new StreamReader(fs); string curmaterial = null; bool foundObjects = LoadDefaultBones(m_ModelFileName); string currentBone = null; int currentBoneIndex = -1; if (!foundObjects) { currentBone = "default_bone_name"; ModelBase.BoneDef defaultBone = new ModelBase.BoneDef(currentBone); defaultBone.m_Geometries.Add("geometry-0", new ModelBase.GeometryDef("geometry-0")); m_Model.m_BoneTree.AddRootBone(defaultBone); currentBoneIndex = m_Model.m_BoneTree.GetBoneIndex(defaultBone); } string curline; while ((curline = sr.ReadLine()) != null) { curline = curline.Trim(); // skip empty lines and comments if (curline.Length < 1) { continue; } if (curline[0] == '#') { continue; } string[] parts = curline.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length < 1) { continue; } switch (parts[0]) { case "mtllib": // material lib file { string filename = curline.Substring(parts[0].Length + 1).Trim(); LoadMaterials(m_ModelPath + Path.DirectorySeparatorChar + filename); } break; case "bonelib": // bone definitions file { string filename = curline.Substring(parts[0].Length + 1).Trim(); LoadBoneDefinitionsForOBJ(m_ModelPath + Path.DirectorySeparatorChar + filename); } break; case "o": // object (bone) if (parts.Length < 2) { continue; } currentBone = parts[1]; m_Model.m_BoneTree.GetBoneByID(currentBone).m_Geometries.Add(currentBone, new ModelBase.GeometryDef(currentBone)); currentBoneIndex = m_Model.m_BoneTree.GetBoneIndex(currentBone); break; case "usemtl": // material name if (parts.Length < 2) { continue; } curmaterial = parts[1]; if (!m_Model.m_Materials.ContainsKey(curmaterial)) { curmaterial = "default_white"; AddWhiteMat(currentBone); } break; case "v": // vertex { if (parts.Length < 4) { continue; } float x = Helper.ParseFloat(parts[1]); float y = Helper.ParseFloat(parts[2]); float z = Helper.ParseFloat(parts[3]); float w = 1f; //(parts.Length < 5) ? 1f : Helper.ParseFloat(parts[4]); m_Vertices.Add(new Vector4(x, y, z, w)); m_VertexBoneIDs.Add(currentBoneIndex); } break; case "vt": // texcoord { if (parts.Length < 2) { continue; } float s = Helper.ParseFloat(parts[1]); float t = (parts.Length < 3) ? 0f : Helper.ParseFloat(parts[2]); m_TexCoords.Add(new Vector2(s, t)); } break; case "vn": // normal { if (parts.Length < 4) { continue; } float x = Helper.ParseFloat(parts[1]); float y = Helper.ParseFloat(parts[2]); float z = Helper.ParseFloat(parts[3]); Vector3 vec = new Vector3(x, y, z).Normalized(); m_Normals.Add(vec); } break; case "vc": // vertex colour (non-standard "Extended OBJ" Blender plugin only) { if (parts.Length < 4) { continue; } float r = Helper.ParseFloat(parts[1]); float g = Helper.ParseFloat(parts[2]); float b = Helper.ParseFloat(parts[3]); Color vcolour = Color.FromArgb((int)(r * 255.0f), (int)(g * 255.0f), (int)(b * 255.0f)); m_Colours.Add(vcolour); } break; case "f": // face { if (parts.Length < 4) { continue; } int nvtx = parts.Length - 1; if (curmaterial != null) { // If a new object is defined but a material to use not set, we need to use the previous one and add // it to the current bone and its parent if (!m_Model.m_BoneTree.GetBoneByID(currentBone).GetRoot().m_MaterialsInBranch.Contains(curmaterial)) { m_Model.m_BoneTree.GetBoneByID(currentBone).GetRoot().m_MaterialsInBranch.Add(curmaterial); } if (!m_Model.m_BoneTree.GetBoneByID(currentBone).m_MaterialsInBranch.Contains(curmaterial)) { m_Model.m_BoneTree.GetBoneByID(currentBone).m_MaterialsInBranch.Add(curmaterial); } } else { // No "usemtl" command before declaring face curmaterial = "default_white"; AddWhiteMat(currentBone); } ModelBase.BoneDef bone = m_Model.m_BoneTree.GetBoneByID(currentBone); ModelBase.GeometryDef geomDef = bone.m_Geometries.Values.ElementAt(0); string polyListKey = "polylist-" + curmaterial; ModelBase.PolyListDef polyList; if (!geomDef.m_PolyLists.TryGetValue(polyListKey, out polyList)) { polyList = new ModelBase.PolyListDef(polyListKey, curmaterial); polyList.m_FaceLists.Add(new ModelBase.FaceListDef()); geomDef.m_PolyLists.Add(polyList.m_ID, polyList); } ModelBase.FaceDef face = new ModelBase.FaceDef(nvtx); for (int i = 0; i < nvtx; i++) { string vtx = parts[i + 1]; string[] idxs = vtx.Split(new char[] { '/' }); ModelBase.VertexDef vert = ModelBase.EMPTY_VERTEX; vert.m_Position = new Vector3(m_Vertices[int.Parse(idxs[0]) - 1].Xyz); if (m_Model.m_Materials[curmaterial].m_TextureDefID != null && idxs.Length >= 2 && idxs[1].Length > 0) { vert.m_TextureCoordinate = m_TexCoords[int.Parse(idxs[1]) - 1]; } else { vert.m_TextureCoordinate = null; } if (m_Model.m_Materials[curmaterial].m_Lights.Contains(true) && idxs.Length >= 3 && idxs[2].Trim().Length > 0) { vert.m_Normal = new Vector3(m_Normals[int.Parse(idxs[2]) - 1]); } else { vert.m_Normal = null; } // Vertex colours (non-standard "Extended OBJ" Blender plugin only) if (idxs.Length >= 4 && !idxs[3].Equals("")) { Color tmp = m_Colours[int.Parse(idxs[3]) - 1]; vert.m_VertexColour = Color.FromArgb(tmp.A, tmp.R, tmp.G, tmp.B); } else { vert.m_VertexColour = Color.White; } vert.m_VertexBoneIndex = currentBoneIndex; face.m_Vertices[i] = vert; } polyList.m_FaceLists[0].m_Faces.Add(face); } break; } } int count = 0; foreach (ModelBase.BoneDef boneDef in m_Model.m_BoneTree) { m_Model.m_BoneTransformsMap.Add(boneDef.m_ID, count); count++; } sr.Close(); m_Model.ScaleModel(scale); return(m_Model); }
protected static void AddVertexToList(int boneID, Vector2? tex, Vector3? nrm, Vector3 pos, int pos_scale, Color clr, List<ModelBase.VertexDef> vertexList) { Vector3 scaledPos = Vector3.Multiply(pos, (1 << pos_scale)); ModelBase.VertexDef vertex = new ModelBase.VertexDef(scaledPos, tex, nrm, clr, boneID); vertexList.Add(vertex); }