// Copyright 2017 The Draco Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // 'use_strict'; const fs = require('fs'); const assert = require('assert'); const draco3d = require('draco3d'); // Global decoder and encoder module variables. let decoderModule = null; let encoderModule = null; // The code to create the encoder and decoder modules is asynchronous. // draco3d.createDecoderModule will return a promise to a funciton with a // module as a parameter when the module has been fully initialized. // Create and set the decoder module. draco3d.createDecoderModule({}).then(function(module) { // This is reached when everything is ready, and you can call methods on // Module. decoderModule = module; console.log('Decoder Module Initialized!'); modulesInitialized(); }); // Create and set the encoder module. draco3d.createEncoderModule({}).then(function(module) { // This is reached when everything is ready, and you can call methods on // Module. encoderModule = module; console.log('Encoder Module Initialized!'); modulesInitialized(); }); function modulesInitialized() { // Check if both the encoder and decoder modules have been initialized. if (encoderModule && decoderModule) { fs.readFile('./bunny.drc', function(err, data) { if (err) { return console.log(err); } console.log("Decoding file of size " + data.byteLength + " .."); // Decode mesh const decoder = new decoderModule.Decoder(); const decodedGeometry = decodeDracoData(data, decoder); // Encode mesh encodeMeshToFile(decodedGeometry, decoder); decoderModule.destroy(decoder); decoderModule.destroy(decodedGeometry); }); } } function decodeDracoData(rawBuffer, decoder) { const buffer = new decoderModule.DecoderBuffer(); buffer.Init(new Int8Array(rawBuffer), rawBuffer.byteLength); const geometryType = decoder.GetEncodedGeometryType(buffer); let dracoGeometry; let status; if (geometryType === decoderModule.TRIANGULAR_MESH) { dracoGeometry = new decoderModule.Mesh(); status = decoder.DecodeBufferToMesh(buffer, dracoGeometry); } else if (geometryType === decoderModule.POINT_CLOUD) { dracoGeometry = new decoderModule.PointCloud(); status = decoder.DecodeBufferToPointCloud(buffer, dracoGeometry); } else { const errorMsg = 'Error: Unknown geometry type.'; console.error(errorMsg); } decoderModule.destroy(buffer); return dracoGeometry; } function encodeMeshToFile(mesh, decoder) { const encoder = new encoderModule.Encoder(); const meshBuilder = new encoderModule.MeshBuilder(); // Create a mesh object for storing mesh data. const newMesh = new encoderModule.Mesh(); const numFaces = mesh.num_faces(); const numIndices = numFaces * 3; const numPoints = mesh.num_points(); const indices = new Uint32Array(numIndices); console.log("Number of faces " + numFaces); console.log("Number of vertices " + numPoints); // Add Faces to mesh const ia = new decoderModule.DracoInt32Array(); for (let i = 0; i < numFaces; ++i) { decoder.GetFaceFromMesh(mesh, i, ia); const index = i * 3; indices[index] = ia.GetValue(0); indices[index + 1] = ia.GetValue(1); indices[index + 2] = ia.GetValue(2); } decoderModule.destroy(ia); meshBuilder.AddFacesToMesh(newMesh, numFaces, indices); const attrs = {POSITION: 3, NORMAL: 3, COLOR: 3, TEX_COORD: 2}; Object.keys(attrs).forEach((attr) => { const stride = attrs[attr]; const numValues = numPoints * stride; const decoderAttr = decoderModule[attr]; const encoderAttr = encoderModule[attr]; const attrId = decoder.GetAttributeId(mesh, decoderAttr); if (attrId < 0) { return; } console.log("Adding %s attribute", attr); const attribute = decoder.GetAttribute(mesh, attrId); const attributeData = new decoderModule.DracoFloat32Array(); decoder.GetAttributeFloatForAllPoints(mesh, attribute, attributeData); assert(numValues === attributeData.size(), 'Wrong attribute size.'); const attributeDataArray = new Float32Array(numValues); for (let i = 0; i < numValues; ++i) { attributeDataArray[i] = attributeData.GetValue(i); } decoderModule.destroy(attributeData); meshBuilder.AddFloatAttributeToMesh(newMesh, encoderAttr, numPoints, stride, attributeDataArray); }); let encodedData = new encoderModule.DracoInt8Array(); // Set encoding options. encoder.SetSpeedOptions(5, 5); encoder.SetAttributeQuantization(encoderModule.POSITION, 10); encoder.SetEncodingMethod(encoderModule.MESH_EDGEBREAKER_ENCODING); // Encoding. console.log("Encoding..."); const encodedLen = encoder.EncodeMeshToDracoBuffer(newMesh, encodedData); encoderModule.destroy(newMesh); if (encodedLen > 0) { console.log("Encoded size is " + encodedLen); } else { console.log("Error: Encoding failed."); } // Copy encoded data to buffer. const outputBuffer = new ArrayBuffer(encodedLen); const outputData = new Int8Array(outputBuffer); for (let i = 0; i < encodedLen; ++i) { outputData[i] = encodedData.GetValue(i); } encoderModule.destroy(encodedData); encoderModule.destroy(encoder); encoderModule.destroy(meshBuilder); // Write to file. You can view the the file using webgl_loader_draco.html // example. fs.writeFile("bunny_10.drc", Buffer.from(outputBuffer), "binary", function(err) { if (err) { console.log(err); } else { console.log("The file was saved!"); } }); }