public void QuaternionVersusMatrixMovingTests() { XbimVector3D t = new XbimVector3D(2, 0, 0); XbimMatrix3D T = new XbimMatrix3D(t); T.RotateAroundZAxis(Math.PI / 2); XbimQuaternion q; XbimQuaternion.RotationMatrix(ref T, out q); // Do the quaternion approach XbimVector3D v = new XbimVector3D(1, 1, 0); XbimVector3D v1; XbimQuaternion.Transform(ref v, ref q, out v1); // Rotate and translate var p1Result = v1 + t; // Do the same with matrix approach XbimPoint3D p = new XbimPoint3D(1, 1, 0); var p2Result = T.Transform(p); Assert.AreEqual(p1Result, XbimPoint3D.Subtract(p2Result, XbimPoint3D.Zero)); // Test quaternion and delta translation (relocating) XbimVector3D mt = new XbimVector3D(2, 2, 0); var dt = t - mt; var pMoved = v1 + dt; var p3Result = pMoved + mt; Assert.AreEqual(p1Result, p3Result); }
internal void AddMesh(byte[] mesh, XbimMatrix3D?transform = null) { int indexBase = Positions.Count; bool needRotate = false; bool needTransform = false; XbimQuaternion xq = new XbimQuaternion(0.0, 0.0, 0.0, 1.0); XbimMatrix3D transformValue = XbimMatrix3D.Identity; if (transform.HasValue) { transformValue = transform.Value; needTransform = !transformValue.IsIdentity; xq = transformValue.GetRotationQuaternion(); // we have to build a rotation transform from the quaternion (to tranform normals later on) needRotate = !xq.IsIdentity(); } using (var ms = new MemoryStream(mesh)) using (var br = new BinaryReader(ms)) { var t = br.ReadShapeTriangulation(); List <float[]> pts; List <int> idx; t.ToPointsWithNormalsAndIndices(out pts, out idx); // add to lists // // Commented because of https://github.com/xBimTeam/XbimGltf/issues/2 //Positions.Capacity += pts.Count; //Normals.Capacity += pts.Count; //Indices.Capacity += idx.Count; foreach (var floatsArray in pts) { var tmpPosition = new XbimPoint3D(floatsArray[0], floatsArray[1], floatsArray[2]); if (needTransform) { tmpPosition = transformValue.Transform(tmpPosition); } Positions.Add(tmpPosition); var tmpNormal = new XbimVector3D(floatsArray[3], floatsArray[4], floatsArray[5]); if (needRotate) //transform the normal if we have to { XbimQuaternion.Transform(ref tmpNormal, ref xq, out tmpNormal); } Normals.Add(tmpNormal); } foreach (var index in idx) { Indices.Add(index + indexBase); } } }
/// <summary> /// Reads an ascii string of Xbim mesh geometry data /// </summary> /// <param name="data"></param> /// <returns></returns> public bool Read(String data, XbimMatrix3D?trans = null) { var version = 2; //we are at at least verson 2 now var q = new XbimQuaternion(); if (trans.HasValue) { q = trans.Value.GetRotationQuaternion(); } using (var sr = new StringReader(data)) { var vertexList = new List <XbimPoint3D>(); //holds the actual positions of the vertices in this data set in the mesh var normalList = new List <XbimVector3D>(); //holds the actual normals of the vertices in this data set in the mesh String line; // Read and display lines from the data until the end of // the data is reached. while ((line = sr.ReadLine()) != null) { var tokens = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (tokens.Length > 1) //we need a command and some data { var command = tokens[0].Trim().ToUpper(); switch (command) { case "P": version = Int32.Parse(tokens[1]); var pointCount = 512; //var faceCount = 128; //var triangleCount = 256; var normalCount = 512; if (tokens.Length > 1) { pointCount = Int32.Parse(tokens[2]); } // if (tokens.Length > 2) faceCount = Int32.Parse(tokens[3]); // if (tokens.Length > 3) triangleCount = Int32.Parse(tokens[4]); //version 2 of the string format uses packed normals if (version < 2 && tokens.Length > 4) { normalCount = Int32.Parse(tokens[5]); } vertexList = new List <XbimPoint3D>(pointCount); normalList = new List <XbimVector3D>(normalCount); break; case "V": //process vertices for (var i = 1; i < tokens.Length; i++) { var xyz = tokens[i].Split(','); var p = new XbimPoint3D(Convert.ToDouble(xyz[0], CultureInfo.InvariantCulture), Convert.ToDouble(xyz[1], CultureInfo.InvariantCulture), Convert.ToDouble(xyz[2], CultureInfo.InvariantCulture)); if (trans.HasValue) { p = trans.Value.Transform(p); } vertexList.Add(p); } break; case "N": //processes normals for (var i = 1; i < tokens.Length; i++) { var xyz = tokens[i].Split(','); var v = new XbimVector3D(Convert.ToDouble(xyz[0], CultureInfo.InvariantCulture), Convert.ToDouble(xyz[1], CultureInfo.InvariantCulture), Convert.ToDouble(xyz[2], CultureInfo.InvariantCulture)); normalList.Add(v); } break; case "T": //process triangulated meshes var currentNormal = XbimVector3D.Zero; //each time we start a new mesh face we have to duplicate the vertices to ensure that we get correct shading of planar and non planar faces var writtenVertices = new Dictionary <int, int>(); for (var i = 1; i < tokens.Length; i++) { var triangleIndices = tokens[i].Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (triangleIndices.Length != 3) { throw new Exception("Invalid triangle definition"); } for (var t = 0; t < 3; t++) { var indexNormalPair = triangleIndices[t].Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); if (indexNormalPair.Length > 1) //we have a normal defined { var normalStr = indexNormalPair[1].Trim(); if (version < 2) { switch (normalStr) { case "F": //Front currentNormal = new XbimVector3D(0, -1, 0); break; case "B": //Back currentNormal = new XbimVector3D(0, 1, 0); break; case "L": //Left currentNormal = new XbimVector3D(-1, 0, 0); break; case "R": //Right currentNormal = new XbimVector3D(1, 0, 0); break; case "U": //Up currentNormal = new XbimVector3D(0, 0, 1); break; case "D": //Down currentNormal = new XbimVector3D(0, 0, -1); break; default: //it is an index number var normalIndex = int.Parse(indexNormalPair[1]); currentNormal = normalList[normalIndex]; break; } } else { var normalIndex = ushort.Parse(indexNormalPair[1]); var packedNormal = new XbimPackedNormal(normalIndex); currentNormal = packedNormal.Normal; } if (trans.HasValue) { XbimVector3D v; XbimQuaternion.Transform(ref currentNormal, ref q, out v); currentNormal = v; } } //now add the index var index = int.Parse(indexNormalPair[0]); int alreadyWrittenAt; if (!writtenVertices.TryGetValue(index, out alreadyWrittenAt)) //if we haven't written it in this mesh pass, add it again unless it is the first one which we know has been written { //all vertices will be unique and have only one normal writtenVertices.Add(index, PositionCount); TriangleIndices.Add(PositionCount); Positions.Add(vertexList[index]); Normals.Add(currentNormal); } else //just add the index reference { TriangleIndices.Add(alreadyWrittenAt); } } } break; case "F": break; default: throw new Exception("Invalid Geometry Command"); } } } } return(true); }
public static void Read(this XbimMeshGeometry3D m3D, byte[] mesh, XbimMatrix3D?transform = null) { var indexBase = m3D.Positions.Count; var qrd = new XbimQuaternion(); XbimMatrix3D?matrix3D = null; if (transform.HasValue) { qrd = transform.Value.GetRotationQuaternion(); matrix3D = transform.Value; } using (var ms = new MemoryStream(mesh)) { using (var br = new BinaryReader(ms)) { // ReSharper disable once UnusedVariable var version = br.ReadByte(); //stream format version var numVertices = br.ReadInt32(); var numTriangles = br.ReadInt32(); var uniqueVertices = new List <XbimPoint3D>(numVertices); var vertices = new List <XbimPoint3D>(numVertices * 4); //approx the size var triangleIndices = new List <int>(numTriangles * 3); var normals = new List <XbimVector3D>(numVertices * 4); for (var i = 0; i < numVertices; i++) { double x = br.ReadSingle(); double y = br.ReadSingle(); double z = br.ReadSingle(); var p = new XbimPoint3D(x, y, z); if (matrix3D.HasValue) { p = matrix3D.Value.Transform(p); } uniqueVertices.Add(p); } var numFaces = br.ReadInt32(); for (var i = 0; i < numFaces; i++) { var numTrianglesInFace = br.ReadInt32(); if (numTrianglesInFace == 0) { continue; } var isPlanar = numTrianglesInFace > 0; numTrianglesInFace = Math.Abs(numTrianglesInFace); if (isPlanar) { var normal = br.ReadPackedNormal().Normal; if (!qrd.IsIdentity()) { var baseVal = new XbimVector3D(normal.X, normal.Y, normal.Z); XbimQuaternion.Transform(ref baseVal, ref qrd, out normal); } var uniqueIndices = new Dictionary <int, int>(); for (var j = 0; j < numTrianglesInFace; j++) { for (var k = 0; k < 3; k++) { var idx = ReadIndex(br, numVertices); int writtenIdx; if (!uniqueIndices.TryGetValue(idx, out writtenIdx)) //we haven't got it, so add it { writtenIdx = vertices.Count; vertices.Add(uniqueVertices[idx]); uniqueIndices.Add(idx, writtenIdx); //add a matching normal normals.Add(normal); } triangleIndices.Add(indexBase + writtenIdx); } } } else { var uniqueIndices = new Dictionary <int, int>(); for (var j = 0; j < numTrianglesInFace; j++) { for (var k = 0; k < 3; k++) { var idx = ReadIndex(br, numVertices); var normal = br.ReadPackedNormal().Normal; int writtenIdx; if (!uniqueIndices.TryGetValue(idx, out writtenIdx)) //we haven't got it, so add it { writtenIdx = vertices.Count; vertices.Add(uniqueVertices[idx]); uniqueIndices.Add(idx, writtenIdx); if (!qrd.IsIdentity()) { var baseVal = new XbimVector3D(normal.X, normal.Y, normal.Z); XbimQuaternion.Transform(ref baseVal, ref qrd, out normal); } normals.Add(normal); } triangleIndices.Add(indexBase + writtenIdx); } } } } m3D.Positions = m3D.Positions.Concat(vertices).ToList(); m3D.TriangleIndices = m3D.TriangleIndices.Concat(triangleIndices).ToList(); m3D.Normals = m3D.Normals.Concat(normals).ToList(); } } }