/// <summary> ///This function should be called after all the vertices and polygons for each object ///have been written to the file. The function updates the following information: ///The main chunk length ///The 3D editor chunk length ///The object chunk length ///The triangular mesh chunk length ///The vertices chunk length ///The number of vertices ///The polygons chunk length ///The number of polygons ///Texture information /// </summary> public void MakeFinalUpdates() { //Copy the polygon data from the temp file to the main file _polygonDataHandler.CopyPlgnData(ref _writer); //Write the material information for all the objects var faceMaterialChunkLength = MaterialChunkWriter.GetMaterialListChunkLength(_polygonDataHandler.PolygonCount); MaterialChunkWriter.WriteMatInformationForObjFaces(_polygonDataHandler.PolygonCount, faceMaterialChunkLength, _writer); //Copy the texture data (u,v) values from the temp file to the main file //Store the length of the texture chunk in a variable before it is reset in the below call var texCoodChunkLength = 0; if (_texCoordinatesHandler.DoesTextureValueExist()) { texCoodChunkLength = _texCoordinatesHandler.MappingCoodChunkLength(); _texCoordinatesHandler.CopyTexCoodData(ref _writer, _polygonDataHandler.PolygonCount); } //Calculate the length of the vertices list chunk var vertChnkLen = (2 + (VertexCount * 3 * sizeof(float)) + 6); //6 for the usual header length //The first two bytes are for the quantity value written after the vertices header //Calculate the length of the polygons chunk (faces chunk) var polsChnkLen = (6 + 2 + (_polygonDataHandler.PolygonCount * 4 * 2) + faceMaterialChunkLength); //6 for the usual header length //Calculate the length of the triangular mesh chunk var meshChnkLen = 6 + vertChnkLen + polsChnkLen + texCoodChunkLength; //Header + sub-chunks //Calculate the length of the object block chunk var objChnkLen = 6 + ObjNameLen + meshChnkLen; //6 for header var prevLen = _writer.BaseStream.Length - objChnkLen; //Update the object chunk length var offset = (int)(prevLen + 2); //2 because the chunk length is stored after the chunk id _writer.Seek(offset, SeekOrigin.Begin); _writer.Write(objChnkLen); //Object name is stored at this position in the object chunk //Update the triangular mesh chunk length offset += 6 + ObjNameLen; _writer.Seek(offset, SeekOrigin.Begin); _writer.Write(meshChnkLen); //Update the vertices chunk length offset += 6; _writer.Seek(offset, SeekOrigin.Begin); _writer.Write(vertChnkLen); //Update the number of vertices _writer.Write(VertexCount); //Update the polygon chunk length offset += vertChnkLen; _writer.Seek(offset, SeekOrigin.Begin); _writer.Write(polsChnkLen); //Update the number of polygons _writer.Write(_polygonDataHandler.PolygonCount); _polygonDataHandler = null; _texCoordinatesHandler = null; }
public static void ExportTo3DS(string filePath, GeometryModel3D modelData, Image textureFileData) { //sanity checks if (modelData == null) { throw new ArgumentException("Geometry model info not supplied."); } if (!(modelData.Geometry != null && modelData.Geometry is MeshGeometry3D)) { throw new ArgumentException("Passed model is not of type mesh geometry 3d"); } //read specific values from xaml file var modelColor = GetModelColor(modelData); var mesh = ((MeshGeometry3D)modelData.Geometry); var containsTextureInfo = ContainsTextureInfo(mesh); var positionCount = mesh.Positions.Count; var points = mesh.Positions; var textures = mesh.TextureCoordinates; var polygons = mesh.TriangleIndices; //Open the 3DS file for writing var writer = InitializeFile(filePath); //Write Material Info var textureFileName = MakeTextureFileName(filePath); MaterialChunkWriter.WriteMaterialChunk(modelColor, containsTextureInfo, textureFileName, writer); var objectCount = 0; _3DSObjectDataHandler objHandler = null; ushort[] setIndicesForObj = null; bool[] isIndicesSetForObj = null; ushort positionIndexInObj = 0; for (var ctr = 0; ctr < polygons.Count; ctr += 3) { var createNewObject = objHandler == null || objHandler.PolygonCount >= MaxValForUnsignedShort || objHandler.TextureCoodCount >= MaxValForUnsignedShort || objHandler.VertexCount >= MaxValForUnsignedShort; if (createNewObject) { if (objHandler != null) { objHandler.MakeFinalUpdates(); } objectCount++; if (objectCount > 9999) { throw new Exception("Error: Cannot create 3ds file.\n The number of objects into which the model can be broken down exceeds 9999"); } objHandler = new _3DSObjectDataHandler(writer, objectCount); setIndicesForObj = new ushort[positionCount]; isIndicesSetForObj = new bool[positionCount]; positionIndexInObj = 0; } var origIndices = new[] { (ushort)polygons[ctr], (ushort)polygons[ctr + 1], (ushort)polygons[ctr + 2] }; var newIndices = new List <ushort>(); foreach (var origIndex in origIndices) { if (isIndicesSetForObj[origIndex]) { newIndices.Add(setIndicesForObj[origIndex]); } else {//Add the new position //Interchange y and z coordinates and change the sign of y to accomodate //the difference in the axes orientation between the 3ds and xaml format var origPoint = points[origIndex]; var point = new Point3D(origPoint.X, -origPoint.Z, origPoint.Y); objHandler.AddPosition(point); if (textures != null && textures.Count > origIndex) { var texCood = mesh.TextureCoordinates[origIndex]; objHandler.AddTextureIndices((float)texCood.X, (float)texCood.Y); } setIndicesForObj[origIndex] = positionIndexInObj; isIndicesSetForObj[origIndex] = true; newIndices.Add(positionIndexInObj); positionIndexInObj++; } } objHandler.AddPolygonIndices(newIndices[0], newIndices[1], newIndices[2]); } //Make the final updation call on object data handler if (objHandler != null) { objHandler.MakeFinalUpdates(); } //Close the 3ds file (after final updations to the header chunks) UpdateAndCloseFile(writer); //save the texture image data SaveTextureImageData(filePath, textureFileData, textureFileName); }