Draco 1.5.7 release.

This commit is contained in:
Ondrej Stava 2023-12-12 11:53:21 -08:00
parent 9f856abaaf
commit 812dba9b91
127 changed files with 5018 additions and 1392 deletions

View File

@ -13,6 +13,9 @@
# the License.
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
if(DRACO_TRANSCODER_SUPPORTED)
set(CMAKE_CXX_STANDARD 17)
endif()
project(draco C CXX)
if(NOT CMAKE_BUILD_TYPE)
@ -479,10 +482,14 @@ list(
"${draco_src_root}/metadata/geometry_metadata.h"
"${draco_src_root}/metadata/metadata.cc"
"${draco_src_root}/metadata/metadata.h"
"${draco_src_root}/metadata/property_attribute.cc"
"${draco_src_root}/metadata/property_attribute.h"
"${draco_src_root}/metadata/property_table.cc"
"${draco_src_root}/metadata/property_table.h"
"${draco_src_root}/metadata/structural_metadata.cc"
"${draco_src_root}/metadata/structural_metadata.h")
"${draco_src_root}/metadata/structural_metadata.h"
"${draco_src_root}/metadata/structural_metadata_schema.cc"
"${draco_src_root}/metadata/structural_metadata_schema.h")
list(APPEND draco_metadata_enc_sources
"${draco_src_root}/metadata/metadata_encoder.cc"

View File

@ -14,6 +14,14 @@ delays can result in transient errors that can be difficult to diagnose when
new Draco releases are launched. To avoid the issue pin your sites to a
versioned release.
### Version 1.5.7 release:
* Using the versioned www.gstatic.com WASM and Javascript decoders continues
to be recommended. To use v1.5.7, use this URL:
* https://www.gstatic.com/draco/versioned/decoders/1.5.7/*
* Added support for normalized attributes to Emscripten encoder API.
* Bug fixes.
* Security fixes.
### Version 1.5.6 release:
* Using the versioned www.gstatic.com WASM and Javascript decoders continues
to be recommended. To use v1.5.6, use this URL:

View File

@ -56,7 +56,7 @@ macro(draco_set_build_definitions)
# passed to libtool.
#
# We set DRACO_SOVERSION = [c-a].a.r
set(LT_CURRENT 8)
set(LT_CURRENT 9)
set(LT_REVISION 0)
set(LT_AGE 0)
math(EXPR DRACO_SOVERSION_MAJOR "${LT_CURRENT} - ${LT_AGE}")

View File

@ -89,8 +89,10 @@ if(DRACO_TRANSCODER_SUPPORTED)
"${draco_src_root}/io/texture_io_test.cc"
"${draco_src_root}/material/material_library_test.cc"
"${draco_src_root}/material/material_test.cc"
"${draco_src_root}/metadata/property_attribute_test.cc"
"${draco_src_root}/metadata/property_table_test.cc"
"${draco_src_root}/metadata/structural_metadata_test.cc"
"${draco_src_root}/metadata/structural_metadata_schema_test.cc"
"${draco_src_root}/scene/instance_array_test.cc"
"${draco_src_root}/scene/light_test.cc"
"${draco_src_root}/scene/mesh_group_test.cc"

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -32,9 +32,9 @@ D(z)[this.ptr]=this}function G(){this.ptr=wa();D(G)[this.ptr]=this}function E(){
typeof window,Y="function"==typeof importScripts,Ia="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,L="";if(Ia){var Ja=require("fs"),la=require("path");L=Y?la.dirname(L)+"/":__dirname+"/";var Ka=function(f,b){f=f.startsWith("file://")?new URL(f):la.normalize(f);return Ja.readFileSync(f,b?void 0:"utf8")};var ia=function(f){f=Ka(f,!0);f.buffer||(f=new Uint8Array(f));return f};var ja=function(f,b,c){f=f.startsWith("file://")?new URL(f):la.normalize(f);
Ja.readFile(f,function(d,e){d?c(d):b(e.buffer)})};1<process.argv.length&&process.argv[1].replace(/\\/g,"/");process.argv.slice(2);a.inspect=function(){return"[Emscripten Module object]"}}else if(oa||Y)Y?L=self.location.href:"undefined"!=typeof document&&document.currentScript&&(L=document.currentScript.src),l&&(L=l),L=0!==L.indexOf("blob:")?L.substr(0,L.replace(/[?#].*/,"").lastIndexOf("/")+1):"",Ka=function(f){var b=new XMLHttpRequest;b.open("GET",f,!1);b.send(null);return b.responseText},Y&&(ia=
function(f){var b=new XMLHttpRequest;b.open("GET",f,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),ja=function(f,b,c){var d=new XMLHttpRequest;d.open("GET",f,!0);d.responseType="arraybuffer";d.onload=function(){200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};a.print||console.log.bind(console);var W=a.printErr||console.warn.bind(console);Object.assign(a,Ha);Ha=null;var X;a.wasmBinary&&(X=a.wasmBinary);"object"!=typeof WebAssembly&&
k("no native wasm support detected");var ba,na=!1,O,fa,ea,S,T,ha,ta=[],ka=[],sa=[],qa=!1,U=0,ma=null,aa=null;var K="draco_encoder.wasm";K.startsWith("data:application/octet-stream;base64,")||(K=m(K));var nc=0,oc={b:function(f,b,c){(new w(f)).init(b,c);nc++;throw f;},a:function(){k("")},d:function(f,b,c){fa.copyWithin(f,b,b+c)},c:function(f){var b=fa.length;f>>>=0;if(2147483648<f)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,f+100663296);var e=Math;d=Math.max(f,d);e=e.min.call(e,2147483648,
k("no native wasm support detected");var ba,na=!1,O,fa,ea,S,T,ha,ta=[],ka=[],sa=[],qa=!1,U=0,ma=null,aa=null;var K="draco_encoder.wasm";K.startsWith("data:application/octet-stream;base64,")||(K=m(K));var pc=0,qc={b:function(f,b,c){(new w(f)).init(b,c);pc++;throw f;},a:function(){k("")},d:function(f,b,c){fa.copyWithin(f,b,b+c)},c:function(f){var b=fa.length;f>>>=0;if(2147483648<f)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,f+100663296);var e=Math;d=Math.max(f,d);e=e.min.call(e,2147483648,
d+(65536-d%65536)%65536);a:{d=ba.buffer;try{ba.grow(e-d.byteLength+65535>>>16);r();var g=1;break a}catch(t){}g=void 0}if(g)return!0}return!1}};(function(){function f(e,g){a.asm=e.exports;ba=a.asm.e;r();ka.unshift(a.asm.f);U--;a.monitorRunDependencies&&a.monitorRunDependencies(U);0==U&&(null!==ma&&(clearInterval(ma),ma=null),aa&&(e=aa,aa=null,e()))}function b(e){f(e.instance)}function c(e){return h().then(function(g){return WebAssembly.instantiate(g,d)}).then(function(g){return g}).then(e,function(g){W("failed to asynchronously prepare wasm: "+
g);k(g)})}var d={a:oc};U++;a.monitorRunDependencies&&a.monitorRunDependencies(U);if(a.instantiateWasm)try{return a.instantiateWasm(d,f)}catch(e){W("Module.instantiateWasm callback failed with error: "+e),ca(e)}(function(){return X||"function"!=typeof WebAssembly.instantiateStreaming||K.startsWith("data:application/octet-stream;base64,")||K.startsWith("file://")||Ia||"function"!=typeof fetch?c(b):fetch(K,{credentials:"same-origin"}).then(function(e){return WebAssembly.instantiateStreaming(e,d).then(b,
g);k(g)})}var d={a:qc};U++;a.monitorRunDependencies&&a.monitorRunDependencies(U);if(a.instantiateWasm)try{return a.instantiateWasm(d,f)}catch(e){W("Module.instantiateWasm callback failed with error: "+e),ca(e)}(function(){return X||"function"!=typeof WebAssembly.instantiateStreaming||K.startsWith("data:application/octet-stream;base64,")||K.startsWith("file://")||Ia||"function"!=typeof fetch?c(b):fetch(K,{credentials:"same-origin"}).then(function(e){return WebAssembly.instantiateStreaming(e,d).then(b,
function(g){W("wasm streaming compile failed: "+g);W("falling back to ArrayBuffer instantiation");return c(b)})})})().catch(ca);return{}})();var La=a._emscripten_bind_VoidPtr___destroy___0=function(){return(La=a._emscripten_bind_VoidPtr___destroy___0=a.asm.h).apply(null,arguments)},ua=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(ua=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=a.asm.i).apply(null,arguments)},Ma=a._emscripten_bind_GeometryAttribute___destroy___0=
function(){return(Ma=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.j).apply(null,arguments)},va=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(va=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.k).apply(null,arguments)},Na=a._emscripten_bind_PointAttribute_size_0=function(){return(Na=a._emscripten_bind_PointAttribute_size_0=a.asm.l).apply(null,arguments)},Oa=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(Oa=a._emscripten_bind_PointAttribute_attribute_type_0=
a.asm.m).apply(null,arguments)},Pa=a._emscripten_bind_PointAttribute_data_type_0=function(){return(Pa=a._emscripten_bind_PointAttribute_data_type_0=a.asm.n).apply(null,arguments)},Qa=a._emscripten_bind_PointAttribute_num_components_0=function(){return(Qa=a._emscripten_bind_PointAttribute_num_components_0=a.asm.o).apply(null,arguments)},Ra=a._emscripten_bind_PointAttribute_normalized_0=function(){return(Ra=a._emscripten_bind_PointAttribute_normalized_0=a.asm.p).apply(null,arguments)},Sa=a._emscripten_bind_PointAttribute_byte_stride_0=
@ -48,50 +48,51 @@ ib=a._emscripten_bind_MetadataBuilder_AddIntEntry_3=function(){return(ib=a._emsc
function(){return(lb=a._emscripten_bind_MetadataBuilder___destroy___0=a.asm.P).apply(null,arguments)},Ba=a._emscripten_bind_PointCloudBuilder_PointCloudBuilder_0=function(){return(Ba=a._emscripten_bind_PointCloudBuilder_PointCloudBuilder_0=a.asm.Q).apply(null,arguments)},mb=a._emscripten_bind_PointCloudBuilder_AddFloatAttribute_5=function(){return(mb=a._emscripten_bind_PointCloudBuilder_AddFloatAttribute_5=a.asm.R).apply(null,arguments)},nb=a._emscripten_bind_PointCloudBuilder_AddInt8Attribute_5=
function(){return(nb=a._emscripten_bind_PointCloudBuilder_AddInt8Attribute_5=a.asm.S).apply(null,arguments)},ob=a._emscripten_bind_PointCloudBuilder_AddUInt8Attribute_5=function(){return(ob=a._emscripten_bind_PointCloudBuilder_AddUInt8Attribute_5=a.asm.T).apply(null,arguments)},pb=a._emscripten_bind_PointCloudBuilder_AddInt16Attribute_5=function(){return(pb=a._emscripten_bind_PointCloudBuilder_AddInt16Attribute_5=a.asm.U).apply(null,arguments)},qb=a._emscripten_bind_PointCloudBuilder_AddUInt16Attribute_5=
function(){return(qb=a._emscripten_bind_PointCloudBuilder_AddUInt16Attribute_5=a.asm.V).apply(null,arguments)},rb=a._emscripten_bind_PointCloudBuilder_AddInt32Attribute_5=function(){return(rb=a._emscripten_bind_PointCloudBuilder_AddInt32Attribute_5=a.asm.W).apply(null,arguments)},sb=a._emscripten_bind_PointCloudBuilder_AddUInt32Attribute_5=function(){return(sb=a._emscripten_bind_PointCloudBuilder_AddUInt32Attribute_5=a.asm.X).apply(null,arguments)},tb=a._emscripten_bind_PointCloudBuilder_AddMetadata_2=
function(){return(tb=a._emscripten_bind_PointCloudBuilder_AddMetadata_2=a.asm.Y).apply(null,arguments)},ub=a._emscripten_bind_PointCloudBuilder_SetMetadataForAttribute_3=function(){return(ub=a._emscripten_bind_PointCloudBuilder_SetMetadataForAttribute_3=a.asm.Z).apply(null,arguments)},vb=a._emscripten_bind_PointCloudBuilder___destroy___0=function(){return(vb=a._emscripten_bind_PointCloudBuilder___destroy___0=a.asm._).apply(null,arguments)},Ca=a._emscripten_bind_MeshBuilder_MeshBuilder_0=function(){return(Ca=
a._emscripten_bind_MeshBuilder_MeshBuilder_0=a.asm.$).apply(null,arguments)},wb=a._emscripten_bind_MeshBuilder_AddFacesToMesh_3=function(){return(wb=a._emscripten_bind_MeshBuilder_AddFacesToMesh_3=a.asm.aa).apply(null,arguments)},xb=a._emscripten_bind_MeshBuilder_AddFloatAttributeToMesh_5=function(){return(xb=a._emscripten_bind_MeshBuilder_AddFloatAttributeToMesh_5=a.asm.ba).apply(null,arguments)},yb=a._emscripten_bind_MeshBuilder_AddInt32AttributeToMesh_5=function(){return(yb=a._emscripten_bind_MeshBuilder_AddInt32AttributeToMesh_5=
a.asm.ca).apply(null,arguments)},zb=a._emscripten_bind_MeshBuilder_AddMetadataToMesh_2=function(){return(zb=a._emscripten_bind_MeshBuilder_AddMetadataToMesh_2=a.asm.da).apply(null,arguments)},Ab=a._emscripten_bind_MeshBuilder_AddFloatAttribute_5=function(){return(Ab=a._emscripten_bind_MeshBuilder_AddFloatAttribute_5=a.asm.ea).apply(null,arguments)},Bb=a._emscripten_bind_MeshBuilder_AddInt8Attribute_5=function(){return(Bb=a._emscripten_bind_MeshBuilder_AddInt8Attribute_5=a.asm.fa).apply(null,arguments)},
Cb=a._emscripten_bind_MeshBuilder_AddUInt8Attribute_5=function(){return(Cb=a._emscripten_bind_MeshBuilder_AddUInt8Attribute_5=a.asm.ga).apply(null,arguments)},Db=a._emscripten_bind_MeshBuilder_AddInt16Attribute_5=function(){return(Db=a._emscripten_bind_MeshBuilder_AddInt16Attribute_5=a.asm.ha).apply(null,arguments)},Eb=a._emscripten_bind_MeshBuilder_AddUInt16Attribute_5=function(){return(Eb=a._emscripten_bind_MeshBuilder_AddUInt16Attribute_5=a.asm.ia).apply(null,arguments)},Fb=a._emscripten_bind_MeshBuilder_AddInt32Attribute_5=
function(){return(Fb=a._emscripten_bind_MeshBuilder_AddInt32Attribute_5=a.asm.ja).apply(null,arguments)},Gb=a._emscripten_bind_MeshBuilder_AddUInt32Attribute_5=function(){return(Gb=a._emscripten_bind_MeshBuilder_AddUInt32Attribute_5=a.asm.ka).apply(null,arguments)},Hb=a._emscripten_bind_MeshBuilder_AddMetadata_2=function(){return(Hb=a._emscripten_bind_MeshBuilder_AddMetadata_2=a.asm.la).apply(null,arguments)},Ib=a._emscripten_bind_MeshBuilder_SetMetadataForAttribute_3=function(){return(Ib=a._emscripten_bind_MeshBuilder_SetMetadataForAttribute_3=
a.asm.ma).apply(null,arguments)},Jb=a._emscripten_bind_MeshBuilder___destroy___0=function(){return(Jb=a._emscripten_bind_MeshBuilder___destroy___0=a.asm.na).apply(null,arguments)},Da=a._emscripten_bind_Encoder_Encoder_0=function(){return(Da=a._emscripten_bind_Encoder_Encoder_0=a.asm.oa).apply(null,arguments)},Kb=a._emscripten_bind_Encoder_SetEncodingMethod_1=function(){return(Kb=a._emscripten_bind_Encoder_SetEncodingMethod_1=a.asm.pa).apply(null,arguments)},Lb=a._emscripten_bind_Encoder_SetAttributeQuantization_2=
function(){return(Lb=a._emscripten_bind_Encoder_SetAttributeQuantization_2=a.asm.qa).apply(null,arguments)},Mb=a._emscripten_bind_Encoder_SetAttributeExplicitQuantization_5=function(){return(Mb=a._emscripten_bind_Encoder_SetAttributeExplicitQuantization_5=a.asm.ra).apply(null,arguments)},Nb=a._emscripten_bind_Encoder_SetSpeedOptions_2=function(){return(Nb=a._emscripten_bind_Encoder_SetSpeedOptions_2=a.asm.sa).apply(null,arguments)},Ob=a._emscripten_bind_Encoder_SetTrackEncodedProperties_1=function(){return(Ob=
a._emscripten_bind_Encoder_SetTrackEncodedProperties_1=a.asm.ta).apply(null,arguments)},Pb=a._emscripten_bind_Encoder_EncodeMeshToDracoBuffer_2=function(){return(Pb=a._emscripten_bind_Encoder_EncodeMeshToDracoBuffer_2=a.asm.ua).apply(null,arguments)},Qb=a._emscripten_bind_Encoder_EncodePointCloudToDracoBuffer_3=function(){return(Qb=a._emscripten_bind_Encoder_EncodePointCloudToDracoBuffer_3=a.asm.va).apply(null,arguments)},Rb=a._emscripten_bind_Encoder_GetNumberOfEncodedPoints_0=function(){return(Rb=
a._emscripten_bind_Encoder_GetNumberOfEncodedPoints_0=a.asm.wa).apply(null,arguments)},Sb=a._emscripten_bind_Encoder_GetNumberOfEncodedFaces_0=function(){return(Sb=a._emscripten_bind_Encoder_GetNumberOfEncodedFaces_0=a.asm.xa).apply(null,arguments)},Tb=a._emscripten_bind_Encoder___destroy___0=function(){return(Tb=a._emscripten_bind_Encoder___destroy___0=a.asm.ya).apply(null,arguments)},Ea=a._emscripten_bind_ExpertEncoder_ExpertEncoder_1=function(){return(Ea=a._emscripten_bind_ExpertEncoder_ExpertEncoder_1=
a.asm.za).apply(null,arguments)},Ub=a._emscripten_bind_ExpertEncoder_SetEncodingMethod_1=function(){return(Ub=a._emscripten_bind_ExpertEncoder_SetEncodingMethod_1=a.asm.Aa).apply(null,arguments)},Vb=a._emscripten_bind_ExpertEncoder_SetAttributeQuantization_2=function(){return(Vb=a._emscripten_bind_ExpertEncoder_SetAttributeQuantization_2=a.asm.Ba).apply(null,arguments)},Wb=a._emscripten_bind_ExpertEncoder_SetAttributeExplicitQuantization_5=function(){return(Wb=a._emscripten_bind_ExpertEncoder_SetAttributeExplicitQuantization_5=
a.asm.Ca).apply(null,arguments)},Xb=a._emscripten_bind_ExpertEncoder_SetSpeedOptions_2=function(){return(Xb=a._emscripten_bind_ExpertEncoder_SetSpeedOptions_2=a.asm.Da).apply(null,arguments)},Yb=a._emscripten_bind_ExpertEncoder_SetTrackEncodedProperties_1=function(){return(Yb=a._emscripten_bind_ExpertEncoder_SetTrackEncodedProperties_1=a.asm.Ea).apply(null,arguments)},Zb=a._emscripten_bind_ExpertEncoder_EncodeToDracoBuffer_2=function(){return(Zb=a._emscripten_bind_ExpertEncoder_EncodeToDracoBuffer_2=
a.asm.Fa).apply(null,arguments)},$b=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedPoints_0=function(){return($b=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedPoints_0=a.asm.Ga).apply(null,arguments)},ac=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedFaces_0=function(){return(ac=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedFaces_0=a.asm.Ha).apply(null,arguments)},bc=a._emscripten_bind_ExpertEncoder___destroy___0=function(){return(bc=a._emscripten_bind_ExpertEncoder___destroy___0=
a.asm.Ia).apply(null,arguments)},cc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(cc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.Ja).apply(null,arguments)},dc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(dc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=a.asm.Ka).apply(null,arguments)},ec=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(ec=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=
a.asm.La).apply(null,arguments)},fc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(fc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.Ma).apply(null,arguments)},gc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(gc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=a.asm.Na).apply(null,arguments)},hc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(hc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=
a.asm.Oa).apply(null,arguments)},ic=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(ic=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.Pa).apply(null,arguments)},jc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(jc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=a.asm.Qa).apply(null,arguments)},kc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(kc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=
a.asm.Ra).apply(null,arguments)},lc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_SEQUENTIAL_ENCODING=function(){return(lc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_SEQUENTIAL_ENCODING=a.asm.Sa).apply(null,arguments)},mc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_EDGEBREAKER_ENCODING=function(){return(mc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_EDGEBREAKER_ENCODING=a.asm.Ta).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Ua).apply(null,arguments)};a._free=function(){return(a._free=
a.asm.Va).apply(null,arguments)};var pa=function(){return(pa=a.asm.Wa).apply(null,arguments)};a.___start_em_js=19116;a.___stop_em_js=19214;var da;aa=function b(){da||B();da||(aa=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();B();v.prototype=Object.create(v.prototype);v.prototype.constructor=v;v.prototype.__class__=v;v.__cache__={};a.WrapperObject=v;a.getCache=D;a.wrapPointer=I;a.castObject=function(b,c){return I(b.ptr,c)};a.NULL=I(0);
a.destroy=function(b){if(!b.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";b.__destroy__();delete D(b.__class__)[b.ptr]};a.compare=function(b,c){return b.ptr===c.ptr};a.getPointer=function(b){return b.ptr};a.getClass=function(b){return b.__class__};var n={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(n.needed){for(var b=0;b<n.temps.length;b++)a._free(n.temps[b]);n.temps.length=0;a._free(n.buffer);n.buffer=0;n.size+=n.needed;n.needed=0}n.buffer||(n.size+=
128,n.buffer=a._malloc(n.size),n.buffer||k(void 0));n.pos=0},alloc:function(b,c){n.buffer||k(void 0);b=b.length*c.BYTES_PER_ELEMENT;b=b+7&-8;n.pos+b>=n.size?(0<b||k(void 0),n.needed+=b,c=a._malloc(b),n.temps.push(c)):(c=n.buffer+n.pos,n.pos+=b);return c},copy:function(b,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var e=0;e<b.length;e++)c[d+e]=b[e]}};P.prototype=Object.create(v.prototype);P.prototype.constructor=P;P.prototype.__class__=P;P.__cache__=
{};a.VoidPtr=P;P.prototype.__destroy__=P.prototype.__destroy__=function(){La(this.ptr)};M.prototype=Object.create(v.prototype);M.prototype.constructor=M;M.prototype.__class__=M;M.__cache__={};a.GeometryAttribute=M;M.prototype.__destroy__=M.prototype.__destroy__=function(){Ma(this.ptr)};z.prototype=Object.create(v.prototype);z.prototype.constructor=z;z.prototype.__class__=z;z.__cache__={};a.PointAttribute=z;z.prototype.size=z.prototype.size=function(){return Na(this.ptr)};z.prototype.attribute_type=
z.prototype.attribute_type=function(){return Oa(this.ptr)};z.prototype.data_type=z.prototype.data_type=function(){return Pa(this.ptr)};z.prototype.num_components=z.prototype.num_components=function(){return Qa(this.ptr)};z.prototype.normalized=z.prototype.normalized=function(){return!!Ra(this.ptr)};z.prototype.byte_stride=z.prototype.byte_stride=function(){return Sa(this.ptr)};z.prototype.byte_offset=z.prototype.byte_offset=function(){return Ta(this.ptr)};z.prototype.unique_id=z.prototype.unique_id=
function(){return Ua(this.ptr)};z.prototype.__destroy__=z.prototype.__destroy__=function(){Va(this.ptr)};G.prototype=Object.create(v.prototype);G.prototype.constructor=G;G.prototype.__class__=G;G.__cache__={};a.PointCloud=G;G.prototype.num_attributes=G.prototype.num_attributes=function(){return Wa(this.ptr)};G.prototype.num_points=G.prototype.num_points=function(){return Xa(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){Ya(this.ptr)};E.prototype=Object.create(v.prototype);E.prototype.constructor=
E;E.prototype.__class__=E;E.__cache__={};a.Mesh=E;E.prototype.num_faces=E.prototype.num_faces=function(){return Za(this.ptr)};E.prototype.num_attributes=E.prototype.num_attributes=function(){return $a(this.ptr)};E.prototype.num_points=E.prototype.num_points=function(){return ab(this.ptr)};E.prototype.set_num_points=E.prototype.set_num_points=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);bb(c,b)};E.prototype.__destroy__=E.prototype.__destroy__=function(){cb(this.ptr)};N.prototype=Object.create(v.prototype);
N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.Metadata=N;N.prototype.__destroy__=N.prototype.__destroy__=function(){db(this.ptr)};H.prototype=Object.create(v.prototype);H.prototype.constructor=H;H.prototype.__class__=H;H.__cache__={};a.DracoInt8Array=H;H.prototype.GetValue=H.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return eb(c,b)};H.prototype.size=H.prototype.size=function(){return fb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=
function(){gb(this.ptr)};F.prototype=Object.create(v.prototype);F.prototype.constructor=F;F.prototype.__class__=F;F.__cache__={};a.MetadataBuilder=F;F.prototype.AddStringEntry=F.prototype.AddStringEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);d=d&&"object"===typeof d?d.ptr:J(d);return!!hb(e,b,c,d)};F.prototype.AddIntEntry=F.prototype.AddIntEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&
"object"===typeof c?c.ptr:J(c);d&&"object"===typeof d&&(d=d.ptr);return!!ib(e,b,c,d)};F.prototype.AddIntEntryArray=F.prototype.AddIntEntryArray=function(b,c,d,e){var g=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);"object"==typeof d&&(d=V(d));e&&"object"===typeof e&&(e=e.ptr);return!!jb(g,b,c,d,e)};F.prototype.AddDoubleEntry=F.prototype.AddDoubleEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?
c.ptr:J(c);d&&"object"===typeof d&&(d=d.ptr);return!!kb(e,b,c,d)};F.prototype.__destroy__=F.prototype.__destroy__=function(){lb(this.ptr)};x.prototype=Object.create(v.prototype);x.prototype.constructor=x;x.prototype.__class__=x;x.__cache__={};a.PointCloudBuilder=x;x.prototype.AddFloatAttribute=x.prototype.AddFloatAttribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=
e.ptr);"object"==typeof g&&(g=Z(g));return mb(t,b,c,d,e,g)};x.prototype.AddInt8Attribute=x.prototype.AddInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return nb(t,b,c,d,e,g)};x.prototype.AddUInt8Attribute=x.prototype.AddUInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===
typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return ob(t,b,c,d,e,g)};x.prototype.AddInt16Attribute=x.prototype.AddInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return pb(t,b,c,d,e,g)};x.prototype.AddUInt16Attribute=x.prototype.AddUInt16Attribute=function(b,
c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return qb(t,b,c,d,e,g)};x.prototype.AddInt32Attribute=x.prototype.AddInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return rb(t,
b,c,d,e,g)};x.prototype.AddUInt32Attribute=x.prototype.AddUInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return sb(t,b,c,d,e,g)};x.prototype.AddMetadata=x.prototype.AddMetadata=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!tb(d,b,c)};x.prototype.SetMetadataForAttribute=
x.prototype.SetMetadataForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!ub(e,b,c,d)};x.prototype.__destroy__=x.prototype.__destroy__=function(){vb(this.ptr)};u.prototype=Object.create(v.prototype);u.prototype.constructor=u;u.prototype.__class__=u;u.__cache__={};a.MeshBuilder=u;u.prototype.AddFacesToMesh=u.prototype.AddFacesToMesh=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&
(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);"object"==typeof d&&(d=V(d));return!!wb(e,b,c,d)};u.prototype.AddFloatAttributeToMesh=u.prototype.AddFloatAttributeToMesh=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return xb(t,b,c,d,e,g)};u.prototype.AddInt32AttributeToMesh=u.prototype.AddInt32AttributeToMesh=function(b,c,d,e,g){var t=this.ptr;
n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return yb(t,b,c,d,e,g)};u.prototype.AddMetadataToMesh=u.prototype.AddMetadataToMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!zb(d,b,c)};u.prototype.AddFloatAttribute=u.prototype.AddFloatAttribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&
(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return Ab(t,b,c,d,e,g)};u.prototype.AddInt8Attribute=u.prototype.AddInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return Bb(t,b,c,d,e,g)};u.prototype.AddUInt8Attribute=u.prototype.AddUInt8Attribute=
function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return Cb(t,b,c,d,e,g)};u.prototype.AddInt16Attribute=u.prototype.AddInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=
R(g));return Db(t,b,c,d,e,g)};u.prototype.AddUInt16Attribute=u.prototype.AddUInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return Eb(t,b,c,d,e,g)};u.prototype.AddInt32Attribute=u.prototype.AddInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);
d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return Fb(t,b,c,d,e,g)};u.prototype.AddUInt32Attribute=u.prototype.AddUInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return Gb(t,b,c,d,e,g)};u.prototype.AddMetadata=u.prototype.AddMetadata=function(b,c){var d=this.ptr;b&&"object"===
typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!Hb(d,b,c)};u.prototype.SetMetadataForAttribute=u.prototype.SetMetadataForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Ib(e,b,c,d)};u.prototype.__destroy__=u.prototype.__destroy__=function(){Jb(this.ptr)};y.prototype=Object.create(v.prototype);y.prototype.constructor=y;y.prototype.__class__=y;y.__cache__={};a.Encoder=y;y.prototype.SetEncodingMethod=
y.prototype.SetEncodingMethod=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Kb(c,b)};y.prototype.SetAttributeQuantization=y.prototype.SetAttributeQuantization=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Lb(d,b,c)};y.prototype.SetAttributeExplicitQuantization=y.prototype.SetAttributeExplicitQuantization=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
typeof d&&(d=d.ptr);"object"==typeof e&&(e=Z(e));g&&"object"===typeof g&&(g=g.ptr);Mb(t,b,c,d,e,g)};y.prototype.SetSpeedOptions=y.prototype.SetSpeedOptions=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Nb(d,b,c)};y.prototype.SetTrackEncodedProperties=y.prototype.SetTrackEncodedProperties=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Ob(c,b)};y.prototype.EncodeMeshToDracoBuffer=y.prototype.EncodeMeshToDracoBuffer=function(b,c){var d=
this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return Pb(d,b,c)};y.prototype.EncodePointCloudToDracoBuffer=y.prototype.EncodePointCloudToDracoBuffer=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return Qb(e,b,c,d)};y.prototype.GetNumberOfEncodedPoints=y.prototype.GetNumberOfEncodedPoints=function(){return Rb(this.ptr)};y.prototype.GetNumberOfEncodedFaces=y.prototype.GetNumberOfEncodedFaces=
function(){return Sb(this.ptr)};y.prototype.__destroy__=y.prototype.__destroy__=function(){Tb(this.ptr)};A.prototype=Object.create(v.prototype);A.prototype.constructor=A;A.prototype.__class__=A;A.__cache__={};a.ExpertEncoder=A;A.prototype.SetEncodingMethod=A.prototype.SetEncodingMethod=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Ub(c,b)};A.prototype.SetAttributeQuantization=A.prototype.SetAttributeQuantization=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===
typeof c&&(c=c.ptr);Vb(d,b,c)};A.prototype.SetAttributeExplicitQuantization=A.prototype.SetAttributeExplicitQuantization=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);"object"==typeof e&&(e=Z(e));g&&"object"===typeof g&&(g=g.ptr);Wb(t,b,c,d,e,g)};A.prototype.SetSpeedOptions=A.prototype.SetSpeedOptions=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);
Xb(d,b,c)};A.prototype.SetTrackEncodedProperties=A.prototype.SetTrackEncodedProperties=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Yb(c,b)};A.prototype.EncodeToDracoBuffer=A.prototype.EncodeToDracoBuffer=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return Zb(d,b,c)};A.prototype.GetNumberOfEncodedPoints=A.prototype.GetNumberOfEncodedPoints=function(){return $b(this.ptr)};A.prototype.GetNumberOfEncodedFaces=A.prototype.GetNumberOfEncodedFaces=
function(){return ac(this.ptr)};A.prototype.__destroy__=A.prototype.__destroy__=function(){bc(this.ptr)};(function(){function b(){a.INVALID=cc();a.POSITION=dc();a.NORMAL=ec();a.COLOR=fc();a.TEX_COORD=gc();a.GENERIC=hc();a.INVALID_GEOMETRY_TYPE=ic();a.POINT_CLOUD=jc();a.TRIANGULAR_MESH=kc();a.MESH_SEQUENTIAL_ENCODING=lc();a.MESH_EDGEBREAKER_ENCODING=mc()}qa?b():ka.unshift(b)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();return p.ready}}();
"object"===typeof exports&&"object"===typeof module?module.exports=DracoEncoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoEncoderModule}):"object"===typeof exports&&(exports.DracoEncoderModule=DracoEncoderModule);
function(){return(tb=a._emscripten_bind_PointCloudBuilder_AddMetadata_2=a.asm.Y).apply(null,arguments)},ub=a._emscripten_bind_PointCloudBuilder_SetMetadataForAttribute_3=function(){return(ub=a._emscripten_bind_PointCloudBuilder_SetMetadataForAttribute_3=a.asm.Z).apply(null,arguments)},vb=a._emscripten_bind_PointCloudBuilder_SetNormalizedFlagForAttribute_3=function(){return(vb=a._emscripten_bind_PointCloudBuilder_SetNormalizedFlagForAttribute_3=a.asm._).apply(null,arguments)},wb=a._emscripten_bind_PointCloudBuilder___destroy___0=
function(){return(wb=a._emscripten_bind_PointCloudBuilder___destroy___0=a.asm.$).apply(null,arguments)},Ca=a._emscripten_bind_MeshBuilder_MeshBuilder_0=function(){return(Ca=a._emscripten_bind_MeshBuilder_MeshBuilder_0=a.asm.aa).apply(null,arguments)},xb=a._emscripten_bind_MeshBuilder_AddFacesToMesh_3=function(){return(xb=a._emscripten_bind_MeshBuilder_AddFacesToMesh_3=a.asm.ba).apply(null,arguments)},yb=a._emscripten_bind_MeshBuilder_AddFloatAttributeToMesh_5=function(){return(yb=a._emscripten_bind_MeshBuilder_AddFloatAttributeToMesh_5=
a.asm.ca).apply(null,arguments)},zb=a._emscripten_bind_MeshBuilder_AddInt32AttributeToMesh_5=function(){return(zb=a._emscripten_bind_MeshBuilder_AddInt32AttributeToMesh_5=a.asm.da).apply(null,arguments)},Ab=a._emscripten_bind_MeshBuilder_AddMetadataToMesh_2=function(){return(Ab=a._emscripten_bind_MeshBuilder_AddMetadataToMesh_2=a.asm.ea).apply(null,arguments)},Bb=a._emscripten_bind_MeshBuilder_AddFloatAttribute_5=function(){return(Bb=a._emscripten_bind_MeshBuilder_AddFloatAttribute_5=a.asm.fa).apply(null,
arguments)},Cb=a._emscripten_bind_MeshBuilder_AddInt8Attribute_5=function(){return(Cb=a._emscripten_bind_MeshBuilder_AddInt8Attribute_5=a.asm.ga).apply(null,arguments)},Db=a._emscripten_bind_MeshBuilder_AddUInt8Attribute_5=function(){return(Db=a._emscripten_bind_MeshBuilder_AddUInt8Attribute_5=a.asm.ha).apply(null,arguments)},Eb=a._emscripten_bind_MeshBuilder_AddInt16Attribute_5=function(){return(Eb=a._emscripten_bind_MeshBuilder_AddInt16Attribute_5=a.asm.ia).apply(null,arguments)},Fb=a._emscripten_bind_MeshBuilder_AddUInt16Attribute_5=
function(){return(Fb=a._emscripten_bind_MeshBuilder_AddUInt16Attribute_5=a.asm.ja).apply(null,arguments)},Gb=a._emscripten_bind_MeshBuilder_AddInt32Attribute_5=function(){return(Gb=a._emscripten_bind_MeshBuilder_AddInt32Attribute_5=a.asm.ka).apply(null,arguments)},Hb=a._emscripten_bind_MeshBuilder_AddUInt32Attribute_5=function(){return(Hb=a._emscripten_bind_MeshBuilder_AddUInt32Attribute_5=a.asm.la).apply(null,arguments)},Ib=a._emscripten_bind_MeshBuilder_AddMetadata_2=function(){return(Ib=a._emscripten_bind_MeshBuilder_AddMetadata_2=
a.asm.ma).apply(null,arguments)},Jb=a._emscripten_bind_MeshBuilder_SetMetadataForAttribute_3=function(){return(Jb=a._emscripten_bind_MeshBuilder_SetMetadataForAttribute_3=a.asm.na).apply(null,arguments)},Kb=a._emscripten_bind_MeshBuilder_SetNormalizedFlagForAttribute_3=function(){return(Kb=a._emscripten_bind_MeshBuilder_SetNormalizedFlagForAttribute_3=a.asm.oa).apply(null,arguments)},Lb=a._emscripten_bind_MeshBuilder___destroy___0=function(){return(Lb=a._emscripten_bind_MeshBuilder___destroy___0=
a.asm.pa).apply(null,arguments)},Da=a._emscripten_bind_Encoder_Encoder_0=function(){return(Da=a._emscripten_bind_Encoder_Encoder_0=a.asm.qa).apply(null,arguments)},Mb=a._emscripten_bind_Encoder_SetEncodingMethod_1=function(){return(Mb=a._emscripten_bind_Encoder_SetEncodingMethod_1=a.asm.ra).apply(null,arguments)},Nb=a._emscripten_bind_Encoder_SetAttributeQuantization_2=function(){return(Nb=a._emscripten_bind_Encoder_SetAttributeQuantization_2=a.asm.sa).apply(null,arguments)},Ob=a._emscripten_bind_Encoder_SetAttributeExplicitQuantization_5=
function(){return(Ob=a._emscripten_bind_Encoder_SetAttributeExplicitQuantization_5=a.asm.ta).apply(null,arguments)},Pb=a._emscripten_bind_Encoder_SetSpeedOptions_2=function(){return(Pb=a._emscripten_bind_Encoder_SetSpeedOptions_2=a.asm.ua).apply(null,arguments)},Qb=a._emscripten_bind_Encoder_SetTrackEncodedProperties_1=function(){return(Qb=a._emscripten_bind_Encoder_SetTrackEncodedProperties_1=a.asm.va).apply(null,arguments)},Rb=a._emscripten_bind_Encoder_EncodeMeshToDracoBuffer_2=function(){return(Rb=
a._emscripten_bind_Encoder_EncodeMeshToDracoBuffer_2=a.asm.wa).apply(null,arguments)},Sb=a._emscripten_bind_Encoder_EncodePointCloudToDracoBuffer_3=function(){return(Sb=a._emscripten_bind_Encoder_EncodePointCloudToDracoBuffer_3=a.asm.xa).apply(null,arguments)},Tb=a._emscripten_bind_Encoder_GetNumberOfEncodedPoints_0=function(){return(Tb=a._emscripten_bind_Encoder_GetNumberOfEncodedPoints_0=a.asm.ya).apply(null,arguments)},Ub=a._emscripten_bind_Encoder_GetNumberOfEncodedFaces_0=function(){return(Ub=
a._emscripten_bind_Encoder_GetNumberOfEncodedFaces_0=a.asm.za).apply(null,arguments)},Vb=a._emscripten_bind_Encoder___destroy___0=function(){return(Vb=a._emscripten_bind_Encoder___destroy___0=a.asm.Aa).apply(null,arguments)},Ea=a._emscripten_bind_ExpertEncoder_ExpertEncoder_1=function(){return(Ea=a._emscripten_bind_ExpertEncoder_ExpertEncoder_1=a.asm.Ba).apply(null,arguments)},Wb=a._emscripten_bind_ExpertEncoder_SetEncodingMethod_1=function(){return(Wb=a._emscripten_bind_ExpertEncoder_SetEncodingMethod_1=
a.asm.Ca).apply(null,arguments)},Xb=a._emscripten_bind_ExpertEncoder_SetAttributeQuantization_2=function(){return(Xb=a._emscripten_bind_ExpertEncoder_SetAttributeQuantization_2=a.asm.Da).apply(null,arguments)},Yb=a._emscripten_bind_ExpertEncoder_SetAttributeExplicitQuantization_5=function(){return(Yb=a._emscripten_bind_ExpertEncoder_SetAttributeExplicitQuantization_5=a.asm.Ea).apply(null,arguments)},Zb=a._emscripten_bind_ExpertEncoder_SetSpeedOptions_2=function(){return(Zb=a._emscripten_bind_ExpertEncoder_SetSpeedOptions_2=
a.asm.Fa).apply(null,arguments)},$b=a._emscripten_bind_ExpertEncoder_SetTrackEncodedProperties_1=function(){return($b=a._emscripten_bind_ExpertEncoder_SetTrackEncodedProperties_1=a.asm.Ga).apply(null,arguments)},ac=a._emscripten_bind_ExpertEncoder_EncodeToDracoBuffer_2=function(){return(ac=a._emscripten_bind_ExpertEncoder_EncodeToDracoBuffer_2=a.asm.Ha).apply(null,arguments)},bc=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedPoints_0=function(){return(bc=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedPoints_0=
a.asm.Ia).apply(null,arguments)},cc=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedFaces_0=function(){return(cc=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedFaces_0=a.asm.Ja).apply(null,arguments)},dc=a._emscripten_bind_ExpertEncoder___destroy___0=function(){return(dc=a._emscripten_bind_ExpertEncoder___destroy___0=a.asm.Ka).apply(null,arguments)},ec=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(ec=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.La).apply(null,
arguments)},fc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(fc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=a.asm.Ma).apply(null,arguments)},gc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(gc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.Na).apply(null,arguments)},hc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(hc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.Oa).apply(null,
arguments)},ic=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(ic=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=a.asm.Pa).apply(null,arguments)},jc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(jc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.Qa).apply(null,arguments)},kc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(kc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=
a.asm.Ra).apply(null,arguments)},lc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(lc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=a.asm.Sa).apply(null,arguments)},mc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(mc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.Ta).apply(null,arguments)},nc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_SEQUENTIAL_ENCODING=function(){return(nc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_SEQUENTIAL_ENCODING=
a.asm.Ua).apply(null,arguments)},oc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_EDGEBREAKER_ENCODING=function(){return(oc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_EDGEBREAKER_ENCODING=a.asm.Va).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Wa).apply(null,arguments)};a._free=function(){return(a._free=a.asm.Xa).apply(null,arguments)};var pa=function(){return(pa=a.asm.Ya).apply(null,arguments)};a.___start_em_js=19116;a.___stop_em_js=19214;var da;aa=function b(){da||B();
da||(aa=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();B();v.prototype=Object.create(v.prototype);v.prototype.constructor=v;v.prototype.__class__=v;v.__cache__={};a.WrapperObject=v;a.getCache=D;a.wrapPointer=I;a.castObject=function(b,c){return I(b.ptr,c)};a.NULL=I(0);a.destroy=function(b){if(!b.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";b.__destroy__();delete D(b.__class__)[b.ptr]};a.compare=function(b,
c){return b.ptr===c.ptr};a.getPointer=function(b){return b.ptr};a.getClass=function(b){return b.__class__};var n={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(n.needed){for(var b=0;b<n.temps.length;b++)a._free(n.temps[b]);n.temps.length=0;a._free(n.buffer);n.buffer=0;n.size+=n.needed;n.needed=0}n.buffer||(n.size+=128,n.buffer=a._malloc(n.size),n.buffer||k(void 0));n.pos=0},alloc:function(b,c){n.buffer||k(void 0);b=b.length*c.BYTES_PER_ELEMENT;b=b+7&-8;n.pos+b>=n.size?(0<b||k(void 0),
n.needed+=b,c=a._malloc(b),n.temps.push(c)):(c=n.buffer+n.pos,n.pos+=b);return c},copy:function(b,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var e=0;e<b.length;e++)c[d+e]=b[e]}};P.prototype=Object.create(v.prototype);P.prototype.constructor=P;P.prototype.__class__=P;P.__cache__={};a.VoidPtr=P;P.prototype.__destroy__=P.prototype.__destroy__=function(){La(this.ptr)};M.prototype=Object.create(v.prototype);M.prototype.constructor=M;M.prototype.__class__=
M;M.__cache__={};a.GeometryAttribute=M;M.prototype.__destroy__=M.prototype.__destroy__=function(){Ma(this.ptr)};z.prototype=Object.create(v.prototype);z.prototype.constructor=z;z.prototype.__class__=z;z.__cache__={};a.PointAttribute=z;z.prototype.size=z.prototype.size=function(){return Na(this.ptr)};z.prototype.attribute_type=z.prototype.attribute_type=function(){return Oa(this.ptr)};z.prototype.data_type=z.prototype.data_type=function(){return Pa(this.ptr)};z.prototype.num_components=z.prototype.num_components=
function(){return Qa(this.ptr)};z.prototype.normalized=z.prototype.normalized=function(){return!!Ra(this.ptr)};z.prototype.byte_stride=z.prototype.byte_stride=function(){return Sa(this.ptr)};z.prototype.byte_offset=z.prototype.byte_offset=function(){return Ta(this.ptr)};z.prototype.unique_id=z.prototype.unique_id=function(){return Ua(this.ptr)};z.prototype.__destroy__=z.prototype.__destroy__=function(){Va(this.ptr)};G.prototype=Object.create(v.prototype);G.prototype.constructor=G;G.prototype.__class__=
G;G.__cache__={};a.PointCloud=G;G.prototype.num_attributes=G.prototype.num_attributes=function(){return Wa(this.ptr)};G.prototype.num_points=G.prototype.num_points=function(){return Xa(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){Ya(this.ptr)};E.prototype=Object.create(v.prototype);E.prototype.constructor=E;E.prototype.__class__=E;E.__cache__={};a.Mesh=E;E.prototype.num_faces=E.prototype.num_faces=function(){return Za(this.ptr)};E.prototype.num_attributes=E.prototype.num_attributes=
function(){return $a(this.ptr)};E.prototype.num_points=E.prototype.num_points=function(){return ab(this.ptr)};E.prototype.set_num_points=E.prototype.set_num_points=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);bb(c,b)};E.prototype.__destroy__=E.prototype.__destroy__=function(){cb(this.ptr)};N.prototype=Object.create(v.prototype);N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.Metadata=N;N.prototype.__destroy__=N.prototype.__destroy__=function(){db(this.ptr)};H.prototype=
Object.create(v.prototype);H.prototype.constructor=H;H.prototype.__class__=H;H.__cache__={};a.DracoInt8Array=H;H.prototype.GetValue=H.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return eb(c,b)};H.prototype.size=H.prototype.size=function(){return fb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=function(){gb(this.ptr)};F.prototype=Object.create(v.prototype);F.prototype.constructor=F;F.prototype.__class__=F;F.__cache__={};a.MetadataBuilder=F;F.prototype.AddStringEntry=
F.prototype.AddStringEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);d=d&&"object"===typeof d?d.ptr:J(d);return!!hb(e,b,c,d)};F.prototype.AddIntEntry=F.prototype.AddIntEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);d&&"object"===typeof d&&(d=d.ptr);return!!ib(e,b,c,d)};F.prototype.AddIntEntryArray=F.prototype.AddIntEntryArray=function(b,c,d,e){var g=this.ptr;
n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);"object"==typeof d&&(d=V(d));e&&"object"===typeof e&&(e=e.ptr);return!!jb(g,b,c,d,e)};F.prototype.AddDoubleEntry=F.prototype.AddDoubleEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);d&&"object"===typeof d&&(d=d.ptr);return!!kb(e,b,c,d)};F.prototype.__destroy__=F.prototype.__destroy__=function(){lb(this.ptr)};x.prototype=Object.create(v.prototype);
x.prototype.constructor=x;x.prototype.__class__=x;x.__cache__={};a.PointCloudBuilder=x;x.prototype.AddFloatAttribute=x.prototype.AddFloatAttribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return mb(t,b,c,d,e,g)};x.prototype.AddInt8Attribute=x.prototype.AddInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===
typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return nb(t,b,c,d,e,g)};x.prototype.AddUInt8Attribute=x.prototype.AddUInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return ob(t,b,c,d,e,g)};x.prototype.AddInt16Attribute=
x.prototype.AddInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return pb(t,b,c,d,e,g)};x.prototype.AddUInt16Attribute=x.prototype.AddUInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&
(e=e.ptr);"object"==typeof g&&(g=R(g));return qb(t,b,c,d,e,g)};x.prototype.AddInt32Attribute=x.prototype.AddInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return rb(t,b,c,d,e,g)};x.prototype.AddUInt32Attribute=x.prototype.AddUInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);
c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return sb(t,b,c,d,e,g)};x.prototype.AddMetadata=x.prototype.AddMetadata=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!tb(d,b,c)};x.prototype.SetMetadataForAttribute=x.prototype.SetMetadataForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
typeof d&&(d=d.ptr);return!!ub(e,b,c,d)};x.prototype.SetNormalizedFlagForAttribute=x.prototype.SetNormalizedFlagForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!vb(e,b,c,d)};x.prototype.__destroy__=x.prototype.__destroy__=function(){wb(this.ptr)};u.prototype=Object.create(v.prototype);u.prototype.constructor=u;u.prototype.__class__=u;u.__cache__={};a.MeshBuilder=u;u.prototype.AddFacesToMesh=u.prototype.AddFacesToMesh=
function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);"object"==typeof d&&(d=V(d));return!!xb(e,b,c,d)};u.prototype.AddFloatAttributeToMesh=u.prototype.AddFloatAttributeToMesh=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return yb(t,b,c,d,e,g)};u.prototype.AddInt32AttributeToMesh=
u.prototype.AddInt32AttributeToMesh=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return zb(t,b,c,d,e,g)};u.prototype.AddMetadataToMesh=u.prototype.AddMetadataToMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!Ab(d,b,c)};u.prototype.AddFloatAttribute=u.prototype.AddFloatAttribute=
function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return Bb(t,b,c,d,e,g)};u.prototype.AddInt8Attribute=u.prototype.AddInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));
return Cb(t,b,c,d,e,g)};u.prototype.AddUInt8Attribute=u.prototype.AddUInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return Db(t,b,c,d,e,g)};u.prototype.AddInt16Attribute=u.prototype.AddInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return Eb(t,b,c,d,e,g)};u.prototype.AddUInt16Attribute=u.prototype.AddUInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return Fb(t,b,c,d,e,g)};u.prototype.AddInt32Attribute=u.prototype.AddInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();
b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return Gb(t,b,c,d,e,g)};u.prototype.AddUInt32Attribute=u.prototype.AddUInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return Hb(t,b,c,d,e,g)};u.prototype.AddMetadata=
u.prototype.AddMetadata=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!Ib(d,b,c)};u.prototype.SetMetadataForAttribute=u.prototype.SetMetadataForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Jb(e,b,c,d)};u.prototype.SetNormalizedFlagForAttribute=u.prototype.SetNormalizedFlagForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&
(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Kb(e,b,c,d)};u.prototype.__destroy__=u.prototype.__destroy__=function(){Lb(this.ptr)};y.prototype=Object.create(v.prototype);y.prototype.constructor=y;y.prototype.__class__=y;y.__cache__={};a.Encoder=y;y.prototype.SetEncodingMethod=y.prototype.SetEncodingMethod=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Mb(c,b)};y.prototype.SetAttributeQuantization=y.prototype.SetAttributeQuantization=function(b,
c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Nb(d,b,c)};y.prototype.SetAttributeExplicitQuantization=y.prototype.SetAttributeExplicitQuantization=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);"object"==typeof e&&(e=Z(e));g&&"object"===typeof g&&(g=g.ptr);Ob(t,b,c,d,e,g)};y.prototype.SetSpeedOptions=y.prototype.SetSpeedOptions=function(b,c){var d=this.ptr;
b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Pb(d,b,c)};y.prototype.SetTrackEncodedProperties=y.prototype.SetTrackEncodedProperties=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Qb(c,b)};y.prototype.EncodeMeshToDracoBuffer=y.prototype.EncodeMeshToDracoBuffer=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return Rb(d,b,c)};y.prototype.EncodePointCloudToDracoBuffer=y.prototype.EncodePointCloudToDracoBuffer=function(b,
c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return Sb(e,b,c,d)};y.prototype.GetNumberOfEncodedPoints=y.prototype.GetNumberOfEncodedPoints=function(){return Tb(this.ptr)};y.prototype.GetNumberOfEncodedFaces=y.prototype.GetNumberOfEncodedFaces=function(){return Ub(this.ptr)};y.prototype.__destroy__=y.prototype.__destroy__=function(){Vb(this.ptr)};A.prototype=Object.create(v.prototype);A.prototype.constructor=A;A.prototype.__class__=
A;A.__cache__={};a.ExpertEncoder=A;A.prototype.SetEncodingMethod=A.prototype.SetEncodingMethod=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Wb(c,b)};A.prototype.SetAttributeQuantization=A.prototype.SetAttributeQuantization=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Xb(d,b,c)};A.prototype.SetAttributeExplicitQuantization=A.prototype.SetAttributeExplicitQuantization=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&
(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);"object"==typeof e&&(e=Z(e));g&&"object"===typeof g&&(g=g.ptr);Yb(t,b,c,d,e,g)};A.prototype.SetSpeedOptions=A.prototype.SetSpeedOptions=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Zb(d,b,c)};A.prototype.SetTrackEncodedProperties=A.prototype.SetTrackEncodedProperties=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);$b(c,b)};A.prototype.EncodeToDracoBuffer=A.prototype.EncodeToDracoBuffer=
function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return ac(d,b,c)};A.prototype.GetNumberOfEncodedPoints=A.prototype.GetNumberOfEncodedPoints=function(){return bc(this.ptr)};A.prototype.GetNumberOfEncodedFaces=A.prototype.GetNumberOfEncodedFaces=function(){return cc(this.ptr)};A.prototype.__destroy__=A.prototype.__destroy__=function(){dc(this.ptr)};(function(){function b(){a.INVALID=ec();a.POSITION=fc();a.NORMAL=gc();a.COLOR=hc();a.TEX_COORD=ic();a.GENERIC=
jc();a.INVALID_GEOMETRY_TYPE=kc();a.POINT_CLOUD=lc();a.TRIANGULAR_MESH=mc();a.MESH_SEQUENTIAL_ENCODING=nc();a.MESH_EDGEBREAKER_ENCODING=oc()}qa?b():ka.unshift(b)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();return p.ready}}();"object"===typeof exports&&"object"===typeof module?module.exports=DracoEncoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoEncoderModule}):"object"===typeof exports&&(exports.DracoEncoderModule=DracoEncoderModule);

View File

@ -46,7 +46,7 @@ Create DracoLoader by setting the decoder type:
```js
// (Optional) Change decoder source directory (defaults to
// 'https://www.gstatic.com/draco/versioned/decoders/1.5.6/'). It is recommended
// 'https://www.gstatic.com/draco/versioned/decoders/1.5.7/'). It is recommended
// to always pull your Draco JavaScript and WASM decoders from this URL. Users
// will benefit from having the Draco decoder in cache as more sites start using
// the static URL.

View File

@ -38,7 +38,7 @@
// It is recommended to always pull your Draco JavaScript and WASM decoders
// from this URL. Users will benefit from having the Draco decoder in cache
// as more sites start using the static URL.
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/');
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.7/');
var camera, cameraTarget, scene, renderer;

View File

@ -6,7 +6,7 @@
News
=======
Check out the [README](https://github.com/google/draco/blob/1.5.6/README.md)
Check out the [README](https://github.com/google/draco/blob/1.5.7/README.md)
file for news about this release.
Description

View File

@ -32,9 +32,9 @@ D(z)[this.ptr]=this}function G(){this.ptr=wa();D(G)[this.ptr]=this}function E(){
typeof window,Y="function"==typeof importScripts,Ia="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,L="";if(Ia){var Ja=require("fs"),la=require("path");L=Y?la.dirname(L)+"/":__dirname+"/";var Ka=function(f,b){f=f.startsWith("file://")?new URL(f):la.normalize(f);return Ja.readFileSync(f,b?void 0:"utf8")};var ia=function(f){f=Ka(f,!0);f.buffer||(f=new Uint8Array(f));return f};var ja=function(f,b,c){f=f.startsWith("file://")?new URL(f):la.normalize(f);
Ja.readFile(f,function(d,e){d?c(d):b(e.buffer)})};1<process.argv.length&&process.argv[1].replace(/\\/g,"/");process.argv.slice(2);a.inspect=function(){return"[Emscripten Module object]"}}else if(oa||Y)Y?L=self.location.href:"undefined"!=typeof document&&document.currentScript&&(L=document.currentScript.src),l&&(L=l),L=0!==L.indexOf("blob:")?L.substr(0,L.replace(/[?#].*/,"").lastIndexOf("/")+1):"",Ka=function(f){var b=new XMLHttpRequest;b.open("GET",f,!1);b.send(null);return b.responseText},Y&&(ia=
function(f){var b=new XMLHttpRequest;b.open("GET",f,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),ja=function(f,b,c){var d=new XMLHttpRequest;d.open("GET",f,!0);d.responseType="arraybuffer";d.onload=function(){200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};a.print||console.log.bind(console);var W=a.printErr||console.warn.bind(console);Object.assign(a,Ha);Ha=null;var X;a.wasmBinary&&(X=a.wasmBinary);"object"!=typeof WebAssembly&&
k("no native wasm support detected");var ba,na=!1,O,fa,ea,S,T,ha,ta=[],ka=[],sa=[],qa=!1,U=0,ma=null,aa=null;var K="draco_encoder.wasm";K.startsWith("data:application/octet-stream;base64,")||(K=m(K));var nc=0,oc={b:function(f,b,c){(new w(f)).init(b,c);nc++;throw f;},a:function(){k("")},d:function(f,b,c){fa.copyWithin(f,b,b+c)},c:function(f){var b=fa.length;f>>>=0;if(2147483648<f)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,f+100663296);var e=Math;d=Math.max(f,d);e=e.min.call(e,2147483648,
k("no native wasm support detected");var ba,na=!1,O,fa,ea,S,T,ha,ta=[],ka=[],sa=[],qa=!1,U=0,ma=null,aa=null;var K="draco_encoder.wasm";K.startsWith("data:application/octet-stream;base64,")||(K=m(K));var pc=0,qc={b:function(f,b,c){(new w(f)).init(b,c);pc++;throw f;},a:function(){k("")},d:function(f,b,c){fa.copyWithin(f,b,b+c)},c:function(f){var b=fa.length;f>>>=0;if(2147483648<f)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,f+100663296);var e=Math;d=Math.max(f,d);e=e.min.call(e,2147483648,
d+(65536-d%65536)%65536);a:{d=ba.buffer;try{ba.grow(e-d.byteLength+65535>>>16);r();var g=1;break a}catch(t){}g=void 0}if(g)return!0}return!1}};(function(){function f(e,g){a.asm=e.exports;ba=a.asm.e;r();ka.unshift(a.asm.f);U--;a.monitorRunDependencies&&a.monitorRunDependencies(U);0==U&&(null!==ma&&(clearInterval(ma),ma=null),aa&&(e=aa,aa=null,e()))}function b(e){f(e.instance)}function c(e){return h().then(function(g){return WebAssembly.instantiate(g,d)}).then(function(g){return g}).then(e,function(g){W("failed to asynchronously prepare wasm: "+
g);k(g)})}var d={a:oc};U++;a.monitorRunDependencies&&a.monitorRunDependencies(U);if(a.instantiateWasm)try{return a.instantiateWasm(d,f)}catch(e){W("Module.instantiateWasm callback failed with error: "+e),ca(e)}(function(){return X||"function"!=typeof WebAssembly.instantiateStreaming||K.startsWith("data:application/octet-stream;base64,")||K.startsWith("file://")||Ia||"function"!=typeof fetch?c(b):fetch(K,{credentials:"same-origin"}).then(function(e){return WebAssembly.instantiateStreaming(e,d).then(b,
g);k(g)})}var d={a:qc};U++;a.monitorRunDependencies&&a.monitorRunDependencies(U);if(a.instantiateWasm)try{return a.instantiateWasm(d,f)}catch(e){W("Module.instantiateWasm callback failed with error: "+e),ca(e)}(function(){return X||"function"!=typeof WebAssembly.instantiateStreaming||K.startsWith("data:application/octet-stream;base64,")||K.startsWith("file://")||Ia||"function"!=typeof fetch?c(b):fetch(K,{credentials:"same-origin"}).then(function(e){return WebAssembly.instantiateStreaming(e,d).then(b,
function(g){W("wasm streaming compile failed: "+g);W("falling back to ArrayBuffer instantiation");return c(b)})})})().catch(ca);return{}})();var La=a._emscripten_bind_VoidPtr___destroy___0=function(){return(La=a._emscripten_bind_VoidPtr___destroy___0=a.asm.h).apply(null,arguments)},ua=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(ua=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=a.asm.i).apply(null,arguments)},Ma=a._emscripten_bind_GeometryAttribute___destroy___0=
function(){return(Ma=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.j).apply(null,arguments)},va=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(va=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.k).apply(null,arguments)},Na=a._emscripten_bind_PointAttribute_size_0=function(){return(Na=a._emscripten_bind_PointAttribute_size_0=a.asm.l).apply(null,arguments)},Oa=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(Oa=a._emscripten_bind_PointAttribute_attribute_type_0=
a.asm.m).apply(null,arguments)},Pa=a._emscripten_bind_PointAttribute_data_type_0=function(){return(Pa=a._emscripten_bind_PointAttribute_data_type_0=a.asm.n).apply(null,arguments)},Qa=a._emscripten_bind_PointAttribute_num_components_0=function(){return(Qa=a._emscripten_bind_PointAttribute_num_components_0=a.asm.o).apply(null,arguments)},Ra=a._emscripten_bind_PointAttribute_normalized_0=function(){return(Ra=a._emscripten_bind_PointAttribute_normalized_0=a.asm.p).apply(null,arguments)},Sa=a._emscripten_bind_PointAttribute_byte_stride_0=
@ -48,50 +48,51 @@ ib=a._emscripten_bind_MetadataBuilder_AddIntEntry_3=function(){return(ib=a._emsc
function(){return(lb=a._emscripten_bind_MetadataBuilder___destroy___0=a.asm.P).apply(null,arguments)},Ba=a._emscripten_bind_PointCloudBuilder_PointCloudBuilder_0=function(){return(Ba=a._emscripten_bind_PointCloudBuilder_PointCloudBuilder_0=a.asm.Q).apply(null,arguments)},mb=a._emscripten_bind_PointCloudBuilder_AddFloatAttribute_5=function(){return(mb=a._emscripten_bind_PointCloudBuilder_AddFloatAttribute_5=a.asm.R).apply(null,arguments)},nb=a._emscripten_bind_PointCloudBuilder_AddInt8Attribute_5=
function(){return(nb=a._emscripten_bind_PointCloudBuilder_AddInt8Attribute_5=a.asm.S).apply(null,arguments)},ob=a._emscripten_bind_PointCloudBuilder_AddUInt8Attribute_5=function(){return(ob=a._emscripten_bind_PointCloudBuilder_AddUInt8Attribute_5=a.asm.T).apply(null,arguments)},pb=a._emscripten_bind_PointCloudBuilder_AddInt16Attribute_5=function(){return(pb=a._emscripten_bind_PointCloudBuilder_AddInt16Attribute_5=a.asm.U).apply(null,arguments)},qb=a._emscripten_bind_PointCloudBuilder_AddUInt16Attribute_5=
function(){return(qb=a._emscripten_bind_PointCloudBuilder_AddUInt16Attribute_5=a.asm.V).apply(null,arguments)},rb=a._emscripten_bind_PointCloudBuilder_AddInt32Attribute_5=function(){return(rb=a._emscripten_bind_PointCloudBuilder_AddInt32Attribute_5=a.asm.W).apply(null,arguments)},sb=a._emscripten_bind_PointCloudBuilder_AddUInt32Attribute_5=function(){return(sb=a._emscripten_bind_PointCloudBuilder_AddUInt32Attribute_5=a.asm.X).apply(null,arguments)},tb=a._emscripten_bind_PointCloudBuilder_AddMetadata_2=
function(){return(tb=a._emscripten_bind_PointCloudBuilder_AddMetadata_2=a.asm.Y).apply(null,arguments)},ub=a._emscripten_bind_PointCloudBuilder_SetMetadataForAttribute_3=function(){return(ub=a._emscripten_bind_PointCloudBuilder_SetMetadataForAttribute_3=a.asm.Z).apply(null,arguments)},vb=a._emscripten_bind_PointCloudBuilder___destroy___0=function(){return(vb=a._emscripten_bind_PointCloudBuilder___destroy___0=a.asm._).apply(null,arguments)},Ca=a._emscripten_bind_MeshBuilder_MeshBuilder_0=function(){return(Ca=
a._emscripten_bind_MeshBuilder_MeshBuilder_0=a.asm.$).apply(null,arguments)},wb=a._emscripten_bind_MeshBuilder_AddFacesToMesh_3=function(){return(wb=a._emscripten_bind_MeshBuilder_AddFacesToMesh_3=a.asm.aa).apply(null,arguments)},xb=a._emscripten_bind_MeshBuilder_AddFloatAttributeToMesh_5=function(){return(xb=a._emscripten_bind_MeshBuilder_AddFloatAttributeToMesh_5=a.asm.ba).apply(null,arguments)},yb=a._emscripten_bind_MeshBuilder_AddInt32AttributeToMesh_5=function(){return(yb=a._emscripten_bind_MeshBuilder_AddInt32AttributeToMesh_5=
a.asm.ca).apply(null,arguments)},zb=a._emscripten_bind_MeshBuilder_AddMetadataToMesh_2=function(){return(zb=a._emscripten_bind_MeshBuilder_AddMetadataToMesh_2=a.asm.da).apply(null,arguments)},Ab=a._emscripten_bind_MeshBuilder_AddFloatAttribute_5=function(){return(Ab=a._emscripten_bind_MeshBuilder_AddFloatAttribute_5=a.asm.ea).apply(null,arguments)},Bb=a._emscripten_bind_MeshBuilder_AddInt8Attribute_5=function(){return(Bb=a._emscripten_bind_MeshBuilder_AddInt8Attribute_5=a.asm.fa).apply(null,arguments)},
Cb=a._emscripten_bind_MeshBuilder_AddUInt8Attribute_5=function(){return(Cb=a._emscripten_bind_MeshBuilder_AddUInt8Attribute_5=a.asm.ga).apply(null,arguments)},Db=a._emscripten_bind_MeshBuilder_AddInt16Attribute_5=function(){return(Db=a._emscripten_bind_MeshBuilder_AddInt16Attribute_5=a.asm.ha).apply(null,arguments)},Eb=a._emscripten_bind_MeshBuilder_AddUInt16Attribute_5=function(){return(Eb=a._emscripten_bind_MeshBuilder_AddUInt16Attribute_5=a.asm.ia).apply(null,arguments)},Fb=a._emscripten_bind_MeshBuilder_AddInt32Attribute_5=
function(){return(Fb=a._emscripten_bind_MeshBuilder_AddInt32Attribute_5=a.asm.ja).apply(null,arguments)},Gb=a._emscripten_bind_MeshBuilder_AddUInt32Attribute_5=function(){return(Gb=a._emscripten_bind_MeshBuilder_AddUInt32Attribute_5=a.asm.ka).apply(null,arguments)},Hb=a._emscripten_bind_MeshBuilder_AddMetadata_2=function(){return(Hb=a._emscripten_bind_MeshBuilder_AddMetadata_2=a.asm.la).apply(null,arguments)},Ib=a._emscripten_bind_MeshBuilder_SetMetadataForAttribute_3=function(){return(Ib=a._emscripten_bind_MeshBuilder_SetMetadataForAttribute_3=
a.asm.ma).apply(null,arguments)},Jb=a._emscripten_bind_MeshBuilder___destroy___0=function(){return(Jb=a._emscripten_bind_MeshBuilder___destroy___0=a.asm.na).apply(null,arguments)},Da=a._emscripten_bind_Encoder_Encoder_0=function(){return(Da=a._emscripten_bind_Encoder_Encoder_0=a.asm.oa).apply(null,arguments)},Kb=a._emscripten_bind_Encoder_SetEncodingMethod_1=function(){return(Kb=a._emscripten_bind_Encoder_SetEncodingMethod_1=a.asm.pa).apply(null,arguments)},Lb=a._emscripten_bind_Encoder_SetAttributeQuantization_2=
function(){return(Lb=a._emscripten_bind_Encoder_SetAttributeQuantization_2=a.asm.qa).apply(null,arguments)},Mb=a._emscripten_bind_Encoder_SetAttributeExplicitQuantization_5=function(){return(Mb=a._emscripten_bind_Encoder_SetAttributeExplicitQuantization_5=a.asm.ra).apply(null,arguments)},Nb=a._emscripten_bind_Encoder_SetSpeedOptions_2=function(){return(Nb=a._emscripten_bind_Encoder_SetSpeedOptions_2=a.asm.sa).apply(null,arguments)},Ob=a._emscripten_bind_Encoder_SetTrackEncodedProperties_1=function(){return(Ob=
a._emscripten_bind_Encoder_SetTrackEncodedProperties_1=a.asm.ta).apply(null,arguments)},Pb=a._emscripten_bind_Encoder_EncodeMeshToDracoBuffer_2=function(){return(Pb=a._emscripten_bind_Encoder_EncodeMeshToDracoBuffer_2=a.asm.ua).apply(null,arguments)},Qb=a._emscripten_bind_Encoder_EncodePointCloudToDracoBuffer_3=function(){return(Qb=a._emscripten_bind_Encoder_EncodePointCloudToDracoBuffer_3=a.asm.va).apply(null,arguments)},Rb=a._emscripten_bind_Encoder_GetNumberOfEncodedPoints_0=function(){return(Rb=
a._emscripten_bind_Encoder_GetNumberOfEncodedPoints_0=a.asm.wa).apply(null,arguments)},Sb=a._emscripten_bind_Encoder_GetNumberOfEncodedFaces_0=function(){return(Sb=a._emscripten_bind_Encoder_GetNumberOfEncodedFaces_0=a.asm.xa).apply(null,arguments)},Tb=a._emscripten_bind_Encoder___destroy___0=function(){return(Tb=a._emscripten_bind_Encoder___destroy___0=a.asm.ya).apply(null,arguments)},Ea=a._emscripten_bind_ExpertEncoder_ExpertEncoder_1=function(){return(Ea=a._emscripten_bind_ExpertEncoder_ExpertEncoder_1=
a.asm.za).apply(null,arguments)},Ub=a._emscripten_bind_ExpertEncoder_SetEncodingMethod_1=function(){return(Ub=a._emscripten_bind_ExpertEncoder_SetEncodingMethod_1=a.asm.Aa).apply(null,arguments)},Vb=a._emscripten_bind_ExpertEncoder_SetAttributeQuantization_2=function(){return(Vb=a._emscripten_bind_ExpertEncoder_SetAttributeQuantization_2=a.asm.Ba).apply(null,arguments)},Wb=a._emscripten_bind_ExpertEncoder_SetAttributeExplicitQuantization_5=function(){return(Wb=a._emscripten_bind_ExpertEncoder_SetAttributeExplicitQuantization_5=
a.asm.Ca).apply(null,arguments)},Xb=a._emscripten_bind_ExpertEncoder_SetSpeedOptions_2=function(){return(Xb=a._emscripten_bind_ExpertEncoder_SetSpeedOptions_2=a.asm.Da).apply(null,arguments)},Yb=a._emscripten_bind_ExpertEncoder_SetTrackEncodedProperties_1=function(){return(Yb=a._emscripten_bind_ExpertEncoder_SetTrackEncodedProperties_1=a.asm.Ea).apply(null,arguments)},Zb=a._emscripten_bind_ExpertEncoder_EncodeToDracoBuffer_2=function(){return(Zb=a._emscripten_bind_ExpertEncoder_EncodeToDracoBuffer_2=
a.asm.Fa).apply(null,arguments)},$b=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedPoints_0=function(){return($b=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedPoints_0=a.asm.Ga).apply(null,arguments)},ac=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedFaces_0=function(){return(ac=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedFaces_0=a.asm.Ha).apply(null,arguments)},bc=a._emscripten_bind_ExpertEncoder___destroy___0=function(){return(bc=a._emscripten_bind_ExpertEncoder___destroy___0=
a.asm.Ia).apply(null,arguments)},cc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(cc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.Ja).apply(null,arguments)},dc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(dc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=a.asm.Ka).apply(null,arguments)},ec=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(ec=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=
a.asm.La).apply(null,arguments)},fc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(fc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.Ma).apply(null,arguments)},gc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(gc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=a.asm.Na).apply(null,arguments)},hc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(hc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=
a.asm.Oa).apply(null,arguments)},ic=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(ic=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.Pa).apply(null,arguments)},jc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(jc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=a.asm.Qa).apply(null,arguments)},kc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(kc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=
a.asm.Ra).apply(null,arguments)},lc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_SEQUENTIAL_ENCODING=function(){return(lc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_SEQUENTIAL_ENCODING=a.asm.Sa).apply(null,arguments)},mc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_EDGEBREAKER_ENCODING=function(){return(mc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_EDGEBREAKER_ENCODING=a.asm.Ta).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Ua).apply(null,arguments)};a._free=function(){return(a._free=
a.asm.Va).apply(null,arguments)};var pa=function(){return(pa=a.asm.Wa).apply(null,arguments)};a.___start_em_js=19116;a.___stop_em_js=19214;var da;aa=function b(){da||B();da||(aa=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();B();v.prototype=Object.create(v.prototype);v.prototype.constructor=v;v.prototype.__class__=v;v.__cache__={};a.WrapperObject=v;a.getCache=D;a.wrapPointer=I;a.castObject=function(b,c){return I(b.ptr,c)};a.NULL=I(0);
a.destroy=function(b){if(!b.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";b.__destroy__();delete D(b.__class__)[b.ptr]};a.compare=function(b,c){return b.ptr===c.ptr};a.getPointer=function(b){return b.ptr};a.getClass=function(b){return b.__class__};var n={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(n.needed){for(var b=0;b<n.temps.length;b++)a._free(n.temps[b]);n.temps.length=0;a._free(n.buffer);n.buffer=0;n.size+=n.needed;n.needed=0}n.buffer||(n.size+=
128,n.buffer=a._malloc(n.size),n.buffer||k(void 0));n.pos=0},alloc:function(b,c){n.buffer||k(void 0);b=b.length*c.BYTES_PER_ELEMENT;b=b+7&-8;n.pos+b>=n.size?(0<b||k(void 0),n.needed+=b,c=a._malloc(b),n.temps.push(c)):(c=n.buffer+n.pos,n.pos+=b);return c},copy:function(b,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var e=0;e<b.length;e++)c[d+e]=b[e]}};P.prototype=Object.create(v.prototype);P.prototype.constructor=P;P.prototype.__class__=P;P.__cache__=
{};a.VoidPtr=P;P.prototype.__destroy__=P.prototype.__destroy__=function(){La(this.ptr)};M.prototype=Object.create(v.prototype);M.prototype.constructor=M;M.prototype.__class__=M;M.__cache__={};a.GeometryAttribute=M;M.prototype.__destroy__=M.prototype.__destroy__=function(){Ma(this.ptr)};z.prototype=Object.create(v.prototype);z.prototype.constructor=z;z.prototype.__class__=z;z.__cache__={};a.PointAttribute=z;z.prototype.size=z.prototype.size=function(){return Na(this.ptr)};z.prototype.attribute_type=
z.prototype.attribute_type=function(){return Oa(this.ptr)};z.prototype.data_type=z.prototype.data_type=function(){return Pa(this.ptr)};z.prototype.num_components=z.prototype.num_components=function(){return Qa(this.ptr)};z.prototype.normalized=z.prototype.normalized=function(){return!!Ra(this.ptr)};z.prototype.byte_stride=z.prototype.byte_stride=function(){return Sa(this.ptr)};z.prototype.byte_offset=z.prototype.byte_offset=function(){return Ta(this.ptr)};z.prototype.unique_id=z.prototype.unique_id=
function(){return Ua(this.ptr)};z.prototype.__destroy__=z.prototype.__destroy__=function(){Va(this.ptr)};G.prototype=Object.create(v.prototype);G.prototype.constructor=G;G.prototype.__class__=G;G.__cache__={};a.PointCloud=G;G.prototype.num_attributes=G.prototype.num_attributes=function(){return Wa(this.ptr)};G.prototype.num_points=G.prototype.num_points=function(){return Xa(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){Ya(this.ptr)};E.prototype=Object.create(v.prototype);E.prototype.constructor=
E;E.prototype.__class__=E;E.__cache__={};a.Mesh=E;E.prototype.num_faces=E.prototype.num_faces=function(){return Za(this.ptr)};E.prototype.num_attributes=E.prototype.num_attributes=function(){return $a(this.ptr)};E.prototype.num_points=E.prototype.num_points=function(){return ab(this.ptr)};E.prototype.set_num_points=E.prototype.set_num_points=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);bb(c,b)};E.prototype.__destroy__=E.prototype.__destroy__=function(){cb(this.ptr)};N.prototype=Object.create(v.prototype);
N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.Metadata=N;N.prototype.__destroy__=N.prototype.__destroy__=function(){db(this.ptr)};H.prototype=Object.create(v.prototype);H.prototype.constructor=H;H.prototype.__class__=H;H.__cache__={};a.DracoInt8Array=H;H.prototype.GetValue=H.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return eb(c,b)};H.prototype.size=H.prototype.size=function(){return fb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=
function(){gb(this.ptr)};F.prototype=Object.create(v.prototype);F.prototype.constructor=F;F.prototype.__class__=F;F.__cache__={};a.MetadataBuilder=F;F.prototype.AddStringEntry=F.prototype.AddStringEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);d=d&&"object"===typeof d?d.ptr:J(d);return!!hb(e,b,c,d)};F.prototype.AddIntEntry=F.prototype.AddIntEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&
"object"===typeof c?c.ptr:J(c);d&&"object"===typeof d&&(d=d.ptr);return!!ib(e,b,c,d)};F.prototype.AddIntEntryArray=F.prototype.AddIntEntryArray=function(b,c,d,e){var g=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);"object"==typeof d&&(d=V(d));e&&"object"===typeof e&&(e=e.ptr);return!!jb(g,b,c,d,e)};F.prototype.AddDoubleEntry=F.prototype.AddDoubleEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?
c.ptr:J(c);d&&"object"===typeof d&&(d=d.ptr);return!!kb(e,b,c,d)};F.prototype.__destroy__=F.prototype.__destroy__=function(){lb(this.ptr)};x.prototype=Object.create(v.prototype);x.prototype.constructor=x;x.prototype.__class__=x;x.__cache__={};a.PointCloudBuilder=x;x.prototype.AddFloatAttribute=x.prototype.AddFloatAttribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=
e.ptr);"object"==typeof g&&(g=Z(g));return mb(t,b,c,d,e,g)};x.prototype.AddInt8Attribute=x.prototype.AddInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return nb(t,b,c,d,e,g)};x.prototype.AddUInt8Attribute=x.prototype.AddUInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===
typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return ob(t,b,c,d,e,g)};x.prototype.AddInt16Attribute=x.prototype.AddInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return pb(t,b,c,d,e,g)};x.prototype.AddUInt16Attribute=x.prototype.AddUInt16Attribute=function(b,
c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return qb(t,b,c,d,e,g)};x.prototype.AddInt32Attribute=x.prototype.AddInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return rb(t,
b,c,d,e,g)};x.prototype.AddUInt32Attribute=x.prototype.AddUInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return sb(t,b,c,d,e,g)};x.prototype.AddMetadata=x.prototype.AddMetadata=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!tb(d,b,c)};x.prototype.SetMetadataForAttribute=
x.prototype.SetMetadataForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!ub(e,b,c,d)};x.prototype.__destroy__=x.prototype.__destroy__=function(){vb(this.ptr)};u.prototype=Object.create(v.prototype);u.prototype.constructor=u;u.prototype.__class__=u;u.__cache__={};a.MeshBuilder=u;u.prototype.AddFacesToMesh=u.prototype.AddFacesToMesh=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&
(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);"object"==typeof d&&(d=V(d));return!!wb(e,b,c,d)};u.prototype.AddFloatAttributeToMesh=u.prototype.AddFloatAttributeToMesh=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return xb(t,b,c,d,e,g)};u.prototype.AddInt32AttributeToMesh=u.prototype.AddInt32AttributeToMesh=function(b,c,d,e,g){var t=this.ptr;
n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return yb(t,b,c,d,e,g)};u.prototype.AddMetadataToMesh=u.prototype.AddMetadataToMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!zb(d,b,c)};u.prototype.AddFloatAttribute=u.prototype.AddFloatAttribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&
(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return Ab(t,b,c,d,e,g)};u.prototype.AddInt8Attribute=u.prototype.AddInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return Bb(t,b,c,d,e,g)};u.prototype.AddUInt8Attribute=u.prototype.AddUInt8Attribute=
function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return Cb(t,b,c,d,e,g)};u.prototype.AddInt16Attribute=u.prototype.AddInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=
R(g));return Db(t,b,c,d,e,g)};u.prototype.AddUInt16Attribute=u.prototype.AddUInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return Eb(t,b,c,d,e,g)};u.prototype.AddInt32Attribute=u.prototype.AddInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);
d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return Fb(t,b,c,d,e,g)};u.prototype.AddUInt32Attribute=u.prototype.AddUInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return Gb(t,b,c,d,e,g)};u.prototype.AddMetadata=u.prototype.AddMetadata=function(b,c){var d=this.ptr;b&&"object"===
typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!Hb(d,b,c)};u.prototype.SetMetadataForAttribute=u.prototype.SetMetadataForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Ib(e,b,c,d)};u.prototype.__destroy__=u.prototype.__destroy__=function(){Jb(this.ptr)};y.prototype=Object.create(v.prototype);y.prototype.constructor=y;y.prototype.__class__=y;y.__cache__={};a.Encoder=y;y.prototype.SetEncodingMethod=
y.prototype.SetEncodingMethod=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Kb(c,b)};y.prototype.SetAttributeQuantization=y.prototype.SetAttributeQuantization=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Lb(d,b,c)};y.prototype.SetAttributeExplicitQuantization=y.prototype.SetAttributeExplicitQuantization=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
typeof d&&(d=d.ptr);"object"==typeof e&&(e=Z(e));g&&"object"===typeof g&&(g=g.ptr);Mb(t,b,c,d,e,g)};y.prototype.SetSpeedOptions=y.prototype.SetSpeedOptions=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Nb(d,b,c)};y.prototype.SetTrackEncodedProperties=y.prototype.SetTrackEncodedProperties=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Ob(c,b)};y.prototype.EncodeMeshToDracoBuffer=y.prototype.EncodeMeshToDracoBuffer=function(b,c){var d=
this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return Pb(d,b,c)};y.prototype.EncodePointCloudToDracoBuffer=y.prototype.EncodePointCloudToDracoBuffer=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return Qb(e,b,c,d)};y.prototype.GetNumberOfEncodedPoints=y.prototype.GetNumberOfEncodedPoints=function(){return Rb(this.ptr)};y.prototype.GetNumberOfEncodedFaces=y.prototype.GetNumberOfEncodedFaces=
function(){return Sb(this.ptr)};y.prototype.__destroy__=y.prototype.__destroy__=function(){Tb(this.ptr)};A.prototype=Object.create(v.prototype);A.prototype.constructor=A;A.prototype.__class__=A;A.__cache__={};a.ExpertEncoder=A;A.prototype.SetEncodingMethod=A.prototype.SetEncodingMethod=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Ub(c,b)};A.prototype.SetAttributeQuantization=A.prototype.SetAttributeQuantization=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===
typeof c&&(c=c.ptr);Vb(d,b,c)};A.prototype.SetAttributeExplicitQuantization=A.prototype.SetAttributeExplicitQuantization=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);"object"==typeof e&&(e=Z(e));g&&"object"===typeof g&&(g=g.ptr);Wb(t,b,c,d,e,g)};A.prototype.SetSpeedOptions=A.prototype.SetSpeedOptions=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);
Xb(d,b,c)};A.prototype.SetTrackEncodedProperties=A.prototype.SetTrackEncodedProperties=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Yb(c,b)};A.prototype.EncodeToDracoBuffer=A.prototype.EncodeToDracoBuffer=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return Zb(d,b,c)};A.prototype.GetNumberOfEncodedPoints=A.prototype.GetNumberOfEncodedPoints=function(){return $b(this.ptr)};A.prototype.GetNumberOfEncodedFaces=A.prototype.GetNumberOfEncodedFaces=
function(){return ac(this.ptr)};A.prototype.__destroy__=A.prototype.__destroy__=function(){bc(this.ptr)};(function(){function b(){a.INVALID=cc();a.POSITION=dc();a.NORMAL=ec();a.COLOR=fc();a.TEX_COORD=gc();a.GENERIC=hc();a.INVALID_GEOMETRY_TYPE=ic();a.POINT_CLOUD=jc();a.TRIANGULAR_MESH=kc();a.MESH_SEQUENTIAL_ENCODING=lc();a.MESH_EDGEBREAKER_ENCODING=mc()}qa?b():ka.unshift(b)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();return p.ready}}();
"object"===typeof exports&&"object"===typeof module?module.exports=DracoEncoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoEncoderModule}):"object"===typeof exports&&(exports.DracoEncoderModule=DracoEncoderModule);
function(){return(tb=a._emscripten_bind_PointCloudBuilder_AddMetadata_2=a.asm.Y).apply(null,arguments)},ub=a._emscripten_bind_PointCloudBuilder_SetMetadataForAttribute_3=function(){return(ub=a._emscripten_bind_PointCloudBuilder_SetMetadataForAttribute_3=a.asm.Z).apply(null,arguments)},vb=a._emscripten_bind_PointCloudBuilder_SetNormalizedFlagForAttribute_3=function(){return(vb=a._emscripten_bind_PointCloudBuilder_SetNormalizedFlagForAttribute_3=a.asm._).apply(null,arguments)},wb=a._emscripten_bind_PointCloudBuilder___destroy___0=
function(){return(wb=a._emscripten_bind_PointCloudBuilder___destroy___0=a.asm.$).apply(null,arguments)},Ca=a._emscripten_bind_MeshBuilder_MeshBuilder_0=function(){return(Ca=a._emscripten_bind_MeshBuilder_MeshBuilder_0=a.asm.aa).apply(null,arguments)},xb=a._emscripten_bind_MeshBuilder_AddFacesToMesh_3=function(){return(xb=a._emscripten_bind_MeshBuilder_AddFacesToMesh_3=a.asm.ba).apply(null,arguments)},yb=a._emscripten_bind_MeshBuilder_AddFloatAttributeToMesh_5=function(){return(yb=a._emscripten_bind_MeshBuilder_AddFloatAttributeToMesh_5=
a.asm.ca).apply(null,arguments)},zb=a._emscripten_bind_MeshBuilder_AddInt32AttributeToMesh_5=function(){return(zb=a._emscripten_bind_MeshBuilder_AddInt32AttributeToMesh_5=a.asm.da).apply(null,arguments)},Ab=a._emscripten_bind_MeshBuilder_AddMetadataToMesh_2=function(){return(Ab=a._emscripten_bind_MeshBuilder_AddMetadataToMesh_2=a.asm.ea).apply(null,arguments)},Bb=a._emscripten_bind_MeshBuilder_AddFloatAttribute_5=function(){return(Bb=a._emscripten_bind_MeshBuilder_AddFloatAttribute_5=a.asm.fa).apply(null,
arguments)},Cb=a._emscripten_bind_MeshBuilder_AddInt8Attribute_5=function(){return(Cb=a._emscripten_bind_MeshBuilder_AddInt8Attribute_5=a.asm.ga).apply(null,arguments)},Db=a._emscripten_bind_MeshBuilder_AddUInt8Attribute_5=function(){return(Db=a._emscripten_bind_MeshBuilder_AddUInt8Attribute_5=a.asm.ha).apply(null,arguments)},Eb=a._emscripten_bind_MeshBuilder_AddInt16Attribute_5=function(){return(Eb=a._emscripten_bind_MeshBuilder_AddInt16Attribute_5=a.asm.ia).apply(null,arguments)},Fb=a._emscripten_bind_MeshBuilder_AddUInt16Attribute_5=
function(){return(Fb=a._emscripten_bind_MeshBuilder_AddUInt16Attribute_5=a.asm.ja).apply(null,arguments)},Gb=a._emscripten_bind_MeshBuilder_AddInt32Attribute_5=function(){return(Gb=a._emscripten_bind_MeshBuilder_AddInt32Attribute_5=a.asm.ka).apply(null,arguments)},Hb=a._emscripten_bind_MeshBuilder_AddUInt32Attribute_5=function(){return(Hb=a._emscripten_bind_MeshBuilder_AddUInt32Attribute_5=a.asm.la).apply(null,arguments)},Ib=a._emscripten_bind_MeshBuilder_AddMetadata_2=function(){return(Ib=a._emscripten_bind_MeshBuilder_AddMetadata_2=
a.asm.ma).apply(null,arguments)},Jb=a._emscripten_bind_MeshBuilder_SetMetadataForAttribute_3=function(){return(Jb=a._emscripten_bind_MeshBuilder_SetMetadataForAttribute_3=a.asm.na).apply(null,arguments)},Kb=a._emscripten_bind_MeshBuilder_SetNormalizedFlagForAttribute_3=function(){return(Kb=a._emscripten_bind_MeshBuilder_SetNormalizedFlagForAttribute_3=a.asm.oa).apply(null,arguments)},Lb=a._emscripten_bind_MeshBuilder___destroy___0=function(){return(Lb=a._emscripten_bind_MeshBuilder___destroy___0=
a.asm.pa).apply(null,arguments)},Da=a._emscripten_bind_Encoder_Encoder_0=function(){return(Da=a._emscripten_bind_Encoder_Encoder_0=a.asm.qa).apply(null,arguments)},Mb=a._emscripten_bind_Encoder_SetEncodingMethod_1=function(){return(Mb=a._emscripten_bind_Encoder_SetEncodingMethod_1=a.asm.ra).apply(null,arguments)},Nb=a._emscripten_bind_Encoder_SetAttributeQuantization_2=function(){return(Nb=a._emscripten_bind_Encoder_SetAttributeQuantization_2=a.asm.sa).apply(null,arguments)},Ob=a._emscripten_bind_Encoder_SetAttributeExplicitQuantization_5=
function(){return(Ob=a._emscripten_bind_Encoder_SetAttributeExplicitQuantization_5=a.asm.ta).apply(null,arguments)},Pb=a._emscripten_bind_Encoder_SetSpeedOptions_2=function(){return(Pb=a._emscripten_bind_Encoder_SetSpeedOptions_2=a.asm.ua).apply(null,arguments)},Qb=a._emscripten_bind_Encoder_SetTrackEncodedProperties_1=function(){return(Qb=a._emscripten_bind_Encoder_SetTrackEncodedProperties_1=a.asm.va).apply(null,arguments)},Rb=a._emscripten_bind_Encoder_EncodeMeshToDracoBuffer_2=function(){return(Rb=
a._emscripten_bind_Encoder_EncodeMeshToDracoBuffer_2=a.asm.wa).apply(null,arguments)},Sb=a._emscripten_bind_Encoder_EncodePointCloudToDracoBuffer_3=function(){return(Sb=a._emscripten_bind_Encoder_EncodePointCloudToDracoBuffer_3=a.asm.xa).apply(null,arguments)},Tb=a._emscripten_bind_Encoder_GetNumberOfEncodedPoints_0=function(){return(Tb=a._emscripten_bind_Encoder_GetNumberOfEncodedPoints_0=a.asm.ya).apply(null,arguments)},Ub=a._emscripten_bind_Encoder_GetNumberOfEncodedFaces_0=function(){return(Ub=
a._emscripten_bind_Encoder_GetNumberOfEncodedFaces_0=a.asm.za).apply(null,arguments)},Vb=a._emscripten_bind_Encoder___destroy___0=function(){return(Vb=a._emscripten_bind_Encoder___destroy___0=a.asm.Aa).apply(null,arguments)},Ea=a._emscripten_bind_ExpertEncoder_ExpertEncoder_1=function(){return(Ea=a._emscripten_bind_ExpertEncoder_ExpertEncoder_1=a.asm.Ba).apply(null,arguments)},Wb=a._emscripten_bind_ExpertEncoder_SetEncodingMethod_1=function(){return(Wb=a._emscripten_bind_ExpertEncoder_SetEncodingMethod_1=
a.asm.Ca).apply(null,arguments)},Xb=a._emscripten_bind_ExpertEncoder_SetAttributeQuantization_2=function(){return(Xb=a._emscripten_bind_ExpertEncoder_SetAttributeQuantization_2=a.asm.Da).apply(null,arguments)},Yb=a._emscripten_bind_ExpertEncoder_SetAttributeExplicitQuantization_5=function(){return(Yb=a._emscripten_bind_ExpertEncoder_SetAttributeExplicitQuantization_5=a.asm.Ea).apply(null,arguments)},Zb=a._emscripten_bind_ExpertEncoder_SetSpeedOptions_2=function(){return(Zb=a._emscripten_bind_ExpertEncoder_SetSpeedOptions_2=
a.asm.Fa).apply(null,arguments)},$b=a._emscripten_bind_ExpertEncoder_SetTrackEncodedProperties_1=function(){return($b=a._emscripten_bind_ExpertEncoder_SetTrackEncodedProperties_1=a.asm.Ga).apply(null,arguments)},ac=a._emscripten_bind_ExpertEncoder_EncodeToDracoBuffer_2=function(){return(ac=a._emscripten_bind_ExpertEncoder_EncodeToDracoBuffer_2=a.asm.Ha).apply(null,arguments)},bc=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedPoints_0=function(){return(bc=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedPoints_0=
a.asm.Ia).apply(null,arguments)},cc=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedFaces_0=function(){return(cc=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedFaces_0=a.asm.Ja).apply(null,arguments)},dc=a._emscripten_bind_ExpertEncoder___destroy___0=function(){return(dc=a._emscripten_bind_ExpertEncoder___destroy___0=a.asm.Ka).apply(null,arguments)},ec=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(ec=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.La).apply(null,
arguments)},fc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(fc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=a.asm.Ma).apply(null,arguments)},gc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(gc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.Na).apply(null,arguments)},hc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(hc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.Oa).apply(null,
arguments)},ic=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(ic=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=a.asm.Pa).apply(null,arguments)},jc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(jc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.Qa).apply(null,arguments)},kc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(kc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=
a.asm.Ra).apply(null,arguments)},lc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(lc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=a.asm.Sa).apply(null,arguments)},mc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(mc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.Ta).apply(null,arguments)},nc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_SEQUENTIAL_ENCODING=function(){return(nc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_SEQUENTIAL_ENCODING=
a.asm.Ua).apply(null,arguments)},oc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_EDGEBREAKER_ENCODING=function(){return(oc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_EDGEBREAKER_ENCODING=a.asm.Va).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Wa).apply(null,arguments)};a._free=function(){return(a._free=a.asm.Xa).apply(null,arguments)};var pa=function(){return(pa=a.asm.Ya).apply(null,arguments)};a.___start_em_js=19116;a.___stop_em_js=19214;var da;aa=function b(){da||B();
da||(aa=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();B();v.prototype=Object.create(v.prototype);v.prototype.constructor=v;v.prototype.__class__=v;v.__cache__={};a.WrapperObject=v;a.getCache=D;a.wrapPointer=I;a.castObject=function(b,c){return I(b.ptr,c)};a.NULL=I(0);a.destroy=function(b){if(!b.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";b.__destroy__();delete D(b.__class__)[b.ptr]};a.compare=function(b,
c){return b.ptr===c.ptr};a.getPointer=function(b){return b.ptr};a.getClass=function(b){return b.__class__};var n={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(n.needed){for(var b=0;b<n.temps.length;b++)a._free(n.temps[b]);n.temps.length=0;a._free(n.buffer);n.buffer=0;n.size+=n.needed;n.needed=0}n.buffer||(n.size+=128,n.buffer=a._malloc(n.size),n.buffer||k(void 0));n.pos=0},alloc:function(b,c){n.buffer||k(void 0);b=b.length*c.BYTES_PER_ELEMENT;b=b+7&-8;n.pos+b>=n.size?(0<b||k(void 0),
n.needed+=b,c=a._malloc(b),n.temps.push(c)):(c=n.buffer+n.pos,n.pos+=b);return c},copy:function(b,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var e=0;e<b.length;e++)c[d+e]=b[e]}};P.prototype=Object.create(v.prototype);P.prototype.constructor=P;P.prototype.__class__=P;P.__cache__={};a.VoidPtr=P;P.prototype.__destroy__=P.prototype.__destroy__=function(){La(this.ptr)};M.prototype=Object.create(v.prototype);M.prototype.constructor=M;M.prototype.__class__=
M;M.__cache__={};a.GeometryAttribute=M;M.prototype.__destroy__=M.prototype.__destroy__=function(){Ma(this.ptr)};z.prototype=Object.create(v.prototype);z.prototype.constructor=z;z.prototype.__class__=z;z.__cache__={};a.PointAttribute=z;z.prototype.size=z.prototype.size=function(){return Na(this.ptr)};z.prototype.attribute_type=z.prototype.attribute_type=function(){return Oa(this.ptr)};z.prototype.data_type=z.prototype.data_type=function(){return Pa(this.ptr)};z.prototype.num_components=z.prototype.num_components=
function(){return Qa(this.ptr)};z.prototype.normalized=z.prototype.normalized=function(){return!!Ra(this.ptr)};z.prototype.byte_stride=z.prototype.byte_stride=function(){return Sa(this.ptr)};z.prototype.byte_offset=z.prototype.byte_offset=function(){return Ta(this.ptr)};z.prototype.unique_id=z.prototype.unique_id=function(){return Ua(this.ptr)};z.prototype.__destroy__=z.prototype.__destroy__=function(){Va(this.ptr)};G.prototype=Object.create(v.prototype);G.prototype.constructor=G;G.prototype.__class__=
G;G.__cache__={};a.PointCloud=G;G.prototype.num_attributes=G.prototype.num_attributes=function(){return Wa(this.ptr)};G.prototype.num_points=G.prototype.num_points=function(){return Xa(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){Ya(this.ptr)};E.prototype=Object.create(v.prototype);E.prototype.constructor=E;E.prototype.__class__=E;E.__cache__={};a.Mesh=E;E.prototype.num_faces=E.prototype.num_faces=function(){return Za(this.ptr)};E.prototype.num_attributes=E.prototype.num_attributes=
function(){return $a(this.ptr)};E.prototype.num_points=E.prototype.num_points=function(){return ab(this.ptr)};E.prototype.set_num_points=E.prototype.set_num_points=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);bb(c,b)};E.prototype.__destroy__=E.prototype.__destroy__=function(){cb(this.ptr)};N.prototype=Object.create(v.prototype);N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.Metadata=N;N.prototype.__destroy__=N.prototype.__destroy__=function(){db(this.ptr)};H.prototype=
Object.create(v.prototype);H.prototype.constructor=H;H.prototype.__class__=H;H.__cache__={};a.DracoInt8Array=H;H.prototype.GetValue=H.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return eb(c,b)};H.prototype.size=H.prototype.size=function(){return fb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=function(){gb(this.ptr)};F.prototype=Object.create(v.prototype);F.prototype.constructor=F;F.prototype.__class__=F;F.__cache__={};a.MetadataBuilder=F;F.prototype.AddStringEntry=
F.prototype.AddStringEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);d=d&&"object"===typeof d?d.ptr:J(d);return!!hb(e,b,c,d)};F.prototype.AddIntEntry=F.prototype.AddIntEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);d&&"object"===typeof d&&(d=d.ptr);return!!ib(e,b,c,d)};F.prototype.AddIntEntryArray=F.prototype.AddIntEntryArray=function(b,c,d,e){var g=this.ptr;
n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);"object"==typeof d&&(d=V(d));e&&"object"===typeof e&&(e=e.ptr);return!!jb(g,b,c,d,e)};F.prototype.AddDoubleEntry=F.prototype.AddDoubleEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);d&&"object"===typeof d&&(d=d.ptr);return!!kb(e,b,c,d)};F.prototype.__destroy__=F.prototype.__destroy__=function(){lb(this.ptr)};x.prototype=Object.create(v.prototype);
x.prototype.constructor=x;x.prototype.__class__=x;x.__cache__={};a.PointCloudBuilder=x;x.prototype.AddFloatAttribute=x.prototype.AddFloatAttribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return mb(t,b,c,d,e,g)};x.prototype.AddInt8Attribute=x.prototype.AddInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===
typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return nb(t,b,c,d,e,g)};x.prototype.AddUInt8Attribute=x.prototype.AddUInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return ob(t,b,c,d,e,g)};x.prototype.AddInt16Attribute=
x.prototype.AddInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return pb(t,b,c,d,e,g)};x.prototype.AddUInt16Attribute=x.prototype.AddUInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&
(e=e.ptr);"object"==typeof g&&(g=R(g));return qb(t,b,c,d,e,g)};x.prototype.AddInt32Attribute=x.prototype.AddInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return rb(t,b,c,d,e,g)};x.prototype.AddUInt32Attribute=x.prototype.AddUInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);
c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return sb(t,b,c,d,e,g)};x.prototype.AddMetadata=x.prototype.AddMetadata=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!tb(d,b,c)};x.prototype.SetMetadataForAttribute=x.prototype.SetMetadataForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
typeof d&&(d=d.ptr);return!!ub(e,b,c,d)};x.prototype.SetNormalizedFlagForAttribute=x.prototype.SetNormalizedFlagForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!vb(e,b,c,d)};x.prototype.__destroy__=x.prototype.__destroy__=function(){wb(this.ptr)};u.prototype=Object.create(v.prototype);u.prototype.constructor=u;u.prototype.__class__=u;u.__cache__={};a.MeshBuilder=u;u.prototype.AddFacesToMesh=u.prototype.AddFacesToMesh=
function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);"object"==typeof d&&(d=V(d));return!!xb(e,b,c,d)};u.prototype.AddFloatAttributeToMesh=u.prototype.AddFloatAttributeToMesh=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return yb(t,b,c,d,e,g)};u.prototype.AddInt32AttributeToMesh=
u.prototype.AddInt32AttributeToMesh=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return zb(t,b,c,d,e,g)};u.prototype.AddMetadataToMesh=u.prototype.AddMetadataToMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!Ab(d,b,c)};u.prototype.AddFloatAttribute=u.prototype.AddFloatAttribute=
function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return Bb(t,b,c,d,e,g)};u.prototype.AddInt8Attribute=u.prototype.AddInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));
return Cb(t,b,c,d,e,g)};u.prototype.AddUInt8Attribute=u.prototype.AddUInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return Db(t,b,c,d,e,g)};u.prototype.AddInt16Attribute=u.prototype.AddInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return Eb(t,b,c,d,e,g)};u.prototype.AddUInt16Attribute=u.prototype.AddUInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return Fb(t,b,c,d,e,g)};u.prototype.AddInt32Attribute=u.prototype.AddInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();
b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return Gb(t,b,c,d,e,g)};u.prototype.AddUInt32Attribute=u.prototype.AddUInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return Hb(t,b,c,d,e,g)};u.prototype.AddMetadata=
u.prototype.AddMetadata=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!Ib(d,b,c)};u.prototype.SetMetadataForAttribute=u.prototype.SetMetadataForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Jb(e,b,c,d)};u.prototype.SetNormalizedFlagForAttribute=u.prototype.SetNormalizedFlagForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&
(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Kb(e,b,c,d)};u.prototype.__destroy__=u.prototype.__destroy__=function(){Lb(this.ptr)};y.prototype=Object.create(v.prototype);y.prototype.constructor=y;y.prototype.__class__=y;y.__cache__={};a.Encoder=y;y.prototype.SetEncodingMethod=y.prototype.SetEncodingMethod=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Mb(c,b)};y.prototype.SetAttributeQuantization=y.prototype.SetAttributeQuantization=function(b,
c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Nb(d,b,c)};y.prototype.SetAttributeExplicitQuantization=y.prototype.SetAttributeExplicitQuantization=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);"object"==typeof e&&(e=Z(e));g&&"object"===typeof g&&(g=g.ptr);Ob(t,b,c,d,e,g)};y.prototype.SetSpeedOptions=y.prototype.SetSpeedOptions=function(b,c){var d=this.ptr;
b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Pb(d,b,c)};y.prototype.SetTrackEncodedProperties=y.prototype.SetTrackEncodedProperties=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Qb(c,b)};y.prototype.EncodeMeshToDracoBuffer=y.prototype.EncodeMeshToDracoBuffer=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return Rb(d,b,c)};y.prototype.EncodePointCloudToDracoBuffer=y.prototype.EncodePointCloudToDracoBuffer=function(b,
c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return Sb(e,b,c,d)};y.prototype.GetNumberOfEncodedPoints=y.prototype.GetNumberOfEncodedPoints=function(){return Tb(this.ptr)};y.prototype.GetNumberOfEncodedFaces=y.prototype.GetNumberOfEncodedFaces=function(){return Ub(this.ptr)};y.prototype.__destroy__=y.prototype.__destroy__=function(){Vb(this.ptr)};A.prototype=Object.create(v.prototype);A.prototype.constructor=A;A.prototype.__class__=
A;A.__cache__={};a.ExpertEncoder=A;A.prototype.SetEncodingMethod=A.prototype.SetEncodingMethod=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Wb(c,b)};A.prototype.SetAttributeQuantization=A.prototype.SetAttributeQuantization=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Xb(d,b,c)};A.prototype.SetAttributeExplicitQuantization=A.prototype.SetAttributeExplicitQuantization=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&
(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);"object"==typeof e&&(e=Z(e));g&&"object"===typeof g&&(g=g.ptr);Yb(t,b,c,d,e,g)};A.prototype.SetSpeedOptions=A.prototype.SetSpeedOptions=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Zb(d,b,c)};A.prototype.SetTrackEncodedProperties=A.prototype.SetTrackEncodedProperties=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);$b(c,b)};A.prototype.EncodeToDracoBuffer=A.prototype.EncodeToDracoBuffer=
function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return ac(d,b,c)};A.prototype.GetNumberOfEncodedPoints=A.prototype.GetNumberOfEncodedPoints=function(){return bc(this.ptr)};A.prototype.GetNumberOfEncodedFaces=A.prototype.GetNumberOfEncodedFaces=function(){return cc(this.ptr)};A.prototype.__destroy__=A.prototype.__destroy__=function(){dc(this.ptr)};(function(){function b(){a.INVALID=ec();a.POSITION=fc();a.NORMAL=gc();a.COLOR=hc();a.TEX_COORD=ic();a.GENERIC=
jc();a.INVALID_GEOMETRY_TYPE=kc();a.POINT_CLOUD=lc();a.TRIANGULAR_MESH=mc();a.MESH_SEQUENTIAL_ENCODING=nc();a.MESH_EDGEBREAKER_ENCODING=oc()}qa?b():ka.unshift(b)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();return p.ready}}();"object"===typeof exports&&"object"===typeof module?module.exports=DracoEncoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoEncoderModule}):"object"===typeof exports&&(exports.DracoEncoderModule=DracoEncoderModule);

View File

@ -1,6 +1,6 @@
{
"name": "draco3d",
"version": "1.5.6",
"version": "1.5.7",
"description": "Draco is a library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics.",
"main": "draco3d.js",
"scripts": {

View File

@ -24,7 +24,7 @@ Draco github glTF branch URL: https://github.com/google/draco/tree/gltf_2.0_drac
News
=======
Check out the [README](https://github.com/google/draco/blob/1.5.6/README.md)
Check out the [README](https://github.com/google/draco/blob/1.5.7/README.md)
file for news about this release.
NPM Package

View File

@ -32,9 +32,9 @@ D(z)[this.ptr]=this}function G(){this.ptr=wa();D(G)[this.ptr]=this}function E(){
typeof window,Y="function"==typeof importScripts,Ia="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,L="";if(Ia){var Ja=require("fs"),la=require("path");L=Y?la.dirname(L)+"/":__dirname+"/";var Ka=function(f,b){f=f.startsWith("file://")?new URL(f):la.normalize(f);return Ja.readFileSync(f,b?void 0:"utf8")};var ia=function(f){f=Ka(f,!0);f.buffer||(f=new Uint8Array(f));return f};var ja=function(f,b,c){f=f.startsWith("file://")?new URL(f):la.normalize(f);
Ja.readFile(f,function(d,e){d?c(d):b(e.buffer)})};1<process.argv.length&&process.argv[1].replace(/\\/g,"/");process.argv.slice(2);a.inspect=function(){return"[Emscripten Module object]"}}else if(oa||Y)Y?L=self.location.href:"undefined"!=typeof document&&document.currentScript&&(L=document.currentScript.src),l&&(L=l),L=0!==L.indexOf("blob:")?L.substr(0,L.replace(/[?#].*/,"").lastIndexOf("/")+1):"",Ka=function(f){var b=new XMLHttpRequest;b.open("GET",f,!1);b.send(null);return b.responseText},Y&&(ia=
function(f){var b=new XMLHttpRequest;b.open("GET",f,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),ja=function(f,b,c){var d=new XMLHttpRequest;d.open("GET",f,!0);d.responseType="arraybuffer";d.onload=function(){200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};a.print||console.log.bind(console);var W=a.printErr||console.warn.bind(console);Object.assign(a,Ha);Ha=null;var X;a.wasmBinary&&(X=a.wasmBinary);"object"!=typeof WebAssembly&&
k("no native wasm support detected");var ba,na=!1,O,fa,ea,S,T,ha,ta=[],ka=[],sa=[],qa=!1,U=0,ma=null,aa=null;var K="draco_encoder.wasm";K.startsWith("data:application/octet-stream;base64,")||(K=m(K));var nc=0,oc={b:function(f,b,c){(new w(f)).init(b,c);nc++;throw f;},a:function(){k("")},d:function(f,b,c){fa.copyWithin(f,b,b+c)},c:function(f){var b=fa.length;f>>>=0;if(2147483648<f)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,f+100663296);var e=Math;d=Math.max(f,d);e=e.min.call(e,2147483648,
k("no native wasm support detected");var ba,na=!1,O,fa,ea,S,T,ha,ta=[],ka=[],sa=[],qa=!1,U=0,ma=null,aa=null;var K="draco_encoder.wasm";K.startsWith("data:application/octet-stream;base64,")||(K=m(K));var pc=0,qc={b:function(f,b,c){(new w(f)).init(b,c);pc++;throw f;},a:function(){k("")},d:function(f,b,c){fa.copyWithin(f,b,b+c)},c:function(f){var b=fa.length;f>>>=0;if(2147483648<f)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,f+100663296);var e=Math;d=Math.max(f,d);e=e.min.call(e,2147483648,
d+(65536-d%65536)%65536);a:{d=ba.buffer;try{ba.grow(e-d.byteLength+65535>>>16);r();var g=1;break a}catch(t){}g=void 0}if(g)return!0}return!1}};(function(){function f(e,g){a.asm=e.exports;ba=a.asm.e;r();ka.unshift(a.asm.f);U--;a.monitorRunDependencies&&a.monitorRunDependencies(U);0==U&&(null!==ma&&(clearInterval(ma),ma=null),aa&&(e=aa,aa=null,e()))}function b(e){f(e.instance)}function c(e){return h().then(function(g){return WebAssembly.instantiate(g,d)}).then(function(g){return g}).then(e,function(g){W("failed to asynchronously prepare wasm: "+
g);k(g)})}var d={a:oc};U++;a.monitorRunDependencies&&a.monitorRunDependencies(U);if(a.instantiateWasm)try{return a.instantiateWasm(d,f)}catch(e){W("Module.instantiateWasm callback failed with error: "+e),ca(e)}(function(){return X||"function"!=typeof WebAssembly.instantiateStreaming||K.startsWith("data:application/octet-stream;base64,")||K.startsWith("file://")||Ia||"function"!=typeof fetch?c(b):fetch(K,{credentials:"same-origin"}).then(function(e){return WebAssembly.instantiateStreaming(e,d).then(b,
g);k(g)})}var d={a:qc};U++;a.monitorRunDependencies&&a.monitorRunDependencies(U);if(a.instantiateWasm)try{return a.instantiateWasm(d,f)}catch(e){W("Module.instantiateWasm callback failed with error: "+e),ca(e)}(function(){return X||"function"!=typeof WebAssembly.instantiateStreaming||K.startsWith("data:application/octet-stream;base64,")||K.startsWith("file://")||Ia||"function"!=typeof fetch?c(b):fetch(K,{credentials:"same-origin"}).then(function(e){return WebAssembly.instantiateStreaming(e,d).then(b,
function(g){W("wasm streaming compile failed: "+g);W("falling back to ArrayBuffer instantiation");return c(b)})})})().catch(ca);return{}})();var La=a._emscripten_bind_VoidPtr___destroy___0=function(){return(La=a._emscripten_bind_VoidPtr___destroy___0=a.asm.h).apply(null,arguments)},ua=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(ua=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=a.asm.i).apply(null,arguments)},Ma=a._emscripten_bind_GeometryAttribute___destroy___0=
function(){return(Ma=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.j).apply(null,arguments)},va=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(va=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.k).apply(null,arguments)},Na=a._emscripten_bind_PointAttribute_size_0=function(){return(Na=a._emscripten_bind_PointAttribute_size_0=a.asm.l).apply(null,arguments)},Oa=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(Oa=a._emscripten_bind_PointAttribute_attribute_type_0=
a.asm.m).apply(null,arguments)},Pa=a._emscripten_bind_PointAttribute_data_type_0=function(){return(Pa=a._emscripten_bind_PointAttribute_data_type_0=a.asm.n).apply(null,arguments)},Qa=a._emscripten_bind_PointAttribute_num_components_0=function(){return(Qa=a._emscripten_bind_PointAttribute_num_components_0=a.asm.o).apply(null,arguments)},Ra=a._emscripten_bind_PointAttribute_normalized_0=function(){return(Ra=a._emscripten_bind_PointAttribute_normalized_0=a.asm.p).apply(null,arguments)},Sa=a._emscripten_bind_PointAttribute_byte_stride_0=
@ -48,50 +48,51 @@ ib=a._emscripten_bind_MetadataBuilder_AddIntEntry_3=function(){return(ib=a._emsc
function(){return(lb=a._emscripten_bind_MetadataBuilder___destroy___0=a.asm.P).apply(null,arguments)},Ba=a._emscripten_bind_PointCloudBuilder_PointCloudBuilder_0=function(){return(Ba=a._emscripten_bind_PointCloudBuilder_PointCloudBuilder_0=a.asm.Q).apply(null,arguments)},mb=a._emscripten_bind_PointCloudBuilder_AddFloatAttribute_5=function(){return(mb=a._emscripten_bind_PointCloudBuilder_AddFloatAttribute_5=a.asm.R).apply(null,arguments)},nb=a._emscripten_bind_PointCloudBuilder_AddInt8Attribute_5=
function(){return(nb=a._emscripten_bind_PointCloudBuilder_AddInt8Attribute_5=a.asm.S).apply(null,arguments)},ob=a._emscripten_bind_PointCloudBuilder_AddUInt8Attribute_5=function(){return(ob=a._emscripten_bind_PointCloudBuilder_AddUInt8Attribute_5=a.asm.T).apply(null,arguments)},pb=a._emscripten_bind_PointCloudBuilder_AddInt16Attribute_5=function(){return(pb=a._emscripten_bind_PointCloudBuilder_AddInt16Attribute_5=a.asm.U).apply(null,arguments)},qb=a._emscripten_bind_PointCloudBuilder_AddUInt16Attribute_5=
function(){return(qb=a._emscripten_bind_PointCloudBuilder_AddUInt16Attribute_5=a.asm.V).apply(null,arguments)},rb=a._emscripten_bind_PointCloudBuilder_AddInt32Attribute_5=function(){return(rb=a._emscripten_bind_PointCloudBuilder_AddInt32Attribute_5=a.asm.W).apply(null,arguments)},sb=a._emscripten_bind_PointCloudBuilder_AddUInt32Attribute_5=function(){return(sb=a._emscripten_bind_PointCloudBuilder_AddUInt32Attribute_5=a.asm.X).apply(null,arguments)},tb=a._emscripten_bind_PointCloudBuilder_AddMetadata_2=
function(){return(tb=a._emscripten_bind_PointCloudBuilder_AddMetadata_2=a.asm.Y).apply(null,arguments)},ub=a._emscripten_bind_PointCloudBuilder_SetMetadataForAttribute_3=function(){return(ub=a._emscripten_bind_PointCloudBuilder_SetMetadataForAttribute_3=a.asm.Z).apply(null,arguments)},vb=a._emscripten_bind_PointCloudBuilder___destroy___0=function(){return(vb=a._emscripten_bind_PointCloudBuilder___destroy___0=a.asm._).apply(null,arguments)},Ca=a._emscripten_bind_MeshBuilder_MeshBuilder_0=function(){return(Ca=
a._emscripten_bind_MeshBuilder_MeshBuilder_0=a.asm.$).apply(null,arguments)},wb=a._emscripten_bind_MeshBuilder_AddFacesToMesh_3=function(){return(wb=a._emscripten_bind_MeshBuilder_AddFacesToMesh_3=a.asm.aa).apply(null,arguments)},xb=a._emscripten_bind_MeshBuilder_AddFloatAttributeToMesh_5=function(){return(xb=a._emscripten_bind_MeshBuilder_AddFloatAttributeToMesh_5=a.asm.ba).apply(null,arguments)},yb=a._emscripten_bind_MeshBuilder_AddInt32AttributeToMesh_5=function(){return(yb=a._emscripten_bind_MeshBuilder_AddInt32AttributeToMesh_5=
a.asm.ca).apply(null,arguments)},zb=a._emscripten_bind_MeshBuilder_AddMetadataToMesh_2=function(){return(zb=a._emscripten_bind_MeshBuilder_AddMetadataToMesh_2=a.asm.da).apply(null,arguments)},Ab=a._emscripten_bind_MeshBuilder_AddFloatAttribute_5=function(){return(Ab=a._emscripten_bind_MeshBuilder_AddFloatAttribute_5=a.asm.ea).apply(null,arguments)},Bb=a._emscripten_bind_MeshBuilder_AddInt8Attribute_5=function(){return(Bb=a._emscripten_bind_MeshBuilder_AddInt8Attribute_5=a.asm.fa).apply(null,arguments)},
Cb=a._emscripten_bind_MeshBuilder_AddUInt8Attribute_5=function(){return(Cb=a._emscripten_bind_MeshBuilder_AddUInt8Attribute_5=a.asm.ga).apply(null,arguments)},Db=a._emscripten_bind_MeshBuilder_AddInt16Attribute_5=function(){return(Db=a._emscripten_bind_MeshBuilder_AddInt16Attribute_5=a.asm.ha).apply(null,arguments)},Eb=a._emscripten_bind_MeshBuilder_AddUInt16Attribute_5=function(){return(Eb=a._emscripten_bind_MeshBuilder_AddUInt16Attribute_5=a.asm.ia).apply(null,arguments)},Fb=a._emscripten_bind_MeshBuilder_AddInt32Attribute_5=
function(){return(Fb=a._emscripten_bind_MeshBuilder_AddInt32Attribute_5=a.asm.ja).apply(null,arguments)},Gb=a._emscripten_bind_MeshBuilder_AddUInt32Attribute_5=function(){return(Gb=a._emscripten_bind_MeshBuilder_AddUInt32Attribute_5=a.asm.ka).apply(null,arguments)},Hb=a._emscripten_bind_MeshBuilder_AddMetadata_2=function(){return(Hb=a._emscripten_bind_MeshBuilder_AddMetadata_2=a.asm.la).apply(null,arguments)},Ib=a._emscripten_bind_MeshBuilder_SetMetadataForAttribute_3=function(){return(Ib=a._emscripten_bind_MeshBuilder_SetMetadataForAttribute_3=
a.asm.ma).apply(null,arguments)},Jb=a._emscripten_bind_MeshBuilder___destroy___0=function(){return(Jb=a._emscripten_bind_MeshBuilder___destroy___0=a.asm.na).apply(null,arguments)},Da=a._emscripten_bind_Encoder_Encoder_0=function(){return(Da=a._emscripten_bind_Encoder_Encoder_0=a.asm.oa).apply(null,arguments)},Kb=a._emscripten_bind_Encoder_SetEncodingMethod_1=function(){return(Kb=a._emscripten_bind_Encoder_SetEncodingMethod_1=a.asm.pa).apply(null,arguments)},Lb=a._emscripten_bind_Encoder_SetAttributeQuantization_2=
function(){return(Lb=a._emscripten_bind_Encoder_SetAttributeQuantization_2=a.asm.qa).apply(null,arguments)},Mb=a._emscripten_bind_Encoder_SetAttributeExplicitQuantization_5=function(){return(Mb=a._emscripten_bind_Encoder_SetAttributeExplicitQuantization_5=a.asm.ra).apply(null,arguments)},Nb=a._emscripten_bind_Encoder_SetSpeedOptions_2=function(){return(Nb=a._emscripten_bind_Encoder_SetSpeedOptions_2=a.asm.sa).apply(null,arguments)},Ob=a._emscripten_bind_Encoder_SetTrackEncodedProperties_1=function(){return(Ob=
a._emscripten_bind_Encoder_SetTrackEncodedProperties_1=a.asm.ta).apply(null,arguments)},Pb=a._emscripten_bind_Encoder_EncodeMeshToDracoBuffer_2=function(){return(Pb=a._emscripten_bind_Encoder_EncodeMeshToDracoBuffer_2=a.asm.ua).apply(null,arguments)},Qb=a._emscripten_bind_Encoder_EncodePointCloudToDracoBuffer_3=function(){return(Qb=a._emscripten_bind_Encoder_EncodePointCloudToDracoBuffer_3=a.asm.va).apply(null,arguments)},Rb=a._emscripten_bind_Encoder_GetNumberOfEncodedPoints_0=function(){return(Rb=
a._emscripten_bind_Encoder_GetNumberOfEncodedPoints_0=a.asm.wa).apply(null,arguments)},Sb=a._emscripten_bind_Encoder_GetNumberOfEncodedFaces_0=function(){return(Sb=a._emscripten_bind_Encoder_GetNumberOfEncodedFaces_0=a.asm.xa).apply(null,arguments)},Tb=a._emscripten_bind_Encoder___destroy___0=function(){return(Tb=a._emscripten_bind_Encoder___destroy___0=a.asm.ya).apply(null,arguments)},Ea=a._emscripten_bind_ExpertEncoder_ExpertEncoder_1=function(){return(Ea=a._emscripten_bind_ExpertEncoder_ExpertEncoder_1=
a.asm.za).apply(null,arguments)},Ub=a._emscripten_bind_ExpertEncoder_SetEncodingMethod_1=function(){return(Ub=a._emscripten_bind_ExpertEncoder_SetEncodingMethod_1=a.asm.Aa).apply(null,arguments)},Vb=a._emscripten_bind_ExpertEncoder_SetAttributeQuantization_2=function(){return(Vb=a._emscripten_bind_ExpertEncoder_SetAttributeQuantization_2=a.asm.Ba).apply(null,arguments)},Wb=a._emscripten_bind_ExpertEncoder_SetAttributeExplicitQuantization_5=function(){return(Wb=a._emscripten_bind_ExpertEncoder_SetAttributeExplicitQuantization_5=
a.asm.Ca).apply(null,arguments)},Xb=a._emscripten_bind_ExpertEncoder_SetSpeedOptions_2=function(){return(Xb=a._emscripten_bind_ExpertEncoder_SetSpeedOptions_2=a.asm.Da).apply(null,arguments)},Yb=a._emscripten_bind_ExpertEncoder_SetTrackEncodedProperties_1=function(){return(Yb=a._emscripten_bind_ExpertEncoder_SetTrackEncodedProperties_1=a.asm.Ea).apply(null,arguments)},Zb=a._emscripten_bind_ExpertEncoder_EncodeToDracoBuffer_2=function(){return(Zb=a._emscripten_bind_ExpertEncoder_EncodeToDracoBuffer_2=
a.asm.Fa).apply(null,arguments)},$b=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedPoints_0=function(){return($b=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedPoints_0=a.asm.Ga).apply(null,arguments)},ac=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedFaces_0=function(){return(ac=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedFaces_0=a.asm.Ha).apply(null,arguments)},bc=a._emscripten_bind_ExpertEncoder___destroy___0=function(){return(bc=a._emscripten_bind_ExpertEncoder___destroy___0=
a.asm.Ia).apply(null,arguments)},cc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(cc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.Ja).apply(null,arguments)},dc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(dc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=a.asm.Ka).apply(null,arguments)},ec=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(ec=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=
a.asm.La).apply(null,arguments)},fc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(fc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.Ma).apply(null,arguments)},gc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(gc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=a.asm.Na).apply(null,arguments)},hc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(hc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=
a.asm.Oa).apply(null,arguments)},ic=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(ic=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.Pa).apply(null,arguments)},jc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(jc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=a.asm.Qa).apply(null,arguments)},kc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(kc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=
a.asm.Ra).apply(null,arguments)},lc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_SEQUENTIAL_ENCODING=function(){return(lc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_SEQUENTIAL_ENCODING=a.asm.Sa).apply(null,arguments)},mc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_EDGEBREAKER_ENCODING=function(){return(mc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_EDGEBREAKER_ENCODING=a.asm.Ta).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Ua).apply(null,arguments)};a._free=function(){return(a._free=
a.asm.Va).apply(null,arguments)};var pa=function(){return(pa=a.asm.Wa).apply(null,arguments)};a.___start_em_js=19116;a.___stop_em_js=19214;var da;aa=function b(){da||B();da||(aa=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();B();v.prototype=Object.create(v.prototype);v.prototype.constructor=v;v.prototype.__class__=v;v.__cache__={};a.WrapperObject=v;a.getCache=D;a.wrapPointer=I;a.castObject=function(b,c){return I(b.ptr,c)};a.NULL=I(0);
a.destroy=function(b){if(!b.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";b.__destroy__();delete D(b.__class__)[b.ptr]};a.compare=function(b,c){return b.ptr===c.ptr};a.getPointer=function(b){return b.ptr};a.getClass=function(b){return b.__class__};var n={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(n.needed){for(var b=0;b<n.temps.length;b++)a._free(n.temps[b]);n.temps.length=0;a._free(n.buffer);n.buffer=0;n.size+=n.needed;n.needed=0}n.buffer||(n.size+=
128,n.buffer=a._malloc(n.size),n.buffer||k(void 0));n.pos=0},alloc:function(b,c){n.buffer||k(void 0);b=b.length*c.BYTES_PER_ELEMENT;b=b+7&-8;n.pos+b>=n.size?(0<b||k(void 0),n.needed+=b,c=a._malloc(b),n.temps.push(c)):(c=n.buffer+n.pos,n.pos+=b);return c},copy:function(b,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var e=0;e<b.length;e++)c[d+e]=b[e]}};P.prototype=Object.create(v.prototype);P.prototype.constructor=P;P.prototype.__class__=P;P.__cache__=
{};a.VoidPtr=P;P.prototype.__destroy__=P.prototype.__destroy__=function(){La(this.ptr)};M.prototype=Object.create(v.prototype);M.prototype.constructor=M;M.prototype.__class__=M;M.__cache__={};a.GeometryAttribute=M;M.prototype.__destroy__=M.prototype.__destroy__=function(){Ma(this.ptr)};z.prototype=Object.create(v.prototype);z.prototype.constructor=z;z.prototype.__class__=z;z.__cache__={};a.PointAttribute=z;z.prototype.size=z.prototype.size=function(){return Na(this.ptr)};z.prototype.attribute_type=
z.prototype.attribute_type=function(){return Oa(this.ptr)};z.prototype.data_type=z.prototype.data_type=function(){return Pa(this.ptr)};z.prototype.num_components=z.prototype.num_components=function(){return Qa(this.ptr)};z.prototype.normalized=z.prototype.normalized=function(){return!!Ra(this.ptr)};z.prototype.byte_stride=z.prototype.byte_stride=function(){return Sa(this.ptr)};z.prototype.byte_offset=z.prototype.byte_offset=function(){return Ta(this.ptr)};z.prototype.unique_id=z.prototype.unique_id=
function(){return Ua(this.ptr)};z.prototype.__destroy__=z.prototype.__destroy__=function(){Va(this.ptr)};G.prototype=Object.create(v.prototype);G.prototype.constructor=G;G.prototype.__class__=G;G.__cache__={};a.PointCloud=G;G.prototype.num_attributes=G.prototype.num_attributes=function(){return Wa(this.ptr)};G.prototype.num_points=G.prototype.num_points=function(){return Xa(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){Ya(this.ptr)};E.prototype=Object.create(v.prototype);E.prototype.constructor=
E;E.prototype.__class__=E;E.__cache__={};a.Mesh=E;E.prototype.num_faces=E.prototype.num_faces=function(){return Za(this.ptr)};E.prototype.num_attributes=E.prototype.num_attributes=function(){return $a(this.ptr)};E.prototype.num_points=E.prototype.num_points=function(){return ab(this.ptr)};E.prototype.set_num_points=E.prototype.set_num_points=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);bb(c,b)};E.prototype.__destroy__=E.prototype.__destroy__=function(){cb(this.ptr)};N.prototype=Object.create(v.prototype);
N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.Metadata=N;N.prototype.__destroy__=N.prototype.__destroy__=function(){db(this.ptr)};H.prototype=Object.create(v.prototype);H.prototype.constructor=H;H.prototype.__class__=H;H.__cache__={};a.DracoInt8Array=H;H.prototype.GetValue=H.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return eb(c,b)};H.prototype.size=H.prototype.size=function(){return fb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=
function(){gb(this.ptr)};F.prototype=Object.create(v.prototype);F.prototype.constructor=F;F.prototype.__class__=F;F.__cache__={};a.MetadataBuilder=F;F.prototype.AddStringEntry=F.prototype.AddStringEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);d=d&&"object"===typeof d?d.ptr:J(d);return!!hb(e,b,c,d)};F.prototype.AddIntEntry=F.prototype.AddIntEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&
"object"===typeof c?c.ptr:J(c);d&&"object"===typeof d&&(d=d.ptr);return!!ib(e,b,c,d)};F.prototype.AddIntEntryArray=F.prototype.AddIntEntryArray=function(b,c,d,e){var g=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);"object"==typeof d&&(d=V(d));e&&"object"===typeof e&&(e=e.ptr);return!!jb(g,b,c,d,e)};F.prototype.AddDoubleEntry=F.prototype.AddDoubleEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?
c.ptr:J(c);d&&"object"===typeof d&&(d=d.ptr);return!!kb(e,b,c,d)};F.prototype.__destroy__=F.prototype.__destroy__=function(){lb(this.ptr)};x.prototype=Object.create(v.prototype);x.prototype.constructor=x;x.prototype.__class__=x;x.__cache__={};a.PointCloudBuilder=x;x.prototype.AddFloatAttribute=x.prototype.AddFloatAttribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=
e.ptr);"object"==typeof g&&(g=Z(g));return mb(t,b,c,d,e,g)};x.prototype.AddInt8Attribute=x.prototype.AddInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return nb(t,b,c,d,e,g)};x.prototype.AddUInt8Attribute=x.prototype.AddUInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===
typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return ob(t,b,c,d,e,g)};x.prototype.AddInt16Attribute=x.prototype.AddInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return pb(t,b,c,d,e,g)};x.prototype.AddUInt16Attribute=x.prototype.AddUInt16Attribute=function(b,
c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return qb(t,b,c,d,e,g)};x.prototype.AddInt32Attribute=x.prototype.AddInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return rb(t,
b,c,d,e,g)};x.prototype.AddUInt32Attribute=x.prototype.AddUInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return sb(t,b,c,d,e,g)};x.prototype.AddMetadata=x.prototype.AddMetadata=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!tb(d,b,c)};x.prototype.SetMetadataForAttribute=
x.prototype.SetMetadataForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!ub(e,b,c,d)};x.prototype.__destroy__=x.prototype.__destroy__=function(){vb(this.ptr)};u.prototype=Object.create(v.prototype);u.prototype.constructor=u;u.prototype.__class__=u;u.__cache__={};a.MeshBuilder=u;u.prototype.AddFacesToMesh=u.prototype.AddFacesToMesh=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&
(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);"object"==typeof d&&(d=V(d));return!!wb(e,b,c,d)};u.prototype.AddFloatAttributeToMesh=u.prototype.AddFloatAttributeToMesh=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return xb(t,b,c,d,e,g)};u.prototype.AddInt32AttributeToMesh=u.prototype.AddInt32AttributeToMesh=function(b,c,d,e,g){var t=this.ptr;
n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return yb(t,b,c,d,e,g)};u.prototype.AddMetadataToMesh=u.prototype.AddMetadataToMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!zb(d,b,c)};u.prototype.AddFloatAttribute=u.prototype.AddFloatAttribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&
(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return Ab(t,b,c,d,e,g)};u.prototype.AddInt8Attribute=u.prototype.AddInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return Bb(t,b,c,d,e,g)};u.prototype.AddUInt8Attribute=u.prototype.AddUInt8Attribute=
function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return Cb(t,b,c,d,e,g)};u.prototype.AddInt16Attribute=u.prototype.AddInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=
R(g));return Db(t,b,c,d,e,g)};u.prototype.AddUInt16Attribute=u.prototype.AddUInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return Eb(t,b,c,d,e,g)};u.prototype.AddInt32Attribute=u.prototype.AddInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);
d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return Fb(t,b,c,d,e,g)};u.prototype.AddUInt32Attribute=u.prototype.AddUInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return Gb(t,b,c,d,e,g)};u.prototype.AddMetadata=u.prototype.AddMetadata=function(b,c){var d=this.ptr;b&&"object"===
typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!Hb(d,b,c)};u.prototype.SetMetadataForAttribute=u.prototype.SetMetadataForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Ib(e,b,c,d)};u.prototype.__destroy__=u.prototype.__destroy__=function(){Jb(this.ptr)};y.prototype=Object.create(v.prototype);y.prototype.constructor=y;y.prototype.__class__=y;y.__cache__={};a.Encoder=y;y.prototype.SetEncodingMethod=
y.prototype.SetEncodingMethod=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Kb(c,b)};y.prototype.SetAttributeQuantization=y.prototype.SetAttributeQuantization=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Lb(d,b,c)};y.prototype.SetAttributeExplicitQuantization=y.prototype.SetAttributeExplicitQuantization=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
typeof d&&(d=d.ptr);"object"==typeof e&&(e=Z(e));g&&"object"===typeof g&&(g=g.ptr);Mb(t,b,c,d,e,g)};y.prototype.SetSpeedOptions=y.prototype.SetSpeedOptions=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Nb(d,b,c)};y.prototype.SetTrackEncodedProperties=y.prototype.SetTrackEncodedProperties=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Ob(c,b)};y.prototype.EncodeMeshToDracoBuffer=y.prototype.EncodeMeshToDracoBuffer=function(b,c){var d=
this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return Pb(d,b,c)};y.prototype.EncodePointCloudToDracoBuffer=y.prototype.EncodePointCloudToDracoBuffer=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return Qb(e,b,c,d)};y.prototype.GetNumberOfEncodedPoints=y.prototype.GetNumberOfEncodedPoints=function(){return Rb(this.ptr)};y.prototype.GetNumberOfEncodedFaces=y.prototype.GetNumberOfEncodedFaces=
function(){return Sb(this.ptr)};y.prototype.__destroy__=y.prototype.__destroy__=function(){Tb(this.ptr)};A.prototype=Object.create(v.prototype);A.prototype.constructor=A;A.prototype.__class__=A;A.__cache__={};a.ExpertEncoder=A;A.prototype.SetEncodingMethod=A.prototype.SetEncodingMethod=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Ub(c,b)};A.prototype.SetAttributeQuantization=A.prototype.SetAttributeQuantization=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===
typeof c&&(c=c.ptr);Vb(d,b,c)};A.prototype.SetAttributeExplicitQuantization=A.prototype.SetAttributeExplicitQuantization=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);"object"==typeof e&&(e=Z(e));g&&"object"===typeof g&&(g=g.ptr);Wb(t,b,c,d,e,g)};A.prototype.SetSpeedOptions=A.prototype.SetSpeedOptions=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);
Xb(d,b,c)};A.prototype.SetTrackEncodedProperties=A.prototype.SetTrackEncodedProperties=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Yb(c,b)};A.prototype.EncodeToDracoBuffer=A.prototype.EncodeToDracoBuffer=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return Zb(d,b,c)};A.prototype.GetNumberOfEncodedPoints=A.prototype.GetNumberOfEncodedPoints=function(){return $b(this.ptr)};A.prototype.GetNumberOfEncodedFaces=A.prototype.GetNumberOfEncodedFaces=
function(){return ac(this.ptr)};A.prototype.__destroy__=A.prototype.__destroy__=function(){bc(this.ptr)};(function(){function b(){a.INVALID=cc();a.POSITION=dc();a.NORMAL=ec();a.COLOR=fc();a.TEX_COORD=gc();a.GENERIC=hc();a.INVALID_GEOMETRY_TYPE=ic();a.POINT_CLOUD=jc();a.TRIANGULAR_MESH=kc();a.MESH_SEQUENTIAL_ENCODING=lc();a.MESH_EDGEBREAKER_ENCODING=mc()}qa?b():ka.unshift(b)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();return p.ready}}();
"object"===typeof exports&&"object"===typeof module?module.exports=DracoEncoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoEncoderModule}):"object"===typeof exports&&(exports.DracoEncoderModule=DracoEncoderModule);
function(){return(tb=a._emscripten_bind_PointCloudBuilder_AddMetadata_2=a.asm.Y).apply(null,arguments)},ub=a._emscripten_bind_PointCloudBuilder_SetMetadataForAttribute_3=function(){return(ub=a._emscripten_bind_PointCloudBuilder_SetMetadataForAttribute_3=a.asm.Z).apply(null,arguments)},vb=a._emscripten_bind_PointCloudBuilder_SetNormalizedFlagForAttribute_3=function(){return(vb=a._emscripten_bind_PointCloudBuilder_SetNormalizedFlagForAttribute_3=a.asm._).apply(null,arguments)},wb=a._emscripten_bind_PointCloudBuilder___destroy___0=
function(){return(wb=a._emscripten_bind_PointCloudBuilder___destroy___0=a.asm.$).apply(null,arguments)},Ca=a._emscripten_bind_MeshBuilder_MeshBuilder_0=function(){return(Ca=a._emscripten_bind_MeshBuilder_MeshBuilder_0=a.asm.aa).apply(null,arguments)},xb=a._emscripten_bind_MeshBuilder_AddFacesToMesh_3=function(){return(xb=a._emscripten_bind_MeshBuilder_AddFacesToMesh_3=a.asm.ba).apply(null,arguments)},yb=a._emscripten_bind_MeshBuilder_AddFloatAttributeToMesh_5=function(){return(yb=a._emscripten_bind_MeshBuilder_AddFloatAttributeToMesh_5=
a.asm.ca).apply(null,arguments)},zb=a._emscripten_bind_MeshBuilder_AddInt32AttributeToMesh_5=function(){return(zb=a._emscripten_bind_MeshBuilder_AddInt32AttributeToMesh_5=a.asm.da).apply(null,arguments)},Ab=a._emscripten_bind_MeshBuilder_AddMetadataToMesh_2=function(){return(Ab=a._emscripten_bind_MeshBuilder_AddMetadataToMesh_2=a.asm.ea).apply(null,arguments)},Bb=a._emscripten_bind_MeshBuilder_AddFloatAttribute_5=function(){return(Bb=a._emscripten_bind_MeshBuilder_AddFloatAttribute_5=a.asm.fa).apply(null,
arguments)},Cb=a._emscripten_bind_MeshBuilder_AddInt8Attribute_5=function(){return(Cb=a._emscripten_bind_MeshBuilder_AddInt8Attribute_5=a.asm.ga).apply(null,arguments)},Db=a._emscripten_bind_MeshBuilder_AddUInt8Attribute_5=function(){return(Db=a._emscripten_bind_MeshBuilder_AddUInt8Attribute_5=a.asm.ha).apply(null,arguments)},Eb=a._emscripten_bind_MeshBuilder_AddInt16Attribute_5=function(){return(Eb=a._emscripten_bind_MeshBuilder_AddInt16Attribute_5=a.asm.ia).apply(null,arguments)},Fb=a._emscripten_bind_MeshBuilder_AddUInt16Attribute_5=
function(){return(Fb=a._emscripten_bind_MeshBuilder_AddUInt16Attribute_5=a.asm.ja).apply(null,arguments)},Gb=a._emscripten_bind_MeshBuilder_AddInt32Attribute_5=function(){return(Gb=a._emscripten_bind_MeshBuilder_AddInt32Attribute_5=a.asm.ka).apply(null,arguments)},Hb=a._emscripten_bind_MeshBuilder_AddUInt32Attribute_5=function(){return(Hb=a._emscripten_bind_MeshBuilder_AddUInt32Attribute_5=a.asm.la).apply(null,arguments)},Ib=a._emscripten_bind_MeshBuilder_AddMetadata_2=function(){return(Ib=a._emscripten_bind_MeshBuilder_AddMetadata_2=
a.asm.ma).apply(null,arguments)},Jb=a._emscripten_bind_MeshBuilder_SetMetadataForAttribute_3=function(){return(Jb=a._emscripten_bind_MeshBuilder_SetMetadataForAttribute_3=a.asm.na).apply(null,arguments)},Kb=a._emscripten_bind_MeshBuilder_SetNormalizedFlagForAttribute_3=function(){return(Kb=a._emscripten_bind_MeshBuilder_SetNormalizedFlagForAttribute_3=a.asm.oa).apply(null,arguments)},Lb=a._emscripten_bind_MeshBuilder___destroy___0=function(){return(Lb=a._emscripten_bind_MeshBuilder___destroy___0=
a.asm.pa).apply(null,arguments)},Da=a._emscripten_bind_Encoder_Encoder_0=function(){return(Da=a._emscripten_bind_Encoder_Encoder_0=a.asm.qa).apply(null,arguments)},Mb=a._emscripten_bind_Encoder_SetEncodingMethod_1=function(){return(Mb=a._emscripten_bind_Encoder_SetEncodingMethod_1=a.asm.ra).apply(null,arguments)},Nb=a._emscripten_bind_Encoder_SetAttributeQuantization_2=function(){return(Nb=a._emscripten_bind_Encoder_SetAttributeQuantization_2=a.asm.sa).apply(null,arguments)},Ob=a._emscripten_bind_Encoder_SetAttributeExplicitQuantization_5=
function(){return(Ob=a._emscripten_bind_Encoder_SetAttributeExplicitQuantization_5=a.asm.ta).apply(null,arguments)},Pb=a._emscripten_bind_Encoder_SetSpeedOptions_2=function(){return(Pb=a._emscripten_bind_Encoder_SetSpeedOptions_2=a.asm.ua).apply(null,arguments)},Qb=a._emscripten_bind_Encoder_SetTrackEncodedProperties_1=function(){return(Qb=a._emscripten_bind_Encoder_SetTrackEncodedProperties_1=a.asm.va).apply(null,arguments)},Rb=a._emscripten_bind_Encoder_EncodeMeshToDracoBuffer_2=function(){return(Rb=
a._emscripten_bind_Encoder_EncodeMeshToDracoBuffer_2=a.asm.wa).apply(null,arguments)},Sb=a._emscripten_bind_Encoder_EncodePointCloudToDracoBuffer_3=function(){return(Sb=a._emscripten_bind_Encoder_EncodePointCloudToDracoBuffer_3=a.asm.xa).apply(null,arguments)},Tb=a._emscripten_bind_Encoder_GetNumberOfEncodedPoints_0=function(){return(Tb=a._emscripten_bind_Encoder_GetNumberOfEncodedPoints_0=a.asm.ya).apply(null,arguments)},Ub=a._emscripten_bind_Encoder_GetNumberOfEncodedFaces_0=function(){return(Ub=
a._emscripten_bind_Encoder_GetNumberOfEncodedFaces_0=a.asm.za).apply(null,arguments)},Vb=a._emscripten_bind_Encoder___destroy___0=function(){return(Vb=a._emscripten_bind_Encoder___destroy___0=a.asm.Aa).apply(null,arguments)},Ea=a._emscripten_bind_ExpertEncoder_ExpertEncoder_1=function(){return(Ea=a._emscripten_bind_ExpertEncoder_ExpertEncoder_1=a.asm.Ba).apply(null,arguments)},Wb=a._emscripten_bind_ExpertEncoder_SetEncodingMethod_1=function(){return(Wb=a._emscripten_bind_ExpertEncoder_SetEncodingMethod_1=
a.asm.Ca).apply(null,arguments)},Xb=a._emscripten_bind_ExpertEncoder_SetAttributeQuantization_2=function(){return(Xb=a._emscripten_bind_ExpertEncoder_SetAttributeQuantization_2=a.asm.Da).apply(null,arguments)},Yb=a._emscripten_bind_ExpertEncoder_SetAttributeExplicitQuantization_5=function(){return(Yb=a._emscripten_bind_ExpertEncoder_SetAttributeExplicitQuantization_5=a.asm.Ea).apply(null,arguments)},Zb=a._emscripten_bind_ExpertEncoder_SetSpeedOptions_2=function(){return(Zb=a._emscripten_bind_ExpertEncoder_SetSpeedOptions_2=
a.asm.Fa).apply(null,arguments)},$b=a._emscripten_bind_ExpertEncoder_SetTrackEncodedProperties_1=function(){return($b=a._emscripten_bind_ExpertEncoder_SetTrackEncodedProperties_1=a.asm.Ga).apply(null,arguments)},ac=a._emscripten_bind_ExpertEncoder_EncodeToDracoBuffer_2=function(){return(ac=a._emscripten_bind_ExpertEncoder_EncodeToDracoBuffer_2=a.asm.Ha).apply(null,arguments)},bc=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedPoints_0=function(){return(bc=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedPoints_0=
a.asm.Ia).apply(null,arguments)},cc=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedFaces_0=function(){return(cc=a._emscripten_bind_ExpertEncoder_GetNumberOfEncodedFaces_0=a.asm.Ja).apply(null,arguments)},dc=a._emscripten_bind_ExpertEncoder___destroy___0=function(){return(dc=a._emscripten_bind_ExpertEncoder___destroy___0=a.asm.Ka).apply(null,arguments)},ec=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(ec=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.La).apply(null,
arguments)},fc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(fc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=a.asm.Ma).apply(null,arguments)},gc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(gc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.Na).apply(null,arguments)},hc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(hc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.Oa).apply(null,
arguments)},ic=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(ic=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=a.asm.Pa).apply(null,arguments)},jc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(jc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.Qa).apply(null,arguments)},kc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(kc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=
a.asm.Ra).apply(null,arguments)},lc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(lc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=a.asm.Sa).apply(null,arguments)},mc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(mc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.Ta).apply(null,arguments)},nc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_SEQUENTIAL_ENCODING=function(){return(nc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_SEQUENTIAL_ENCODING=
a.asm.Ua).apply(null,arguments)},oc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_EDGEBREAKER_ENCODING=function(){return(oc=a._emscripten_enum_draco_MeshEncoderMethod_MESH_EDGEBREAKER_ENCODING=a.asm.Va).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Wa).apply(null,arguments)};a._free=function(){return(a._free=a.asm.Xa).apply(null,arguments)};var pa=function(){return(pa=a.asm.Ya).apply(null,arguments)};a.___start_em_js=19116;a.___stop_em_js=19214;var da;aa=function b(){da||B();
da||(aa=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();B();v.prototype=Object.create(v.prototype);v.prototype.constructor=v;v.prototype.__class__=v;v.__cache__={};a.WrapperObject=v;a.getCache=D;a.wrapPointer=I;a.castObject=function(b,c){return I(b.ptr,c)};a.NULL=I(0);a.destroy=function(b){if(!b.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";b.__destroy__();delete D(b.__class__)[b.ptr]};a.compare=function(b,
c){return b.ptr===c.ptr};a.getPointer=function(b){return b.ptr};a.getClass=function(b){return b.__class__};var n={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(n.needed){for(var b=0;b<n.temps.length;b++)a._free(n.temps[b]);n.temps.length=0;a._free(n.buffer);n.buffer=0;n.size+=n.needed;n.needed=0}n.buffer||(n.size+=128,n.buffer=a._malloc(n.size),n.buffer||k(void 0));n.pos=0},alloc:function(b,c){n.buffer||k(void 0);b=b.length*c.BYTES_PER_ELEMENT;b=b+7&-8;n.pos+b>=n.size?(0<b||k(void 0),
n.needed+=b,c=a._malloc(b),n.temps.push(c)):(c=n.buffer+n.pos,n.pos+=b);return c},copy:function(b,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var e=0;e<b.length;e++)c[d+e]=b[e]}};P.prototype=Object.create(v.prototype);P.prototype.constructor=P;P.prototype.__class__=P;P.__cache__={};a.VoidPtr=P;P.prototype.__destroy__=P.prototype.__destroy__=function(){La(this.ptr)};M.prototype=Object.create(v.prototype);M.prototype.constructor=M;M.prototype.__class__=
M;M.__cache__={};a.GeometryAttribute=M;M.prototype.__destroy__=M.prototype.__destroy__=function(){Ma(this.ptr)};z.prototype=Object.create(v.prototype);z.prototype.constructor=z;z.prototype.__class__=z;z.__cache__={};a.PointAttribute=z;z.prototype.size=z.prototype.size=function(){return Na(this.ptr)};z.prototype.attribute_type=z.prototype.attribute_type=function(){return Oa(this.ptr)};z.prototype.data_type=z.prototype.data_type=function(){return Pa(this.ptr)};z.prototype.num_components=z.prototype.num_components=
function(){return Qa(this.ptr)};z.prototype.normalized=z.prototype.normalized=function(){return!!Ra(this.ptr)};z.prototype.byte_stride=z.prototype.byte_stride=function(){return Sa(this.ptr)};z.prototype.byte_offset=z.prototype.byte_offset=function(){return Ta(this.ptr)};z.prototype.unique_id=z.prototype.unique_id=function(){return Ua(this.ptr)};z.prototype.__destroy__=z.prototype.__destroy__=function(){Va(this.ptr)};G.prototype=Object.create(v.prototype);G.prototype.constructor=G;G.prototype.__class__=
G;G.__cache__={};a.PointCloud=G;G.prototype.num_attributes=G.prototype.num_attributes=function(){return Wa(this.ptr)};G.prototype.num_points=G.prototype.num_points=function(){return Xa(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){Ya(this.ptr)};E.prototype=Object.create(v.prototype);E.prototype.constructor=E;E.prototype.__class__=E;E.__cache__={};a.Mesh=E;E.prototype.num_faces=E.prototype.num_faces=function(){return Za(this.ptr)};E.prototype.num_attributes=E.prototype.num_attributes=
function(){return $a(this.ptr)};E.prototype.num_points=E.prototype.num_points=function(){return ab(this.ptr)};E.prototype.set_num_points=E.prototype.set_num_points=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);bb(c,b)};E.prototype.__destroy__=E.prototype.__destroy__=function(){cb(this.ptr)};N.prototype=Object.create(v.prototype);N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.Metadata=N;N.prototype.__destroy__=N.prototype.__destroy__=function(){db(this.ptr)};H.prototype=
Object.create(v.prototype);H.prototype.constructor=H;H.prototype.__class__=H;H.__cache__={};a.DracoInt8Array=H;H.prototype.GetValue=H.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return eb(c,b)};H.prototype.size=H.prototype.size=function(){return fb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=function(){gb(this.ptr)};F.prototype=Object.create(v.prototype);F.prototype.constructor=F;F.prototype.__class__=F;F.__cache__={};a.MetadataBuilder=F;F.prototype.AddStringEntry=
F.prototype.AddStringEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);d=d&&"object"===typeof d?d.ptr:J(d);return!!hb(e,b,c,d)};F.prototype.AddIntEntry=F.prototype.AddIntEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);d&&"object"===typeof d&&(d=d.ptr);return!!ib(e,b,c,d)};F.prototype.AddIntEntryArray=F.prototype.AddIntEntryArray=function(b,c,d,e){var g=this.ptr;
n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);"object"==typeof d&&(d=V(d));e&&"object"===typeof e&&(e=e.ptr);return!!jb(g,b,c,d,e)};F.prototype.AddDoubleEntry=F.prototype.AddDoubleEntry=function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:J(c);d&&"object"===typeof d&&(d=d.ptr);return!!kb(e,b,c,d)};F.prototype.__destroy__=F.prototype.__destroy__=function(){lb(this.ptr)};x.prototype=Object.create(v.prototype);
x.prototype.constructor=x;x.prototype.__class__=x;x.__cache__={};a.PointCloudBuilder=x;x.prototype.AddFloatAttribute=x.prototype.AddFloatAttribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return mb(t,b,c,d,e,g)};x.prototype.AddInt8Attribute=x.prototype.AddInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===
typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return nb(t,b,c,d,e,g)};x.prototype.AddUInt8Attribute=x.prototype.AddUInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return ob(t,b,c,d,e,g)};x.prototype.AddInt16Attribute=
x.prototype.AddInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return pb(t,b,c,d,e,g)};x.prototype.AddUInt16Attribute=x.prototype.AddUInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&
(e=e.ptr);"object"==typeof g&&(g=R(g));return qb(t,b,c,d,e,g)};x.prototype.AddInt32Attribute=x.prototype.AddInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return rb(t,b,c,d,e,g)};x.prototype.AddUInt32Attribute=x.prototype.AddUInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);
c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return sb(t,b,c,d,e,g)};x.prototype.AddMetadata=x.prototype.AddMetadata=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!tb(d,b,c)};x.prototype.SetMetadataForAttribute=x.prototype.SetMetadataForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
typeof d&&(d=d.ptr);return!!ub(e,b,c,d)};x.prototype.SetNormalizedFlagForAttribute=x.prototype.SetNormalizedFlagForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!vb(e,b,c,d)};x.prototype.__destroy__=x.prototype.__destroy__=function(){wb(this.ptr)};u.prototype=Object.create(v.prototype);u.prototype.constructor=u;u.prototype.__class__=u;u.__cache__={};a.MeshBuilder=u;u.prototype.AddFacesToMesh=u.prototype.AddFacesToMesh=
function(b,c,d){var e=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);"object"==typeof d&&(d=V(d));return!!xb(e,b,c,d)};u.prototype.AddFloatAttributeToMesh=u.prototype.AddFloatAttributeToMesh=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return yb(t,b,c,d,e,g)};u.prototype.AddInt32AttributeToMesh=
u.prototype.AddInt32AttributeToMesh=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return zb(t,b,c,d,e,g)};u.prototype.AddMetadataToMesh=u.prototype.AddMetadataToMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!Ab(d,b,c)};u.prototype.AddFloatAttribute=u.prototype.AddFloatAttribute=
function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Z(g));return Bb(t,b,c,d,e,g)};u.prototype.AddInt8Attribute=u.prototype.AddInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));
return Cb(t,b,c,d,e,g)};u.prototype.AddUInt8Attribute=u.prototype.AddUInt8Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=Q(g));return Db(t,b,c,d,e,g)};u.prototype.AddInt16Attribute=u.prototype.AddInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return Eb(t,b,c,d,e,g)};u.prototype.AddUInt16Attribute=u.prototype.AddUInt16Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=R(g));return Fb(t,b,c,d,e,g)};u.prototype.AddInt32Attribute=u.prototype.AddInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();
b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return Gb(t,b,c,d,e,g)};u.prototype.AddUInt32Attribute=u.prototype.AddUInt32Attribute=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);e&&"object"===typeof e&&(e=e.ptr);"object"==typeof g&&(g=V(g));return Hb(t,b,c,d,e,g)};u.prototype.AddMetadata=
u.prototype.AddMetadata=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!Ib(d,b,c)};u.prototype.SetMetadataForAttribute=u.prototype.SetMetadataForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Jb(e,b,c,d)};u.prototype.SetNormalizedFlagForAttribute=u.prototype.SetNormalizedFlagForAttribute=function(b,c,d){var e=this.ptr;b&&"object"===typeof b&&
(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Kb(e,b,c,d)};u.prototype.__destroy__=u.prototype.__destroy__=function(){Lb(this.ptr)};y.prototype=Object.create(v.prototype);y.prototype.constructor=y;y.prototype.__class__=y;y.__cache__={};a.Encoder=y;y.prototype.SetEncodingMethod=y.prototype.SetEncodingMethod=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Mb(c,b)};y.prototype.SetAttributeQuantization=y.prototype.SetAttributeQuantization=function(b,
c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Nb(d,b,c)};y.prototype.SetAttributeExplicitQuantization=y.prototype.SetAttributeExplicitQuantization=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);"object"==typeof e&&(e=Z(e));g&&"object"===typeof g&&(g=g.ptr);Ob(t,b,c,d,e,g)};y.prototype.SetSpeedOptions=y.prototype.SetSpeedOptions=function(b,c){var d=this.ptr;
b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Pb(d,b,c)};y.prototype.SetTrackEncodedProperties=y.prototype.SetTrackEncodedProperties=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Qb(c,b)};y.prototype.EncodeMeshToDracoBuffer=y.prototype.EncodeMeshToDracoBuffer=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return Rb(d,b,c)};y.prototype.EncodePointCloudToDracoBuffer=y.prototype.EncodePointCloudToDracoBuffer=function(b,
c,d){var e=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return Sb(e,b,c,d)};y.prototype.GetNumberOfEncodedPoints=y.prototype.GetNumberOfEncodedPoints=function(){return Tb(this.ptr)};y.prototype.GetNumberOfEncodedFaces=y.prototype.GetNumberOfEncodedFaces=function(){return Ub(this.ptr)};y.prototype.__destroy__=y.prototype.__destroy__=function(){Vb(this.ptr)};A.prototype=Object.create(v.prototype);A.prototype.constructor=A;A.prototype.__class__=
A;A.__cache__={};a.ExpertEncoder=A;A.prototype.SetEncodingMethod=A.prototype.SetEncodingMethod=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Wb(c,b)};A.prototype.SetAttributeQuantization=A.prototype.SetAttributeQuantization=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Xb(d,b,c)};A.prototype.SetAttributeExplicitQuantization=A.prototype.SetAttributeExplicitQuantization=function(b,c,d,e,g){var t=this.ptr;n.prepare();b&&"object"===typeof b&&
(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);"object"==typeof e&&(e=Z(e));g&&"object"===typeof g&&(g=g.ptr);Yb(t,b,c,d,e,g)};A.prototype.SetSpeedOptions=A.prototype.SetSpeedOptions=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);Zb(d,b,c)};A.prototype.SetTrackEncodedProperties=A.prototype.SetTrackEncodedProperties=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);$b(c,b)};A.prototype.EncodeToDracoBuffer=A.prototype.EncodeToDracoBuffer=
function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return ac(d,b,c)};A.prototype.GetNumberOfEncodedPoints=A.prototype.GetNumberOfEncodedPoints=function(){return bc(this.ptr)};A.prototype.GetNumberOfEncodedFaces=A.prototype.GetNumberOfEncodedFaces=function(){return cc(this.ptr)};A.prototype.__destroy__=A.prototype.__destroy__=function(){dc(this.ptr)};(function(){function b(){a.INVALID=ec();a.POSITION=fc();a.NORMAL=gc();a.COLOR=hc();a.TEX_COORD=ic();a.GENERIC=
jc();a.INVALID_GEOMETRY_TYPE=kc();a.POINT_CLOUD=lc();a.TRIANGULAR_MESH=mc();a.MESH_SEQUENTIAL_ENCODING=nc();a.MESH_EDGEBREAKER_ENCODING=oc()}qa?b():ka.unshift(b)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();return p.ready}}();"object"===typeof exports&&"object"===typeof module?module.exports=DracoEncoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoEncoderModule}):"object"===typeof exports&&(exports.DracoEncoderModule=DracoEncoderModule);

View File

@ -1,6 +1,6 @@
{
"name": "draco3dgltf",
"version": "1.5.6",
"version": "1.5.7",
"description": "This package contains a specific version of Draco 3D geometric compression library that is used for glTF Draco mesh compression extension.",
"main": "draco3dgltf.js",
"scripts": {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -14,6 +14,11 @@
//
#include "draco/attributes/attribute_quantization_transform.h"
#include <cmath>
#include <cstring>
#include <memory>
#include <vector>
#include "draco/attributes/attribute_transform_type.h"
#include "draco/core/quantization_utils.h"
@ -144,6 +149,9 @@ bool AttributeQuantizationTransform::ComputeParameters(
++i) {
attribute.GetValue(i, att_val.get());
for (int c = 0; c < num_components; ++c) {
if (std::isnan(att_val[c])) {
return false;
}
if (min_values_[c] > att_val[c]) {
min_values_[c] = att_val[c];
}

View File

@ -59,6 +59,9 @@ bool GeometryAttribute::CopyFrom(const GeometryAttribute &src_att) {
}
buffer_->Update(src_att.buffer_->data(), src_att.buffer_->data_size());
}
#ifdef DRACO_TRANSCODER_SUPPORTED
name_ = src_att.name_;
#endif
return true;
}
@ -87,6 +90,11 @@ bool GeometryAttribute::operator==(const GeometryAttribute &va) const {
if (byte_offset_ != va.byte_offset_) {
return false;
}
#ifdef DRACO_TRANSCODER_SUPPORTED
if (name_ != va.name_) {
return false;
}
#endif
return true;
}

View File

@ -245,6 +245,16 @@ class GeometryAttribute {
return "TEX_COORD";
case GENERIC:
return "GENERIC";
#ifdef DRACO_TRANSCODER_SUPPORTED
case TANGENT:
return "TANGENT";
case MATERIAL:
return "MATERIAL";
case JOINTS:
return "JOINTS";
case WEIGHTS:
return "WEIGHTS";
#endif
default:
return "UNKNOWN";
}
@ -276,6 +286,10 @@ class GeometryAttribute {
DataBufferDescriptor buffer_descriptor() const { return buffer_descriptor_; }
uint32_t unique_id() const { return unique_id_; }
void set_unique_id(uint32_t id) { unique_id_ = id; }
#ifdef DRACO_TRANSCODER_SUPPORTED
std::string name() const { return name_; }
void set_name(std::string name) { name_ = name; }
#endif
protected:
// Sets a new internal storage for the attribute.
@ -445,6 +459,10 @@ class GeometryAttribute {
// multiple attribute of the same type in a point cloud.
uint32_t unique_id_;
#ifdef DRACO_TRANSCODER_SUPPORTED
std::string name_;
#endif
friend struct GeometryAttributeHasher;
};

View File

@ -14,8 +14,8 @@
//
#include "draco/attributes/point_attribute.h"
#include <tuple>
#include <unordered_map>
using std::unordered_map;
// Shortcut for typed conditionals.
@ -166,10 +166,12 @@ AttributeValueIndex::ValueType PointAttribute::DeduplicateFormattedValues(
AttributeValueIndex unique_vals(0);
typedef std::array<T, num_components_t> AttributeValue;
typedef std::array<HashType, num_components_t> AttributeHashableValue;
// Hash map storing index of the first attribute with a given value.
unordered_map<AttributeHashableValue, AttributeValueIndex,
typedef unordered_map<AttributeHashableValue, AttributeValueIndex,
HashArray<AttributeHashableValue>>
value_to_index_map;
ValueToIndexMap;
// Hash map storing index of the first attribute with a given value.
ValueToIndexMap value_to_index_map;
AttributeValue att_value;
AttributeHashableValue hashable_value;
IndexTypeVector<AttributeValueIndex, AttributeValueIndex> value_map(
@ -180,19 +182,19 @@ AttributeValueIndex::ValueType PointAttribute::DeduplicateFormattedValues(
// Convert the value to hashable type. Bit-copy real attributes to integers.
memcpy(&(hashable_value[0]), &(att_value[0]), sizeof(att_value));
// Check if the given attribute value has been used before already.
auto it = value_to_index_map.find(hashable_value);
if (it != value_to_index_map.end()) {
typename ValueToIndexMap::iterator it;
bool inserted;
std::tie(it, inserted) = value_to_index_map.insert(
std::pair<AttributeHashableValue, AttributeValueIndex>(hashable_value,
unique_vals));
// Try to update the hash map with a new entry pointing to the latest unique
// vertex index.
if (!inserted) {
// Duplicated value found. Update index mapping.
value_map[i] = it->second;
} else {
// New unique value.
// Update the hash map with a new entry pointing to the latest unique
// vertex index.
value_to_index_map.insert(
std::pair<AttributeHashableValue, AttributeValueIndex>(hashable_value,
unique_vals));
// Add the unique value to the mesh builder.
SetAttributeValue(unique_vals, &att_value);
// Update index mapping.
value_map[i] = unique_vals;

View File

@ -200,7 +200,7 @@ bool MeshPredictionSchemeConstrainedMultiParallelogramEncoder<
// Bit-field used for computing permutations of excluded edges
// (parallelograms).
bool exluded_parallelograms[kMaxNumParallelograms];
bool excluded_parallelograms[kMaxNumParallelograms];
// Data about the number of used parallelogram and total number of available
// parallelogram for each context. Used to compute overhead needed for storing
@ -291,12 +291,12 @@ bool MeshPredictionSchemeConstrainedMultiParallelogramEncoder<
num_used_parallelograms <= num_parallelograms;
++num_used_parallelograms) {
// Mark all parallelograms as excluded.
std::fill(exluded_parallelograms,
exluded_parallelograms + num_parallelograms, true);
std::fill(excluded_parallelograms,
excluded_parallelograms + num_parallelograms, true);
// TODO(draco-eng) maybe this should be another std::fill.
// Mark the first |num_used_parallelograms| as not excluded.
for (int j = 0; j < num_used_parallelograms; ++j) {
exluded_parallelograms[j] = false;
excluded_parallelograms[j] = false;
}
// Permute over the excluded edges and compute error for each
// configuration (permutation of excluded parallelograms).
@ -307,7 +307,7 @@ bool MeshPredictionSchemeConstrainedMultiParallelogramEncoder<
}
uint8_t configuration = 0;
for (int j = 0; j < num_parallelograms; ++j) {
if (exluded_parallelograms[j]) {
if (excluded_parallelograms[j]) {
continue;
}
for (int c = 0; c < num_components; ++c) {
@ -322,7 +322,6 @@ bool MeshPredictionSchemeConstrainedMultiParallelogramEncoder<
}
error = ComputeError(multi_pred_vals.data(), in_data + dst_offset,
&current_residuals[0], num_components);
if (num_parallelograms > 0) {
const int64_t new_overhead_bits = ComputeOverheadBits(
total_used_parallelograms[num_parallelograms - 1] +
num_used_parallelograms,
@ -330,7 +329,6 @@ bool MeshPredictionSchemeConstrainedMultiParallelogramEncoder<
// Add overhead bits to the total error.
error.num_bits += new_overhead_bits;
}
if (error < best_prediction.error) {
best_prediction.error = error;
best_prediction.configuration = configuration;
@ -340,8 +338,9 @@ bool MeshPredictionSchemeConstrainedMultiParallelogramEncoder<
best_prediction.residuals.assign(current_residuals.begin(),
current_residuals.end());
}
} while (std::next_permutation(
exluded_parallelograms, exluded_parallelograms + num_parallelograms));
} while (
std::next_permutation(excluded_parallelograms,
excluded_parallelograms + num_parallelograms));
}
if (num_parallelograms > 0) {
total_used_parallelograms[num_parallelograms - 1] +=

View File

@ -151,6 +151,10 @@ bool MeshPredictionSchemeGeometricNormalDecoder<
if (!buffer->Decode(&prediction_mode)) {
return false;
}
if (prediction_mode > TRIANGLE_AREA) {
// Invalid prediction mode.
return false;
}
if (!predictor_.SetNormalPredictionMode(
NormalPredictionMode(prediction_mode))) {

View File

@ -169,7 +169,7 @@ bool MeshPredictionSchemeTexCoordsPortablePredictor<
}
const int64_t pn_uv_absmax_element =
std::max(std::abs(pn_uv[0]), std::abs(pn_uv[1]));
if (cn_dot_pn >
if (std::abs(cn_dot_pn) >
std::numeric_limits<int64_t>::max() / pn_uv_absmax_element) {
// Return false if squared length calculation would overflow.
return false;
@ -177,7 +177,8 @@ bool MeshPredictionSchemeTexCoordsPortablePredictor<
const Vec2 x_uv = n_uv * pn_norm2_squared + (cn_dot_pn * pn_uv);
const int64_t pn_absmax_element =
std::max(std::max(std::abs(pn[0]), std::abs(pn[1])), std::abs(pn[2]));
if (cn_dot_pn > std::numeric_limits<int64_t>::max() / pn_absmax_element) {
if (std::abs(cn_dot_pn) >
std::numeric_limits<int64_t>::max() / pn_absmax_element) {
// Return false if squared length calculation would overflow.
return false;
}

View File

@ -48,6 +48,11 @@ bool SequentialIntegerAttributeDecoder::DecodeValues(
if (!in_buffer->Decode(&prediction_scheme_method)) {
return false;
}
// Check that decoded prediction scheme method type is valid.
if (prediction_scheme_method < PREDICTION_NONE ||
prediction_scheme_method >= NUM_PREDICTION_SCHEMES) {
return false;
}
if (prediction_scheme_method != PREDICTION_NONE) {
int8_t prediction_transform_type;
if (!in_buffer->Decode(&prediction_transform_type)) {

View File

@ -515,6 +515,74 @@ TEST_F(EncodeTest, TestDracoCompressionOptionsGridQuantization) {
15.f * 0.1f, 1e-6f);
}
TEST_F(EncodeTest, TestPointCloudGridQuantization) {
// Test verifies that we can set position quantization via grid spacing for a
// point cloud.
const auto pc = draco::ReadPointCloudFromTestFile("cube_att.obj");
ASSERT_NE(pc, nullptr);
const int pos_att_id =
pc->GetNamedAttributeId(draco::GeometryAttribute::POSITION);
draco::ExpertEncoder encoder(*pc);
encoder.SetAttributeGridQuantization(*pc, pos_att_id, 0.15);
draco::EncoderBuffer buffer;
DRACO_ASSERT_OK(encoder.EncodeToBuffer(&buffer));
// The grid options should be reflected in the |encoder|. Check that the
// computed values are correct.
draco::Vector3f origin;
encoder.options().GetAttributeVector(pos_att_id, "quantization_origin", 3,
&origin[0]);
ASSERT_EQ(origin, draco::Vector3f(0.f, 0.f, 0.f));
// We need 3 quantization bits for 8 grid values [0.00, 0.15, ...,1.05].
ASSERT_EQ(
encoder.options().GetAttributeInt(pos_att_id, "quantization_bits", -1),
3);
// The quantization range should be ((1 << quantization_bits) - 1) * spacing.
ASSERT_NEAR(encoder.options().GetAttributeFloat(pos_att_id,
"quantization_range", 0.f),
1.05f, 1e-6f);
}
TEST_F(EncodeTest, TestPointCloudGridQuantizationFromCompressionOptions) {
// Test verifies that we can set position quantization via grid spacing for a
// point cloud using DracoCompressionOptions.
const auto pc = draco::ReadPointCloudFromTestFile("cube_att.obj");
ASSERT_NE(pc, nullptr);
pc->SetCompressionEnabled(true);
// Set grid quantization for positions.
draco::DracoCompressionOptions compression_options;
// This should result in 10x10x10 quantization.
compression_options.quantization_position.SetGrid(0.15);
pc->SetCompressionOptions(compression_options);
draco::ExpertEncoder encoder(*pc);
draco::EncoderBuffer buffer;
DRACO_ASSERT_OK(encoder.EncodeToBuffer(&buffer));
// The grid options should be reflected in the |encoder|. Check that the
// computed values are correct.
const int pos_att_id =
pc->GetNamedAttributeId(draco::GeometryAttribute::POSITION);
draco::Vector3f origin;
encoder.options().GetAttributeVector(pos_att_id, "quantization_origin", 3,
&origin[0]);
ASSERT_EQ(origin, draco::Vector3f(0.f, 0.f, 0.f));
// We need 3 quantization bits for 8 grid values [0.00, 0.15, ...,1.05].
ASSERT_EQ(
encoder.options().GetAttributeInt(pos_att_id, "quantization_bits", -1),
3);
// The quantization range should be ((1 << quantization_bits) - 1) * spacing.
ASSERT_NEAR(encoder.options().GetAttributeFloat(pos_att_id,
"quantization_range", 0.f),
1.05f, 1e-6f);
}
TEST_F(EncodeTest, TestDracoCompressionOptionsGridQuantizationWithOffset) {
// Test verifies that we can set position quantization via grid spacing when
// the geometry is not perfectly aligned with the quantization grid.

View File

@ -51,6 +51,11 @@ Status ExpertEncoder::EncodeToBuffer(EncoderBuffer *out_buffer) {
Status ExpertEncoder::EncodePointCloudToBuffer(const PointCloud &pc,
EncoderBuffer *out_buffer) {
#ifdef DRACO_POINT_CLOUD_COMPRESSION_SUPPORTED
#ifdef DRACO_TRANSCODER_SUPPORTED
// Apply DracoCompressionOptions associated with the point cloud.
DRACO_RETURN_IF_ERROR(ApplyCompressionOptions(pc));
#endif // DRACO_TRANSCODER_SUPPORTED
std::unique_ptr<PointCloudEncoder> encoder;
const int encoding_method = options().GetGlobalInt("encoding_method", -1);
@ -195,11 +200,11 @@ Status ExpertEncoder::SetAttributePredictionScheme(
}
#ifdef DRACO_TRANSCODER_SUPPORTED
Status ExpertEncoder::ApplyCompressionOptions(const Mesh &mesh) {
if (!mesh.IsCompressionEnabled()) {
Status ExpertEncoder::ApplyCompressionOptions(const PointCloud &pc) {
if (!pc.IsCompressionEnabled()) {
return OkStatus();
}
const auto &compression_options = mesh.GetCompressionOptions();
const auto &compression_options = pc.GetCompressionOptions();
// Set any encoder options that haven't been explicitly set by users (don't
// override existing options).
@ -208,12 +213,12 @@ Status ExpertEncoder::ApplyCompressionOptions(const Mesh &mesh) {
10 - compression_options.compression_level);
}
for (int ai = 0; ai < mesh.num_attributes(); ++ai) {
for (int ai = 0; ai < pc.num_attributes(); ++ai) {
if (options().IsAttributeOptionSet(ai, "quantization_bits")) {
continue; // Don't override options that have been set.
}
int quantization_bits = 0;
const auto type = mesh.attribute(ai)->attribute_type();
const auto type = pc.attribute(ai)->attribute_type();
switch (type) {
case GeometryAttribute::POSITION:
if (compression_options.quantization_position
@ -221,7 +226,7 @@ Status ExpertEncoder::ApplyCompressionOptions(const Mesh &mesh) {
quantization_bits =
compression_options.quantization_position.quantization_bits();
} else {
DRACO_RETURN_IF_ERROR(ApplyGridQuantization(mesh, ai));
DRACO_RETURN_IF_ERROR(ApplyGridQuantization(pc, ai));
}
break;
case GeometryAttribute::TEX_COORD:
@ -252,17 +257,29 @@ Status ExpertEncoder::ApplyCompressionOptions(const Mesh &mesh) {
return OkStatus();
}
Status ExpertEncoder::ApplyGridQuantization(const Mesh &mesh,
Status ExpertEncoder::ApplyGridQuantization(const PointCloud &pc,
int attribute_index) {
const auto compression_options = mesh.GetCompressionOptions();
if (mesh.attribute(attribute_index)->num_components() != 3) {
const auto compression_options = pc.GetCompressionOptions();
const float spacing = compression_options.quantization_position.spacing();
return SetAttributeGridQuantization(pc, attribute_index, spacing);
}
Status ExpertEncoder::SetAttributeGridQuantization(const PointCloud &pc,
int attribute_index,
float spacing) {
const auto *const att = pc.attribute(attribute_index);
if (att->attribute_type() != GeometryAttribute::POSITION) {
return ErrorStatus(
"Invalid attribute type: Grid quantization is currently supported only "
"for positions.");
}
if (att->num_components() != 3) {
return ErrorStatus(
"Invalid number of components: Grid quantization is currently "
"supported only for 3D positions.");
}
const float spacing = compression_options.quantization_position.spacing();
// Compute quantization properties based on the grid spacing.
const auto &bbox = mesh.ComputeBoundingBox();
const auto &bbox = pc.ComputeBoundingBox();
// Snap min and max points of the |bbox| to the quantization grid vertices.
Vector3f min_pos;
int num_values = 0; // Number of values that we need to encode.

View File

@ -132,6 +132,13 @@ class ExpertEncoder : public EncoderBase<EncoderOptions> {
Status SetAttributePredictionScheme(int32_t attribute_id,
int prediction_scheme_method);
#ifdef DRACO_TRANSCODER_SUPPORTED
// Applies grid quantization to position attribute in point cloud |pc| at
// |attribute_index| with a given grid |spacing|.
Status SetAttributeGridQuantization(const PointCloud &pc, int attribute_index,
float spacing);
#endif // DRACO_TRANSCODER_SUPPORTED
private:
Status EncodePointCloudToBuffer(const PointCloud &pc,
EncoderBuffer *out_buffer);
@ -139,9 +146,9 @@ class ExpertEncoder : public EncoderBase<EncoderOptions> {
Status EncodeMeshToBuffer(const Mesh &m, EncoderBuffer *out_buffer);
#ifdef DRACO_TRANSCODER_SUPPORTED
// Applies compression options stored in |mesh|.
Status ApplyCompressionOptions(const Mesh &mesh);
Status ApplyGridQuantization(const Mesh &mesh, int attribute_index);
// Applies compression options stored in |pc|.
Status ApplyCompressionOptions(const PointCloud &pc);
Status ApplyGridQuantization(const PointCloud &pc, int attribute_index);
#endif // DRACO_TRANSCODER_SUPPORTED
const PointCloud *point_cloud_;

View File

@ -15,6 +15,7 @@
#include "draco/compression/mesh/mesh_edgebreaker_decoder_impl.h"
#include <algorithm>
#include <cstdint>
#include "draco/compression/attributes/sequential_attribute_decoders_controller.h"
#include "draco/compression/mesh/mesh_edgebreaker_decoder.h"
@ -299,6 +300,22 @@ bool MeshEdgebreakerDecoderImpl<TraversalDecoder>::DecodeConnectivity() {
if (static_cast<uint32_t>(num_encoded_vertices_) > num_faces * 3) {
return false; // There cannot be more vertices than 3 * num_faces.
}
// Minimum number of edges of the mesh assuming each edge is shared between
// two faces.
const uint32_t min_num_face_edges = 3 * num_faces / 2;
// Maximum number of edges that can exist between |num_encoded_vertices_|.
// This is based on graph theory assuming simple connected graph.
const uint64_t num_encoded_vertices_64 =
static_cast<uint64_t>(num_encoded_vertices_);
const uint64_t max_num_vertex_edges =
num_encoded_vertices_64 * (num_encoded_vertices_64 - 1) / 2;
if (max_num_vertex_edges < min_num_face_edges) {
// It is impossible to construct a manifold mesh with these properties.
return false;
}
uint8_t num_attribute_data;
if (!decoder_->buffer()->Decode(&num_attribute_data)) {
return false;

View File

@ -14,6 +14,9 @@
//
#include "draco/compression/mesh/mesh_sequential_decoder.h"
#include <cstdint>
#include <limits>
#include "draco/compression/attributes/linear_sequencer.h"
#include "draco/compression/attributes/sequential_attribute_decoders_controller.h"
#include "draco/compression/entropy/symbol_decoding.h"
@ -48,7 +51,6 @@ bool MeshSequentialDecoder::DecodeConnectivity() {
// Check that num_faces and num_points are valid values.
const uint64_t faces_64 = static_cast<uint64_t>(num_faces);
const uint64_t points_64 = static_cast<uint64_t>(num_points);
// Compressed sequential encoding can only handle (2^32 - 1) / 3 indices.
if (faces_64 > 0xffffffff / 3) {
return false;
@ -58,9 +60,6 @@ bool MeshSequentialDecoder::DecodeConnectivity() {
// fit in the remaining size of the buffer.
return false;
}
if (points_64 > faces_64 * 3) {
return false;
}
uint8_t connectivity_method;
if (!buffer()->Decode(&connectivity_method)) {
return false;
@ -147,7 +146,7 @@ bool MeshSequentialDecoder::DecodeAndDecompressIndices(uint32_t num_faces) {
}
// Reconstruct the indices from the differences.
// See MeshSequentialEncoder::CompressAndEncodeIndices() for more details.
int32_t last_index_value = 0;
int32_t last_index_value = 0; // This will always be >= 0.
int vertex_index = 0;
for (uint32_t i = 0; i < num_faces; ++i) {
Mesh::Face face;
@ -155,13 +154,19 @@ bool MeshSequentialDecoder::DecodeAndDecompressIndices(uint32_t num_faces) {
const uint32_t encoded_val = indices_buffer[vertex_index++];
int32_t index_diff = (encoded_val >> 1);
if (encoded_val & 1) {
index_diff = -index_diff;
}
const int32_t index_value = index_diff + last_index_value;
if (index_value < 0) {
// Negative indices are not allowed.
if (index_diff > last_index_value) {
// Subtracting index_diff would result in a negative index.
return false;
}
index_diff = -index_diff;
} else {
if (index_diff >
(std::numeric_limits<int32_t>::max() - last_index_value)) {
// Adding index_diff to last_index_value would overflow.
return false;
}
}
const int32_t index_value = index_diff + last_index_value;
face[j] = index_value;
last_index_value = index_value;
}

View File

@ -76,7 +76,7 @@ class FloatPointsTreeDecoder {
static const uint32_t version_ = 3;
QuantizationInfo qinfo_;
PointCloudCompressionMethod method_;
int8_t method_;
uint32_t num_points_;
uint32_t compression_level_;
@ -113,7 +113,7 @@ bool FloatPointsTreeDecoder::DecodePointCloud(DecoderBuffer *buffer,
return false;
}
method_ = static_cast<PointCloudCompressionMethod>(method_number);
method_ = method_number;
if (method_ == KDTREE) {
if (!DecodePointCloudKdTreeInternal(buffer, &qpoints)) {

View File

@ -18,7 +18,7 @@
namespace draco {
// Draco version is comprised of <major>.<minor>.<revision>.
static const char kDracoVersion[] = "1.5.6";
static const char kDracoVersion[] = "1.5.7";
} // namespace draco

View File

@ -15,6 +15,9 @@
#include "draco/io/gltf_decoder.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include <array>
#include <cstdint>
#include <map>
#include <memory>
#include <set>
#include <string>
@ -27,11 +30,15 @@
#include "draco/core/hash_utils.h"
#include "draco/core/status.h"
#include "draco/core/status_or.h"
#include "draco/io/file_utils.h"
#include "draco/io/texture_io.h"
#include "draco/io/tiny_gltf_utils.h"
#include "draco/material/material_library.h"
#include "draco/mesh/mesh.h"
#include "draco/mesh/mesh_features.h"
#include "draco/mesh/triangle_soup_mesh_builder.h"
#include "draco/metadata/geometry_metadata.h"
#include "draco/metadata/metadata.h"
#include "draco/metadata/property_table.h"
#include "draco/point_cloud/point_cloud_builder.h"
#include "draco/scene/scene_indices.h"
@ -80,6 +87,10 @@ GeometryAttribute::Type GltfAttributeToDracoAttribute(
} else if (attribute_name.rfind("_FEATURE_ID_") == 0) {
// Feature ID attribute like _FEATURE_ID_5 from EXT_mesh_features extension.
return GeometryAttribute::GENERIC;
} else if (attribute_name.rfind('_', 0) == 0) {
// Feature ID attribute like _DIRECTION from EXT_structural_metadata
// extension whose name begins with an underscore.
return GeometryAttribute::GENERIC;
}
return GeometryAttribute::INVALID;
}
@ -540,6 +551,9 @@ StatusOr<std::unique_ptr<Mesh>> GltfDecoder::BuildMesh() {
DRACO_RETURN_IF_ERROR(AddAttributesToDracoMesh(&pb_));
}
// Clear attribute indices before populating attributes in |mb_| or |pb_|.
feature_id_attribute_indices_.clear();
for (const tinygltf::Scene &scene : gltf_model_.scenes) {
for (int i = 0; i < scene.nodes.size(); ++i) {
const Eigen::Matrix4d parent_matrix = Eigen::Matrix4d::Identity();
@ -548,43 +562,59 @@ StatusOr<std::unique_ptr<Mesh>> GltfDecoder::BuildMesh() {
}
DRACO_ASSIGN_OR_RETURN(
std::unique_ptr<Mesh> mesh,
BuildMeshFromBuilder(total_face_indices_count_ > 0, &mb_, &pb_));
BuildMeshFromBuilder(total_face_indices_count_ > 0, &mb_, &pb_,
deduplicate_vertices_));
DRACO_RETURN_IF_ERROR(CopyTextures<Mesh>(mesh.get()));
SetAttributePropertiesOnDracoMesh(mesh.get());
DRACO_RETURN_IF_ERROR(AddMaterialsToDracoMesh(mesh.get()));
DRACO_RETURN_IF_ERROR(AddMeshFeaturesToDracoMesh(mesh.get()));
DRACO_RETURN_IF_ERROR(AddPrimitiveExtensionsToDracoMesh(mesh.get()));
DRACO_RETURN_IF_ERROR(AddStructuralMetadataToGeometry(mesh.get()));
MoveNonMaterialTextures(mesh.get());
DRACO_RETURN_IF_ERROR(AddAssetMetadata(mesh.get()));
return mesh;
}
Status GltfDecoder::AddMeshFeaturesToDracoMesh(Mesh *mesh) {
Status GltfDecoder::AddPrimitiveExtensionsToDracoMesh(Mesh *mesh) {
for (const tinygltf::Scene &scene : gltf_model_.scenes) {
for (int i = 0; i < scene.nodes.size(); ++i) {
DRACO_RETURN_IF_ERROR(AddMeshFeaturesToDracoMesh(scene.nodes[i], mesh));
DRACO_RETURN_IF_ERROR(
AddPrimitiveExtensionsToDracoMesh(scene.nodes[i], mesh));
}
}
return OkStatus();
}
Status GltfDecoder::AddMeshFeaturesToDracoMesh(int node_index, Mesh *mesh) {
Status GltfDecoder::AddPrimitiveExtensionsToDracoMesh(int node_index,
Mesh *mesh) {
const tinygltf::Node &node = gltf_model_.nodes[node_index];
if (node.mesh >= 0) {
const tinygltf::Mesh &gltf_mesh = gltf_model_.meshes[node.mesh];
for (const auto &primitive : gltf_mesh.primitives) {
// Decode mesh feature ID sets if present in this primitive.
DRACO_RETURN_IF_ERROR(DecodeMeshFeatures(
// Decode extensions present in this primitive.
DRACO_RETURN_IF_ERROR(AddPrimitiveExtensionsToDracoMesh(
primitive, &mesh->GetMaterialLibrary().MutableTextureLibrary(),
mesh));
}
}
for (int i = 0; i < node.children.size(); ++i) {
DRACO_RETURN_IF_ERROR(AddMeshFeaturesToDracoMesh(node.children[i], mesh));
DRACO_RETURN_IF_ERROR(
AddPrimitiveExtensionsToDracoMesh(node.children[i], mesh));
}
return OkStatus();
}
Status GltfDecoder::AddPrimitiveExtensionsToDracoMesh(
const tinygltf::Primitive &primitive, TextureLibrary *texture_library,
Mesh *mesh) {
// Decode mesh feature ID sets if present in this |primitive|.
DRACO_RETURN_IF_ERROR(DecodeMeshFeatures(primitive, texture_library, mesh));
// Decode structural metadata if present in this |primitive|.
DRACO_RETURN_IF_ERROR(DecodeStructuralMetadata(primitive, mesh));
return OkStatus();
}
Status GltfDecoder::CheckUnsupportedFeatures() {
// Check for morph targets.
for (const auto &mesh : gltf_model_.meshes) {
@ -853,6 +883,12 @@ Status GltfDecoder::AddAttributesToDracoMesh(BuilderT *builder) {
attribute_name_to_draco_mesh_attribute_id_[attribute.first] = -1;
continue;
}
// TODO(vytyaz): Check that when glTF is decoded into a single draco::Mesh
// the feature ID vertex attributes are consistent with geometry, e.g., the
// number of values in attribute with name _FEATURE_ID_5 accumulated from
// all primitives must be equal to the accumulated number of mesh vertices.
// Furthermore, accumulated attributes should probably come from mesh
// feature ID sets with the same labels.
DRACO_ASSIGN_OR_RETURN(
const int att_id,
AddAttribute(draco_att_type, attribute.second.component_type,
@ -875,6 +911,13 @@ Status GltfDecoder::AddAttributesToDracoMesh(BuilderT *builder) {
return OkStatus();
}
// Returns the index from a feature ID vertex attribute name like _FEATURE_ID_5.
int GetIndexFromFeatureIdAttributeName(const std::string &name) {
const std::string prefix = "_FEATURE_ID_";
const std::string number = name.substr(prefix.length());
return std::stoi(number);
}
template <typename BuilderT>
Status GltfDecoder::AddAttributeValuesToBuilder(
const std::string &attribute_name, const tinygltf::Accessor &accessor,
@ -903,6 +946,16 @@ Status GltfDecoder::AddAttributeValuesToBuilder(
DRACO_RETURN_IF_ERROR(AddFeatureIdToBuilder(
accessor, indices_data, att_id, number_of_elements, reverse_winding,
attribute_name, builder));
// Populate map from the index in attribute name like _FEATURE_ID_5 to the
// attribute index in the builder.
const int index = GetIndexFromFeatureIdAttributeName(attribute_name);
feature_id_attribute_indices_[index] = att_id;
} else if (attribute_name.rfind('_', 0) == 0) {
// This is a structural metadata property attribute with a name like
// _DIRECTION that begins with an underscore.
DRACO_RETURN_IF_ERROR(AddPropertyAttributeToBuilder(
accessor, indices_data, att_id, number_of_elements, reverse_winding,
attribute_name, builder));
} else {
DRACO_RETURN_IF_ERROR(AddAttributeDataByTypes(accessor, indices_data,
att_id, number_of_elements,
@ -987,12 +1040,22 @@ Status GltfDecoder::AddFeatureIdToBuilder(
DRACO_RETURN_IF_ERROR(AddAttributeDataByTypes(accessor, indices_data, att_id,
number_of_elements,
reverse_winding, builder));
return OkStatus();
}
// Store feature ID attribute name with index like _FEATURE_ID_5 in Draco
// attribute metadata.
std::unique_ptr<AttributeMetadata> metadata(new draco::AttributeMetadata());
metadata->AddEntryString("attribute_name", attribute_name);
builder->AddAttributeMetadata(att_id, std::move(metadata));
template <typename BuilderT>
Status GltfDecoder::AddPropertyAttributeToBuilder(
const tinygltf::Accessor &accessor,
const std::vector<uint32_t> &indices_data, int att_id,
int number_of_elements, bool reverse_winding,
const std::string &attribute_name, BuilderT *builder) {
// Set property attribute values to mesh.
DRACO_RETURN_IF_ERROR(AddAttributeDataByTypes(accessor, indices_data, att_id,
number_of_elements,
reverse_winding, builder));
// Store property attribute name like _DIRECTION in Draco attribute.
builder->SetAttributeName(att_id, attribute_name);
return OkStatus();
}
@ -1277,7 +1340,7 @@ Status GltfDecoder::AddMaterialsToDracoMesh(Mesh *mesh) {
bool is_normal_map_used = false;
int default_material_index = -1;
const auto it = gltf_primitive_material_to_draco_material_.find(-1);
auto it = gltf_primitive_material_to_draco_material_.find(-1);
if (it != gltf_primitive_material_to_draco_material_.end()) {
default_material_index = it->second;
}
@ -1286,14 +1349,19 @@ Status GltfDecoder::AddMaterialsToDracoMesh(Mesh *mesh) {
for (int input_material_index = 0;
input_material_index < gltf_model_.materials.size();
++input_material_index) {
it = gltf_primitive_material_to_draco_material_.find(input_material_index);
if (it == gltf_primitive_material_to_draco_material_.end()) {
continue;
}
output_material_index = it->second;
if (default_material_index == input_material_index) {
// Insert a default material here for primitives that did not have a
// material index.
mesh->GetMaterialLibrary().MutableMaterial(output_material_index++);
mesh->GetMaterialLibrary().MutableMaterial(output_material_index);
}
Material *const output_material =
mesh->GetMaterialLibrary().MutableMaterial(output_material_index++);
mesh->GetMaterialLibrary().MutableMaterial(output_material_index);
DRACO_RETURN_IF_ERROR(
AddGltfMaterial(input_material_index, output_material));
if (output_material->GetTextureMapByType(
@ -1415,6 +1483,7 @@ Status GltfDecoder::DecodeGltfToScene() {
DRACO_RETURN_IF_ERROR(AddMaterialsToScene());
DRACO_RETURN_IF_ERROR(AddSkinsToScene());
MoveNonMaterialTextures(scene_.get());
DRACO_RETURN_IF_ERROR(AddAssetMetadata(scene_.get()));
return OkStatus();
}
@ -1511,10 +1580,30 @@ Status GltfDecoder::AddStructuralMetadataToGeometry(GeometryT *geometry) {
}
const tinygltf::Value::Object &o = e->second.Get<tinygltf::Value::Object>();
// Decode property table schema.
{
const auto &value = o.find("schema");
if (value == o.end()) {
// Decode structural metadata schema.
DRACO_RETURN_IF_ERROR(AddStructuralMetadataSchemaToGeometry(o, geometry));
// Decode structural metadata property tables.
DRACO_RETURN_IF_ERROR(AddPropertyTablesToGeometry(o, geometry));
// Decode structural metadata property attributes.
DRACO_RETURN_IF_ERROR(AddPropertyAttributesToGeometry(o, geometry));
// Check that structural metadata has either property tables, or property
// attribute, or property textures (in the future).
if (geometry->GetStructuralMetadata().NumPropertyTables() == 0 &&
geometry->GetStructuralMetadata().NumPropertyAttributes() == 0) {
return ErrorStatus(
"Structural metadata has no property tables, no property attributes.");
}
return OkStatus();
}
template <typename GeometryT>
Status GltfDecoder::AddStructuralMetadataSchemaToGeometry(
const tinygltf::Value::Object &extension, GeometryT *geometry) {
const auto &value = extension.find("schema");
if (value == extension.end()) {
return ErrorStatus("Structural metadata extension has no schema.");
}
const tinygltf::Value &object = value->second;
@ -1522,10 +1611,10 @@ Status GltfDecoder::AddStructuralMetadataToGeometry(GeometryT *geometry) {
return ErrorStatus("Structural metadata extension schema is malformed.");
}
// Decodes tinygltf::Value into PropertyTable::Schema::Object.
// Decodes tinygltf::Value into StructuralMetadataSchema::Object.
struct SchemaParser {
static Status Parse(const tinygltf::Value &value,
PropertyTable::Schema::Object *object) {
StructuralMetadataSchema::Object *object) {
switch (value.Type()) {
case tinygltf::OBJECT_TYPE: {
for (auto &it : value.Get<tinygltf::Value::Object>()) {
@ -1561,18 +1650,20 @@ Status GltfDecoder::AddStructuralMetadataToGeometry(GeometryT *geometry) {
}
};
// Parse property table schema and set it to |geometry|.
PropertyTable::Schema schema;
// Parse schema of the structural metadata and set it to |geometry|.
StructuralMetadataSchema schema;
DRACO_RETURN_IF_ERROR(SchemaParser::Parse(object, &schema.json));
geometry->GetStructuralMetadata().SetPropertyTableSchema(schema);
geometry->GetStructuralMetadata().SetSchema(schema);
return OkStatus();
}
// Decode property tables.
{
const auto &tables = o.find("propertyTables");
if (tables == o.end()) {
return ErrorStatus(
"Structural metadata extension has no property tables.");
template <typename GeometryT>
Status GltfDecoder::AddPropertyTablesToGeometry(
const tinygltf::Value::Object &extension, GeometryT *geometry) {
const auto &tables = extension.find("propertyTables");
if (tables == extension.end()) {
// Structural metadata has no property tables.
return OkStatus();
}
const tinygltf::Value &tables_array = tables->second;
if (!tables_array.IsArray()) {
@ -1623,8 +1714,7 @@ Status GltfDecoder::AddStructuralMetadataToGeometry(GeometryT *geometry) {
}
const tinygltf::Value &value = object.Get(kName);
if (!value.IsObject()) {
return ErrorStatus(
"Property table properties property is malformed.");
return ErrorStatus("Property table properties property is malformed.");
}
// Loop over property table properties.
@ -1641,28 +1731,26 @@ Status GltfDecoder::AddStructuralMetadataToGeometry(GeometryT *geometry) {
const auto o = property_object.Get<tinygltf::Value::Object>();
// The "values" property is required.
DRACO_ASSIGN_OR_RETURN(
success,
DecodePropertyTableData("values", o, &property->GetData()));
DRACO_ASSIGN_OR_RETURN(success, DecodePropertyTableData(
"values", o, &property->GetData()));
if (!success) {
return ErrorStatus("Property values property is malformed.");
}
// All other properties are not required.
DRACO_ASSIGN_OR_RETURN(
success, DecodeString("stringOffsetType", o, &str_value));
DRACO_ASSIGN_OR_RETURN(success,
DecodeString("stringOffsetType", o, &str_value));
if (success) {
property->GetStringOffsets().type = str_value;
}
DRACO_ASSIGN_OR_RETURN(
success, DecodeString("arrayOffsetType", o, &str_value));
DRACO_ASSIGN_OR_RETURN(success,
DecodeString("arrayOffsetType", o, &str_value));
if (success) {
property->GetArrayOffsets().type = str_value;
}
DRACO_ASSIGN_OR_RETURN(
success,
DecodePropertyTableData("arrayOffsets", o,
&property->GetArrayOffsets().data));
success, DecodePropertyTableData(
"arrayOffsets", o, &property->GetArrayOffsets().data));
DRACO_ASSIGN_OR_RETURN(
success,
DecodePropertyTableData("stringOffsets", o,
@ -1677,6 +1765,93 @@ Status GltfDecoder::AddStructuralMetadataToGeometry(GeometryT *geometry) {
geometry->GetStructuralMetadata().AddPropertyTable(
std::move(property_table));
}
return OkStatus();
}
template <typename GeometryT>
Status GltfDecoder::AddPropertyAttributesToGeometry(
const tinygltf::Value::Object &extension, GeometryT *geometry) {
const auto &attributes = extension.find("propertyAttributes");
if (attributes == extension.end()) {
// Structural metadata has no property attributes.
return OkStatus();
}
const tinygltf::Value &attributes_array = attributes->second;
if (!attributes_array.IsArray()) {
return ErrorStatus("Property attributes array is malformed.");
}
// Loop over all property attributes.
for (int i = 0; i < attributes_array.Size(); i++) {
// Create a property attribute and populate it below.
std::unique_ptr<PropertyAttribute> property_attribute(
new PropertyAttribute());
const auto &object = attributes_array.Get(i);
if (!object.IsObject()) {
return ErrorStatus("Property attribute is malformed.");
}
const auto o = object.Get<tinygltf::Value::Object>();
// The "class" property is required.
bool success;
std::string str_value;
DRACO_ASSIGN_OR_RETURN(success, DecodeString("class", o, &str_value));
if (success) {
property_attribute->SetClass(str_value);
} else {
return ErrorStatus("Property class is malformed.");
}
// The "name" property is optional.
DRACO_ASSIGN_OR_RETURN(success, DecodeString("name", o, &str_value));
if (success) {
property_attribute->SetName(str_value);
}
// Decode property attribute properties.
{
constexpr char kName[] = "properties";
if (!object.Has(kName)) {
return ErrorStatus("Property attribute is malformed.");
}
const tinygltf::Value &value = object.Get(kName);
if (!value.IsObject()) {
return ErrorStatus(
"Property attribute properties property is malformed.");
}
// Loop over property attribute properties.
for (const auto &key : value.Keys()) {
// Create a property attribute property and populate it below.
std::unique_ptr<PropertyAttribute::Property> property(
new PropertyAttribute::Property());
// Decode property name corresponding to a schema class property name.
const auto &property_object = value.Get(key);
if (!property_object.IsObject()) {
return ErrorStatus("Property entry is malformed.");
}
property->SetName(key);
const auto o = property_object.Get<tinygltf::Value::Object>();
// The "attribute" property is required.
DRACO_ASSIGN_OR_RETURN(success,
DecodeString("attribute", o, &str_value));
if (success) {
property->SetAttributeName(str_value);
} else {
return ErrorStatus("Property attribute is malformed.");
}
// Add property to the property attribute.
property_attribute->AddProperty(std::move(property));
}
}
// Add property attribute to structural metadata.
geometry->GetStructuralMetadata().AddPropertyAttribute(
std::move(property_attribute));
}
return OkStatus();
}
@ -1829,6 +2004,9 @@ Status GltfDecoder::DecodePrimitiveForScene(
pb.Start(number_of_points);
}
// Clear attribute indices before populating attributes in |mb| or |pb|.
feature_id_attribute_indices_.clear();
std::set<int32_t> normalized_attributes;
for (const auto &attribute : primitive.attributes) {
if (attribute.second >= gltf_model_.accessors.size()) {
@ -1869,15 +2047,16 @@ Status GltfDecoder::DecodePrimitiveForScene(
DRACO_ASSIGN_OR_RETURN(
std::unique_ptr<Mesh> mesh,
BuildMeshFromBuilder(primitive.mode == TINYGLTF_MODE_TRIANGLES, &mb,
&pb));
BuildMeshFromBuilder(primitive.mode == TINYGLTF_MODE_TRIANGLES, &mb, &pb,
deduplicate_vertices_));
// Set all normalized flags for appropriate attributes.
for (const int32_t att_id : normalized_attributes) {
mesh->attribute(att_id)->set_normalized(true);
}
// Decode mesh feature ID sets if present in this primitive.
DRACO_RETURN_IF_ERROR(DecodeMeshFeatures(
// Decode extensions present in this primitive.
DRACO_RETURN_IF_ERROR(AddPrimitiveExtensionsToDracoMesh(
primitive, &scene_->GetMaterialLibrary().MutableTextureLibrary(),
mesh.get()));
@ -1973,6 +2152,33 @@ Status GltfDecoder::DecodeMeshFeatures(const tinygltf::Primitive &primitive,
return OkStatus();
}
Status GltfDecoder::DecodeStructuralMetadata(
const tinygltf::Primitive &primitive, Mesh *mesh) {
const auto &e = primitive.extensions.find("EXT_structural_metadata");
if (e == primitive.extensions.end()) {
return OkStatus();
}
std::vector<int> property_attributes_indices;
DRACO_RETURN_IF_ERROR(DecodeStructuralMetadata(
e->second.Get<tinygltf::Value::Object>(), &property_attributes_indices));
for (const int pai : property_attributes_indices) {
const int index = mesh->AddPropertyAttributesIndex(pai);
if (scene_ == nullptr) {
// If we are decoding to a mesh, we need to restrict the property
// attributes indices to the primitive's material.
// TODO(ostava): This will not work properly when two primitives share the
// same material but have different property attributes indices. We will
// need to duplicate the materials in this case.
const auto mat_it =
gltf_primitive_material_to_draco_material_.find(primitive.material);
if (mat_it != gltf_primitive_material_to_draco_material_.end()) {
mesh->AddPropertyAttributesIndexMaterialMask(index, mat_it->second);
}
}
}
return OkStatus();
}
Status GltfDecoder::DecodeMeshFeatures(
const tinygltf::Value::Object &extension, TextureLibrary *texture_library,
std::vector<std::unique_ptr<MeshFeatures>> *mesh_features) {
@ -2055,7 +2261,11 @@ Status GltfDecoder::DecodeMeshFeatures(
if (!value.IsInt()) {
return ErrorStatus("Attribute property is malformed.");
}
features.SetAttributeIndex(value.Get<int>());
// Convert index in feature ID vertex attribute name like _FEATURE_ID_5
// to attribute index in draco::Mesh.
const int att_name_index = value.Get<int>();
const int att_index = feature_id_attribute_indices_[att_name_index];
features.SetAttributeIndex(att_index);
}
}
{
@ -2066,7 +2276,7 @@ Status GltfDecoder::DecodeMeshFeatures(
return ErrorStatus("Texture property is malformed.");
}
// Decode texture contining mesh feature IDs into the |features| object
// Decode texture containing mesh feature IDs into the |features| object
// via a temporary |material| object.
Material material(texture_library);
const auto &container_object = object.Get<tinygltf::Value::Object>();
@ -2113,6 +2323,32 @@ Status GltfDecoder::DecodeMeshFeatures(
return OkStatus();
}
Status GltfDecoder::DecodeStructuralMetadata(
const tinygltf::Value::Object &extension,
std::vector<int> *property_attributes) {
// Decode all structural metadata from JSON like this in glTF primitive:
// "EXT_structural_metadata": {
// "propertyAttributes": [0]
// }
const auto &object = extension.find("propertyAttributes");
if (object == extension.end()) {
// TODO(vytyaz): Extension might contain property textures, support that.
return OkStatus();
}
const tinygltf::Value &array = object->second;
if (!array.IsArray()) {
return ErrorStatus("Property attributes array is malformed.");
}
for (int i = 0; i < array.Size(); i++) {
const auto &value = array.Get(i);
if (!value.IsInt()) {
return ErrorStatus("Property attributes array entry is malformed.");
}
property_attributes->push_back(value.Get<int>());
}
return OkStatus();
}
template <typename BuilderT>
StatusOr<int> GltfDecoder::AddAttribute(const std::string &attribute_name,
int component_type, int type,
@ -2150,9 +2386,54 @@ StatusOr<int> GltfDecoder::AddAttribute(GeometryAttribute::Type attribute_type,
if (att_id < 0) {
return Status(Status::DRACO_ERROR, "Could not add attribute.");
}
// When glTF is loaded as a mesh, initialize color attribute values to white
// opaque color. Mesh regions corresponding to glTF primitives without vertex
// color will end up having the white color. Since vertex color is used as a
// multiplier for material base color in rendering shader, the white color
// will keep the model appearance unchanged.
if (scene_ == nullptr && attribute_type == GeometryAttribute::Type::COLOR) {
SetWhiteVertexColor(att_id, draco_component_type, builder);
}
return att_id;
}
template <typename BuilderT>
void GltfDecoder::SetWhiteVertexColor(int color_att_id, draco::DataType type,
BuilderT *builder) {
// Valid glTF vertex color types are float, unsigned byte, and unsigned short.
if (type == DT_FLOAT32) {
SetWhiteVertexColorOfType<float>(color_att_id, builder);
} else if (type == DT_UINT8) {
SetWhiteVertexColorOfType<uint8_t>(color_att_id, builder);
} else if (type == DT_UINT16) {
SetWhiteVertexColorOfType<uint16_t>(color_att_id, builder);
}
}
template <typename ComponentT>
void GltfDecoder::SetWhiteVertexColorOfType(int color_att_id,
TriangleSoupMeshBuilder *builder) {
// The alpha component will not be copied for the RGB vertex colors.
std::array<ComponentT, 4> white{1, 1, 1, 1};
const int num_faces = total_face_indices_count_ / 3;
for (FaceIndex fi(0); fi < num_faces; fi++) {
builder->SetAttributeValuesForFace(color_att_id, fi, white.data(),
white.data(), white.data());
}
}
template <typename ComponentT>
void GltfDecoder::SetWhiteVertexColorOfType(int color_att_id,
PointCloudBuilder *builder) {
// The alpha component will not be copied for the RGB vertex colors.
std::array<ComponentT, 4> white{1, 1, 1, 1};
const int num_points = total_point_indices_count_;
for (PointIndex pi(0); pi < num_points; pi++) {
builder->SetAttributeValueForPoint(color_att_id, pi, white.data());
}
}
StatusOr<bool> GltfDecoder::CheckKhrTextureTransform(
const tinygltf::ExtensionMap &extension, TextureTransform *transform) {
bool transform_set = false;
@ -2870,12 +3151,13 @@ size_t GltfDecoder::PrimitiveSignature::Hash::operator()(
}
StatusOr<std::unique_ptr<Mesh>> GltfDecoder::BuildMeshFromBuilder(
bool use_mesh_builder, TriangleSoupMeshBuilder *mb, PointCloudBuilder *pb) {
bool use_mesh_builder, TriangleSoupMeshBuilder *mb, PointCloudBuilder *pb,
bool deduplicate_vertices) {
std::unique_ptr<Mesh> mesh;
if (use_mesh_builder) {
mesh = mb->Finalize();
} else {
std::unique_ptr<PointCloud> pc = pb->Finalize(true);
std::unique_ptr<PointCloud> pc = pb->Finalize(deduplicate_vertices);
if (pc) {
mesh.reset(new Mesh());
PointCloud *mesh_pc = mesh.get();
@ -2888,6 +3170,37 @@ StatusOr<std::unique_ptr<Mesh>> GltfDecoder::BuildMeshFromBuilder(
return mesh;
}
Status GltfDecoder::AddAssetMetadata(Scene *scene) {
return AddAssetMetadata(&scene->GetMetadata());
}
Status GltfDecoder::AddAssetMetadata(Mesh *mesh) {
Metadata *metadata = nullptr;
std::unique_ptr<GeometryMetadata> metadata_owned;
// Use metadata of the mesh or create new one.
if (mesh->GetMetadata() != nullptr) {
metadata = mesh->metadata();
} else {
metadata_owned = std::make_unique<GeometryMetadata>();
metadata = metadata_owned.get();
}
DRACO_RETURN_IF_ERROR(AddAssetMetadata(metadata));
if (metadata_owned != nullptr && metadata->num_entries() > 0) {
// Some metadata was added to the |metadata_owned| instance. Add it to the
// mesh.
mesh->AddMetadata(std::move(metadata_owned));
}
return OkStatus();
}
Status GltfDecoder::AddAssetMetadata(Metadata *metadata) {
// Store the copyright information in the |metadata|.
if (!gltf_model_.asset.copyright.empty()) {
metadata->AddEntryString("copyright", gltf_model_.asset.copyright);
}
return OkStatus();
}
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -71,6 +71,18 @@ class GltfDecoder {
gltf_scene_graph_mode_ = mode;
}
// By default, the decoder will attempt to deduplicate vertices after decoding
// the mesh. This means lower memory usage and smaller output glTFs after
// reencoding. However, for very large meshes, this may become an expensive
// operation. If that becomes an issue, you might want to consider disabling
// deduplication with |SetDeduplicateVertices(false)|.
//
// Note that at this moment, disabling deduplication works ONLY for point
// clouds.
void SetDeduplicateVertices(bool deduplicate_vertices) {
deduplicate_vertices_ = deduplicate_vertices;
}
private:
// Loads |file_name| into |gltf_model_|. Fills |input_files| with paths to all
// input files when non-null.
@ -192,6 +204,18 @@ class GltfDecoder {
const std::string &attribute_name,
BuilderT *builder);
// Copies the property attribute data from |accessor| and adds it to a
// Draco mesh. |indices_data| is the indices data from the glTF file. |att_id|
// is the attribute ID of the mesh feature ID attribute in the Draco mesh.
// |number_of_elements| is the number of faces or points this function will
// process. |reverse_winding| if set will change the orientation of the data.
template <typename BuilderT>
Status AddPropertyAttributeToBuilder(
const tinygltf::Accessor &accessor,
const std::vector<uint32_t> &indices_data, int att_id,
int number_of_elements, bool reverse_winding,
const std::string &attribute_name, BuilderT *builder);
// Copies the attribute data from |accessor| and adds it to a Draco mesh.
// This function will transform all of the data by |transform_matrix| before
// adding the data to the Draco mesh. |indices_data| is the indices data
@ -220,6 +244,16 @@ class GltfDecoder {
const std::vector<T> &data, bool reverse_winding,
PointCloudBuilder *builder);
// Sets colors of for all vertices to white.
template <typename BuilderT>
void SetWhiteVertexColor(int color_att_id, draco::DataType type,
BuilderT *builder);
template <typename ComponentT>
void SetWhiteVertexColorOfType(int color_att_id,
TriangleSoupMeshBuilder *builder);
template <typename ComponentT>
void SetWhiteVertexColorOfType(int color_att_id, PointCloudBuilder *builder);
// Sets values in |data| into the mesh builder |mb| for |att_id|.
// |reverse_winding| if set will change the orientation of the data.
template <typename T>
@ -306,28 +340,55 @@ class GltfDecoder {
const tinygltf::Value::Object &extension,
std::vector<MeshGroup::MaterialsVariantsMapping> *mappings);
// Decodes glTF mesh feature ID sets from all glTF primitives and adds them to
// |mesh|.
Status AddMeshFeaturesToDracoMesh(Mesh *mesh);
// Decodes glTF mesh feature ID sets from glTF primitive in glTF node at
// |node_index| and adds them to |mesh|.
Status AddMeshFeaturesToDracoMesh(int node_index, Mesh *mesh);
// Decode extensions on all primitives of all scenes, such as mesh features
// and structural metadata extensions, and add their contents to |mesh|.
Status AddPrimitiveExtensionsToDracoMesh(Mesh *mesh);
Status AddPrimitiveExtensionsToDracoMesh(int node_index, Mesh *mesh);
Status AddPrimitiveExtensionsToDracoMesh(const tinygltf::Primitive &primitive,
TextureLibrary *texture_library,
Mesh *mesh);
// Decodes glTF structural metadata from glTF model and adds it to |geometry|.
template <typename GeometryT>
Status AddStructuralMetadataToGeometry(GeometryT *geometry);
// Decodes glTF structural metadata schema from |extension| and adds it to
// |geometry|.
template <typename GeometryT>
Status AddStructuralMetadataSchemaToGeometry(
const tinygltf::Value::Object &extension, GeometryT *geometry);
// Decodes glTF structural metadata property tables from |extension| and adds
// them to |geometry|.
template <typename GeometryT>
Status AddPropertyTablesToGeometry(const tinygltf::Value::Object &extension,
GeometryT *geometry);
// Decodes glTF structural metadata property attributes from |extension| and
// adds them to |geometry|.
template <typename GeometryT>
Status AddPropertyAttributesToGeometry(
const tinygltf::Value::Object &extension, GeometryT *geometry);
// Decodes glTF mesh feature ID sets from |primitive| and adds them to |mesh|.
Status DecodeMeshFeatures(const tinygltf::Primitive &primitive,
TextureLibrary *texture_library, Mesh *mesh);
// Decodes glTF structural metadata from |primitive| and adds it to |mesh|.
Status DecodeStructuralMetadata(const tinygltf::Primitive &primitive,
Mesh *mesh);
// Decodes glTF mesh feature ID sets from |extension| and adds them to the
// |mesh_features| vector.
Status DecodeMeshFeatures(
const tinygltf::Value::Object &extension, TextureLibrary *texture_library,
std::vector<std::unique_ptr<MeshFeatures>> *mesh_features);
// Decodes glTF structural metadata from |extension| of a glTF primitive and
// adds its property attribute indices to the |property_attributes| vector.
Status DecodeStructuralMetadata(const tinygltf::Value::Object &extension,
std::vector<int> *property_attributes);
// Adds an attribute of type |attribute_name| to |builder|. Returns the
// attribute id.
template <typename BuilderT>
@ -427,6 +488,11 @@ class GltfDecoder {
// Adds the skins to the scene.
Status AddSkinsToScene();
// Adds various asset metadata to the scene or mesh.
Status AddAssetMetadata(Scene *scene);
Status AddAssetMetadata(Mesh *mesh);
Status AddAssetMetadata(Metadata *metadata);
// All material and non-material textures (e.g., from EXT_mesh_features) are
// initially loaded into a texture library inside the the material library.
// These methods move |non_material_textures| from material texture library
@ -441,8 +507,8 @@ class GltfDecoder {
// point cloud builder |pb|. Mesh builder is used if |use_mesh_builder| is set
// to true.
static StatusOr<std::unique_ptr<Mesh>> BuildMeshFromBuilder(
bool use_mesh_builder, TriangleSoupMeshBuilder *mb,
PointCloudBuilder *pb);
bool use_mesh_builder, TriangleSoupMeshBuilder *mb, PointCloudBuilder *pb,
bool deduplicate_vertices);
// Map of glTF Mesh to Draco scene mesh group.
std::map<int, MeshGroupIndex> gltf_mesh_to_scene_mesh_group_;
@ -457,6 +523,10 @@ class GltfDecoder {
TriangleSoupMeshBuilder mb_;
PointCloudBuilder pb_;
// Map from the index in a feature ID vertex attribute name like _FEATURE_ID_5
// to the corresponding attribute index in the current geometry builder.
std::unordered_map<int, int> feature_id_attribute_indices_;
// Next face index used when adding attribute data to the Draco mesh.
int next_face_id_;
@ -504,6 +574,9 @@ class GltfDecoder {
// Selected mode of the decoded scene graph.
GltfSceneGraphMode gltf_scene_graph_mode_ = GltfSceneGraphMode::TREE;
// Whether vertices should be deduplicated after loading.
bool deduplicate_vertices_ = true;
// Functionality for deduping primitives on decode.
struct PrimitiveSignature {
const tinygltf::Primitive &primitive;

View File

@ -14,9 +14,12 @@
//
#include "draco/io/gltf_decoder.h"
#include <array>
#include <cmath>
#include <iostream>
#include <limits>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
@ -738,17 +741,14 @@ TEST(GltfDecoderTest, TextureTransformTest) {
"KhronosSampleModels/TextureTransformTest/glTF/TextureTransformTest.gltf";
const std::unique_ptr<Mesh> mesh(DecodeGltfFile(filename));
EXPECT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 9);
for (int i = 0; i < 6; ++i) {
EXPECT_FALSE(TextureTransform::IsDefault(mesh->GetMaterialLibrary()
std::set<int> expected_default_transforms = {4, 5, 6};
for (int i = 0; i < 9; ++i) {
const bool expected_default = (expected_default_transforms.count(i) != 0);
EXPECT_EQ(TextureTransform::IsDefault(mesh->GetMaterialLibrary()
.GetMaterial(i)
->GetTextureMapByIndex(0)
->texture_transform()));
}
for (int i = 6; i < 9; ++i) {
EXPECT_TRUE(TextureTransform::IsDefault(mesh->GetMaterialLibrary()
.GetMaterial(i)
->GetTextureMapByIndex(0)
->texture_transform()));
->texture_transform()),
expected_default);
}
const std::unique_ptr<Scene> scene(DecodeGltfFileToScene(filename));
@ -1139,7 +1139,7 @@ TEST(GltfDecoderTest, CorrectVolumeThicknessFactor) {
ASSERT_NE(scene, nullptr);
auto instances = draco::SceneUtils::ComputeAllInstances(*scene);
ASSERT_EQ(instances.size(), 2);
ASSERT_EQ(instances[MeshInstanceIndex(0)].transform.col(0).norm(),
ASSERT_EQ(instances[MeshInstanceIndex(1)].transform.col(0).norm(),
kDragonScale);
ASSERT_EQ(scene->GetMaterialLibrary().GetMaterial(1)->GetThicknessFactor(),
kDragonVolumeThickness);
@ -1256,47 +1256,59 @@ TEST(GltfDecoderTest, MaterialsVariants) {
TEST(GltfDecoderTest, DecodeMeshWithMeshFeaturesWithStructuralMetadata) {
// Checks decoding of a simple glTF with mesh features and structural metadata
// property table as draco::Mesh.
constexpr bool kDracoCompressionEnabled = false;
const auto path = GetTestFileFullPath("BoxMeta/glTF/BoxMeta.gltf");
GltfTestHelper::UseCase use_case;
use_case.has_mesh_features = true;
use_case.has_structural_metadata = true;
draco::GltfDecoder decoder;
DRACO_ASSIGN_OR_ASSERT(auto mesh, decoder.DecodeFromFile(path));
ASSERT_NE(mesh, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh, kDracoCompressionEnabled);
GltfTestHelper::CheckBoxMetaStructuralMetadata(*mesh);
GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh, use_case);
GltfTestHelper::CheckBoxMetaStructuralMetadata(*mesh, use_case);
}
TEST(GltfDecoderTest, DecodeMeshWithMeshFeaturesWithDracoCompression) {
// Checks decoding of a simple glTF with mesh features compressed with Draco
// as draco::Mesh.
constexpr bool kDracoCompressionEnabled = true;
const auto path = GetTestFileFullPath("BoxMetaDraco/glTF/BoxMetaDraco.gltf");
GltfTestHelper::UseCase use_case;
use_case.has_draco_compression = true;
use_case.has_mesh_features = true;
draco::GltfDecoder decoder;
DRACO_ASSIGN_OR_ASSERT(auto mesh, decoder.DecodeFromFile(path));
ASSERT_NE(mesh, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh, kDracoCompressionEnabled);
GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh, use_case);
}
TEST(GltfDecoderTest, DecodeSceneWithMeshFeaturesWithStructuralMetadata) {
// Checks decoding of a simple glTF with mesh features and structural metadata
// property table as draco::Scene.
constexpr bool kHasDracoCompression = false;
const auto path = GetTestFileFullPath("BoxMeta/glTF/BoxMeta.gltf");
GltfTestHelper::UseCase use_case;
use_case.has_mesh_features = true;
use_case.has_structural_metadata = true;
draco::GltfDecoder decoder;
DRACO_ASSIGN_OR_ASSERT(auto scene, decoder.DecodeFromFileToScene(path));
ASSERT_NE(scene, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*scene, kHasDracoCompression);
GltfTestHelper::CheckBoxMetaStructuralMetadata(*scene);
GltfTestHelper::CheckBoxMetaMeshFeatures(*scene, use_case);
GltfTestHelper::CheckBoxMetaStructuralMetadata(*scene, use_case);
}
TEST(GltfDecoderTest, DecodeSceneWithMeshFeaturesWithDracoCompression) {
// Checks decoding of a simple glTF with mesh features compressed with Draco
// as draco::Scene.
constexpr bool kHasDracoCompression = true;
const auto path = GetTestFileFullPath("BoxMetaDraco/glTF/BoxMetaDraco.gltf");
GltfTestHelper::UseCase use_case;
use_case.has_draco_compression = true;
use_case.has_mesh_features = true;
draco::GltfDecoder decoder;
DRACO_ASSIGN_OR_ASSERT(auto scene, decoder.DecodeFromFileToScene(path));
ASSERT_NE(scene, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*scene, kHasDracoCompression);
GltfTestHelper::CheckBoxMetaMeshFeatures(*scene, use_case);
}
TEST(GltfDecoderTest, DecodePointCloudToMesh) {
@ -1316,6 +1328,10 @@ TEST(GltfDecoderTest, DecodePointCloudToMesh) {
ASSERT_EQ(mesh->NumNamedAttributes(draco::GeometryAttribute::TANGENT), 1);
ASSERT_EQ(mesh->NumNamedAttributes(draco::GeometryAttribute::MATERIAL), 1);
// Verify that vertex deduplication was performed
ASSERT_LT(mesh->GetNamedAttribute(draco::GeometryAttribute::NORMAL)->size(),
462);
// Check the point cloud has two materials.
ASSERT_EQ(mesh->GetNamedAttribute(draco::GeometryAttribute::MATERIAL)->size(),
2);
@ -1398,5 +1414,52 @@ TEST(GltfDecoderTest, TestLoadUnsupportedTexCoordAttributes) {
2);
}
TEST(GltfDecoderTest, TestInvertedMaterials) {
// Checks that GltfDecoder assigns materials properly to sub-meshes when the
// material indices are in reverse order in the input glTF.
auto mesh = draco::ReadMeshFromTestFile("two_objects_inverse_materials.gltf");
ASSERT_NE(mesh, nullptr);
ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 2);
// There are two sub-meshes. A cube with 12 faces and a sphere. The cube
// should be mapped to a "Red" material and the sphere to a "Green" material.
ASSERT_EQ(mesh->GetMaterialLibrary().GetMaterial(0)->GetName(), "Red");
ASSERT_EQ(mesh->GetMaterialLibrary().GetMaterial(1)->GetName(), "Green");
// Count the number of faces for each material index in the mesh.
std::array<int, 2> num_material_faces = {0, 0};
const draco::PointAttribute *const mat_att =
mesh->GetNamedAttribute(draco::GeometryAttribute::MATERIAL);
for (draco::FaceIndex i(0); i < mesh->num_faces(); ++i) {
const auto f = mesh->face(i);
uint32_t mat_index = 0;
mat_att->GetMappedValue(f[0], &mat_index);
ASSERT_TRUE(mat_index == 0 || mat_index == 1);
num_material_faces[mat_index]++;
}
// There should be 12 faces mapped to the red material (index 0), rest should
// be mapped to the green one.
ASSERT_EQ(num_material_faces[0], 12);
}
TEST(GltfDecoderTest, DecodePointCloudToMeshWithDeduplicationDisabled) {
// Checks that no deduplication is performed when it is explicitly disabled.
const auto path = GetTestFileFullPath(
"SphereTwoMaterials/sphere_two_materials_point_cloud.gltf");
draco::GltfDecoder decoder;
decoder.SetDeduplicateVertices(false);
DRACO_ASSIGN_OR_ASSERT(auto mesh, decoder.DecodeFromFile(path));
ASSERT_NE(mesh, nullptr);
// Check the point cloud has expected number of points and attributes.
ASSERT_EQ(mesh->num_faces(), 0);
ASSERT_EQ(mesh->num_points(), 462);
// Verify that no deduplication was performed.
ASSERT_EQ(mesh->GetNamedAttribute(draco::GeometryAttribute::NORMAL)->size(),
462);
}
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -45,6 +45,7 @@
#include "draco/mesh/mesh_features.h"
#include "draco/mesh/mesh_splitter.h"
#include "draco/mesh/mesh_utils.h"
#include "draco/metadata/property_attribute.h"
#include "draco/scene/instance_array.h"
#include "draco/scene/scene_indices.h"
#include "draco/scene/scene_utils.h"
@ -90,22 +91,46 @@ int TextureAxisWrappingModeToGltfValue(TextureMap::AxisWrappingMode mode) {
}
}
// Checks |att| metadata entry in |mesh| with key "attribute_name" and returns
// entry value if it begins with "_FEATURE_ID_", or an empty string otherwise.
std::string GetFeatureIdAttributeName(const PointAttribute &att,
const Mesh &mesh) {
const auto *const metadata =
mesh.GetAttributeMetadataByAttributeId(att.unique_id());
if (metadata) {
std::string attribute_name;
if (metadata->GetEntryString("attribute_name", &attribute_name)) {
constexpr char kPrefix[] = "_FEATURE_ID_";
if (attribute_name.rfind(kPrefix) == 0) {
return attribute_name;
// Returns a boolean indicating whether |mesh| attribute at |att_index| is a
// feature ID vertex attribute referred to by any of the feature ID sets stored
// in the |mesh|.
bool IsFeatureIdAttribute(int att_index, const Mesh &mesh) {
for (MeshFeaturesIndex i(0); i < mesh.NumMeshFeatures(); ++i) {
if (mesh.GetMeshFeatures(i).GetAttributeIndex() == att_index) {
return true;
}
}
return false;
}
// Returns a boolean indicating whether |mesh| attribute at |att_index| is a
// property attribute referred to by the |mesh| and its |structural_metadata|.
bool IsPropertyAttribute(int att_index, const Mesh &mesh,
const StructuralMetadata &structural_metadata) {
// First check if structural metadata has any property attributes.
if (structural_metadata.NumPropertyAttributes() == 0) {
return false;
}
// Property attribute name must start with an underscore like _DIRECTION.
const std::string attribute_name = mesh.attribute(att_index)->name();
if (attribute_name.rfind('_', 0) != 0) {
return false;
}
// Look for an |attribute_name| among all property attributes in the |mesh|.
for (int i = 0; i < mesh.NumPropertyAttributesIndices(); ++i) {
const int property_attribute_index = mesh.GetPropertyAttributesIndex(i);
const PropertyAttribute &attribute =
structural_metadata.GetPropertyAttribute(property_attribute_index);
for (int i = 0; i < attribute.NumProperties(); ++i) {
const PropertyAttribute::Property &property = attribute.GetProperty(i);
if (property.GetAttributeName() == attribute_name) {
return true;
}
}
}
return std::string();
return false;
}
// Struct to hold glTF Scene data.
@ -123,7 +148,7 @@ struct GltfNode {
root_node(false) {}
std::string name;
std::vector<int> childern_indices;
std::vector<int> children_indices;
int mesh_index;
int skin_index;
int light_index;
@ -220,8 +245,13 @@ struct GltfPrimitive {
int material;
std::vector<MeshGroup::MaterialsVariantsMapping> material_variants_mappings;
std::vector<const MeshFeatures *> mesh_features;
std::vector<int> property_attributes;
std::map<std::string, int> attributes;
GltfDracoCompressedMesh compressed_mesh_info;
// Map from the index of a feature ID vertex attribute in draco::Mesh to the
// index in the feature ID vertex attribute name like _FEATURE_ID_5.
std::unordered_map<int, int> feature_id_name_indices;
};
struct GltfMesh {
@ -249,6 +279,8 @@ class GltfAsset {
GltfAsset();
void set_copyright(const std::string &copyright) { copyright_ = copyright; }
std::string copyright() const { return copyright_; }
std::string generator() const { return generator_; }
std::string version() const { return version_; }
std::string buffer_name() const { return buffer_name_; }
@ -351,7 +383,8 @@ class GltfAsset {
int AddDracoJoints(const Mesh &mesh, int num_encoded_points);
int AddDracoWeights(const Mesh &mesh, int num_encoded_points);
std::vector<std::pair<std::string, int>> AddDracoGenerics(
const Mesh &mesh, int num_encoded_points);
const Mesh &mesh, int num_encoded_points,
std::unordered_map<int, int> *feature_id_name_indices);
// Iterate through the materials that are associated with |mesh| and add them
// to the asset. Returns true if |mesh| does not contain any materials or all
@ -515,9 +548,26 @@ class GltfAsset {
gltf_json_.EndArray();
}
// Add a mesh Draco attribute |att| that is comprised of floats to the glTF
// data. Returns the index accessor added to the glTF data. Returns -1 on
// error.
// Add a mesh Draco attribute |att| to the glTF data. Returns the index
// accessor added to the glTF data. Returns -1 on error.
int AddAttribute(const PointAttribute &att, int num_points,
int num_encoded_points, bool compress) {
const int nep = num_encoded_points;
switch (att.data_type()) {
case DT_UINT8:
return AddAttribute<uint8_t>(att, num_points, nep, compress);
case DT_UINT16:
return AddAttribute<uint16_t>(att, num_points, nep, compress);
case DT_FLOAT32:
return AddAttribute<float>(att, num_points, nep, compress);
default:
return -1;
}
}
// Add a mesh Draco attribute |att| that is comprised of |att_data_t| values
// to the glTF data. Returns the index accessor added to the glTF data.
// Returns -1 on error.
template <class att_data_t>
int AddAttribute(const PointAttribute &att, int num_points,
int num_encoded_points, bool compress) {
@ -554,6 +604,10 @@ class GltfAsset {
int num_encoded_points, const std::string &type,
bool compress);
void SetCopyrightFromScene(const Scene &scene);
void SetCopyrightFromMesh(const Mesh &mesh);
std::string copyright_;
std::string generator_;
std::string version_;
std::vector<GltfScene> scenes_;
@ -609,8 +663,7 @@ class GltfAsset {
std::vector<std::unique_ptr<Light>> lights_;
std::vector<std::string> materials_variants_names_;
std::vector<EncoderInstanceArray> instance_arrays_;
PropertyTable::Schema property_table_schema_;
std::vector<const PropertyTable *> property_tables_;
const StructuralMetadata *structural_metadata_;
// Indicates whether Draco compression is used for any of the asset meshes.
bool draco_compression_used_;
@ -618,6 +671,9 @@ class GltfAsset {
// Indicates whether mesh features are used.
bool mesh_features_used_;
// Indicates whether structural metadata is used.
bool structural_metadata_used_;
// Counter for naming mesh feature textures.
int mesh_features_texture_index_;
@ -667,8 +723,10 @@ GltfAsset::GltfAsset()
version_("2.0"),
scene_index_(-1),
buffer_name_("buffer0.bin"),
structural_metadata_(nullptr),
draco_compression_used_(false),
mesh_features_used_(false),
structural_metadata_used_(false),
mesh_features_texture_index_(0),
add_images_to_buffer_(false),
output_type_(GltfEncoder::COMPACT) {}
@ -686,6 +744,9 @@ bool GltfAsset::AddDracoMesh(const Mesh &mesh) {
meshes_.push_back(gltf_mesh);
AddStructuralMetadata(mesh);
if (copyright_.empty()) {
SetCopyrightFromMesh(mesh);
}
const int32_t material_att_id =
mesh.GetNamedAttributeId(GeometryAttribute::MATERIAL);
@ -713,6 +774,10 @@ bool GltfAsset::AddDracoMesh(const Mesh &mesh) {
// Copy over mesh features for a given material index.
Mesh::CopyMeshFeaturesForMaterial(mesh, split_meshes[i].get(), mat_index);
// Copy over property attributes indices for a given material index.
Mesh::CopyPropertyAttributesIndicesForMaterial(
mesh, split_meshes[i].get(), mat_index);
// Move the split mesh to a temporary storage of the GltfAsset. This will
// ensure the mesh will stay alive as long the asset needs it. We have to
// do this because the split mesh may contain mesh features data that are
@ -847,12 +912,19 @@ Status GltfAsset::CompressMeshWithDraco(const Mesh &mesh,
// Create Draco encoder.
EncoderBuffer buffer;
ExpertEncoder encoder(*mesh_copy);
encoder.SetTrackEncodedProperties(true);
std::unique_ptr<ExpertEncoder> encoder;
if (mesh_copy->num_faces() > 0) {
// Encode mesh.
encoder.reset(new ExpertEncoder(*mesh_copy));
} else {
return Status(Status::DRACO_ERROR,
"Draco compression is not supported for glTF point clouds.");
}
encoder->SetTrackEncodedProperties(true);
// Convert compression level to speed (that 0 = slowest, 10 = fastest).
const int speed = 10 - compression_options.compression_level;
encoder.SetSpeedOptions(speed, speed);
encoder->SetSpeedOptions(speed, speed);
// Configure attribute quantization.
for (int i = 0; i < mesh_copy->num_attributes(); ++i) {
@ -904,19 +976,19 @@ Status GltfAsset::CompressMeshWithDraco(const Mesh &mesh,
num_quantization_bits = compression_options.quantization_bits_weight;
break;
case GeometryAttribute::GENERIC:
if (GetFeatureIdAttributeName(*att, *mesh_copy).empty()) {
if (!IsFeatureIdAttribute(i, *mesh_copy)) {
num_quantization_bits =
compression_options.quantization_bits_generic;
} else {
// Quantization is explicitly disabled for feature ID attributes.
encoder.SetAttributeQuantization(i, -1);
encoder->SetAttributeQuantization(i, -1);
}
break;
default:
break;
}
if (num_quantization_bits > 0) {
encoder.SetAttributeQuantization(i, num_quantization_bits);
encoder->SetAttributeQuantization(i, num_quantization_bits);
}
}
}
@ -947,9 +1019,13 @@ Status GltfAsset::CompressMeshWithDraco(const Mesh &mesh,
// |compression_options| may have been modified and we need to update them
// before we start the encoding.
mesh_copy->SetCompressionOptions(compression_options);
DRACO_RETURN_IF_ERROR(encoder.EncodeToBuffer(&buffer));
*num_encoded_points = encoder.num_encoded_points();
*num_encoded_faces = encoder.num_encoded_faces();
DRACO_RETURN_IF_ERROR(encoder->EncodeToBuffer(&buffer));
*num_encoded_points = encoder->num_encoded_points();
if (mesh_copy->num_faces() > 0) {
*num_encoded_faces = encoder->num_encoded_faces();
} else {
*num_encoded_faces = 0;
}
const size_t buffer_start_offset = buffer_.size();
if (!buffer_.Encode(buffer.data(), buffer.size())) {
return Status(Status::DRACO_ERROR, "Could not copy Draco compressed data.");
@ -1056,7 +1132,8 @@ bool GltfAsset::AddDracoMesh(
const int joints_accessor_index = AddDracoJoints(mesh, num_encoded_points);
const int weights_accessor_index = AddDracoWeights(mesh, num_encoded_points);
const std::vector<std::pair<std::string, int>> generics_accessors =
AddDracoGenerics(mesh, num_encoded_points);
AddDracoGenerics(mesh, num_encoded_points,
&primitive.feature_id_name_indices);
if (num_encoded_faces == 0) {
primitive.mode = 0; // POINTS mode.
@ -1067,6 +1144,10 @@ bool GltfAsset::AddDracoMesh(
for (MeshFeaturesIndex i(0); i < mesh.NumMeshFeatures(); ++i) {
primitive.mesh_features.push_back(&mesh.GetMeshFeatures(i));
}
primitive.property_attributes.reserve(mesh.NumPropertyAttributesIndices());
for (int i = 0; i < mesh.NumPropertyAttributesIndices(); ++i) {
primitive.property_attributes.push_back(mesh.GetPropertyAttributesIndex(i));
}
primitive.indices = indices_index;
primitive.attributes.insert(
std::pair<std::string, int>("POSITION", position_index));
@ -1312,62 +1393,68 @@ int GltfAsset::AddDracoWeights(const Mesh &mesh, int num_encoded_points) {
mesh.IsCompressionEnabled());
}
// Adds generic attributes that have metadata describing the attribute name.
// This allows for export of application-specific attributes and feature ID
// attributes defined in glTF extension EXT_mesh_features. Returns a vector of
// attribute-name, accessor pairs for each valid attribute. The length of the
// vector is equal to the number of generic attributes. Vector entries
// corresponding to unsupported attributes (e.g., with no metadata) contain
// empty attribute names.
// Adds generic attributes that have metadata describing the attribute name,
// attributes referred to by one of the mesh feature ID sets or in the |mesh|,
// and attributes referred to by one of the property attributes in the |mesh|.
// This allows for export of application-specific attributes, feature ID
// attributes defined in glTF extension EXT_mesh_features, and property
// attributes defined in glTF extension EXT_structural_metadata. Returns a
// vector of attribute-name, accessor pairs for each valid attribute. Populates
// map from |mesh| attribute index in the feature ID attribute name like
// _FEATURE_ID_5 or _DIRECTION for each feature ID and property attribute in the
// |mesh|.
std::vector<std::pair<std::string, int>> GltfAsset::AddDracoGenerics(
const Mesh &mesh, int num_encoded_points) {
const int num_attributes =
const Mesh &mesh, int num_encoded_points,
std::unordered_map<int, int> *feature_id_name_indices) {
const int num_generic_attributes =
mesh.NumNamedAttributes(GeometryAttribute::GENERIC);
std::vector<std::pair<std::string, int>> attrs(num_attributes);
for (int i = 0; i < num_attributes; ++i) {
const PointAttribute *const att =
mesh.GetNamedAttribute(GeometryAttribute::GENERIC, i);
auto const *metadata =
mesh.GetAttributeMetadataByAttributeId(att->unique_id());
if (metadata) {
std::vector<std::pair<std::string, int>> attrs;
int feature_id_count = 0;
for (int i = 0; i < num_generic_attributes; ++i) {
const int att_index =
mesh.GetNamedAttributeId(GeometryAttribute::GENERIC, i);
const PointAttribute *const att = mesh.attribute(att_index);
std::string attr_name;
int accessor = -1;
auto const *metadata = mesh.GetAttributeMetadataByAttributeId(att_index);
if (metadata) {
if (metadata->GetEntryString(GltfEncoder::kDracoMetadataGltfAttributeName,
&attr_name)) {
if (att->data_type() == DT_FLOAT32) {
int accessor =
accessor =
AddAttribute<float>(*att, mesh.num_points(), num_encoded_points,
mesh.IsCompressionEnabled());
attrs[i] = {attr_name, accessor};
}
}
} else {
// Try to find feature ID attribute name like "_FEATURE_ID_5" then check
// that the attribute stores scalar values of complient data types as
// defined by the EXT_mesh_features glTF extension.
attr_name = GetFeatureIdAttributeName(*att, mesh);
if (!attr_name.empty() && att->num_components() == 1) {
int accessor = -1;
switch (att->data_type()) {
case DT_UINT8:
accessor = AddAttribute<uint8_t>(*att, mesh.num_points(),
num_encoded_points,
if (IsFeatureIdAttribute(att_index, mesh) && att->num_components() == 1) {
// This is an attribute referred to by one of the mesh feature ID sets
// as defined by the EXT_mesh_features glTF extension.
// TODO(vytyaz): Report an error if the number of components is not one.
accessor = AddAttribute(*att, mesh.num_points(), num_encoded_points,
mesh.IsCompressionEnabled());
break;
case DT_UINT16:
accessor = AddAttribute<uint16_t>(*att, mesh.num_points(),
num_encoded_points,
// Generate attribute name like _FEATURE_ID_N where N starts at 0 for
// the first feature ID vertex attribute and continues with consecutive
// positive integers as dictated by the EXT_mesh_features extension.
attr_name =
std::string("_FEATURE_ID_") + std::to_string(feature_id_count);
// Populate map from attribute index in the |mesh| to the index in a
// feature ID vertex attribute name like _FEATURE_ID_5.
(*feature_id_name_indices)[att_index] = feature_id_count;
feature_id_count++;
} else if (IsPropertyAttribute(att_index, mesh, *structural_metadata_)) {
// This is a property attribute as defined by the
// EXT_structural_metadata glTF extension.
accessor = AddAttribute(*att, mesh.num_points(), num_encoded_points,
mesh.IsCompressionEnabled());
break;
case DT_FLOAT32:
accessor = AddAttribute<float>(*att, mesh.num_points(),
num_encoded_points,
mesh.IsCompressionEnabled());
break;
default:
continue;
}
attrs[i] = {attr_name, accessor};
attr_name = att->name();
}
}
if (accessor != -1 && !attr_name.empty()) {
attrs.emplace_back(attr_name, accessor);
}
}
return attrs;
@ -1432,13 +1519,7 @@ StatusOr<int> GltfAsset::AddImage(const std::string &image_stem,
image.texture = texture;
image.owned_texture = std::move(owned_texture);
image.num_components = num_components;
// Always maintain the mime_type. Used elsewhere to determine image type.
if (extension == "jpg") {
image.mime_type = "image/jpeg";
} else {
image.mime_type = "image/" + extension;
}
image.mime_type = TextureUtils::GetTargetMimeType(*texture);
// For KTX2 with Basis compression, state that its extension is required.
if (extension == "ktx2") {
@ -1511,6 +1592,8 @@ Status GltfAsset::AddScene(const Scene &scene) {
if (!AddMaterials(scene)) {
return Status(Status::DRACO_ERROR, "Error adding materials to the scene.");
}
AddStructuralMetadata(scene);
// Initialize base mesh transforms that may be needed when the base meshes are
// compressed with Draco.
base_mesh_transforms_ = SceneUtils::FindLargestBaseMeshTransforms(scene);
@ -1526,7 +1609,9 @@ Status GltfAsset::AddScene(const Scene &scene) {
DRACO_RETURN_IF_ERROR(AddLights(scene));
DRACO_RETURN_IF_ERROR(AddMaterialsVariantsNames(scene));
DRACO_RETURN_IF_ERROR(AddInstanceArrays(scene));
AddStructuralMetadata(scene);
if (copyright_.empty()) {
SetCopyrightFromScene(scene);
}
return OkStatus();
}
@ -1542,7 +1627,7 @@ Status GltfAsset::AddSceneNode(const Scene &scene,
node.trs_matrix.Copy(scene_node->GetTrsMatrix());
for (int i = 0; i < scene_node->NumChildren(); ++i) {
node.childern_indices.push_back(scene_node->Child(i).value());
node.children_indices.push_back(scene_node->Child(i).value());
}
const MeshGroupIndex mesh_group_index = scene_node->GetMeshGroupIndex();
@ -1589,6 +1674,12 @@ Status GltfAsset::AddSceneNode(const Scene &scene,
for (MeshFeaturesIndex j(0); j < mesh.NumMeshFeatures(); ++j) {
primitive.mesh_features.push_back(&mesh.GetMeshFeatures(j));
}
primitive.property_attributes.reserve(
mesh.NumPropertyAttributesIndices());
for (int i = 0; i < mesh.NumPropertyAttributesIndices(); ++i) {
primitive.property_attributes.push_back(
mesh.GetPropertyAttributesIndex(i));
}
meshes_.back().primitives.push_back(primitive);
}
}
@ -1884,14 +1975,7 @@ Status GltfAsset::AddInstanceArrays(const Scene &scene) {
template <typename GeometryT>
void GltfAsset::AddStructuralMetadata(const GeometryT &geometry) {
const StructuralMetadata &structural_metadata =
geometry.GetStructuralMetadata();
if (!structural_metadata.GetPropertyTableSchema().Empty()) {
property_table_schema_ = structural_metadata.GetPropertyTableSchema();
for (int i = 0; i < structural_metadata.NumPropertyTables(); ++i) {
property_tables_.push_back(&structural_metadata.GetPropertyTable(i));
}
}
structural_metadata_ = &geometry.GetStructuralMetadata();
}
StatusOr<int> GltfAsset::AddData(const std::vector<float> &data,
@ -1972,6 +2056,9 @@ bool GltfAsset::EncodeAssetProperty(EncoderBuffer *buf_out) {
gltf_json_.BeginObject("asset");
gltf_json_.OutputValue("version", version_);
gltf_json_.OutputValue("generator", generator_);
if (!copyright_.empty()) {
gltf_json_.OutputValue("copyright", copyright_);
}
gltf_json_.EndObject();
const std::string asset_str = gltf_json_.MoveData();
@ -2044,10 +2131,10 @@ bool GltfAsset::EncodeNodesProperty(EncoderBuffer *buf_out) {
gltf_json_.EndObject();
}
if (!nodes_[i].childern_indices.empty()) {
if (!nodes_[i].children_indices.empty()) {
gltf_json_.BeginArray("children");
for (int j = 0; j < nodes_[i].childern_indices.size(); ++j) {
gltf_json_.OutputValue(nodes_[i].childern_indices[j]);
for (int j = 0; j < nodes_[i].children_indices.size(); ++j) {
gltf_json_.OutputValue(nodes_[i].children_indices[j]);
}
gltf_json_.EndArray();
}
@ -2170,9 +2257,10 @@ Status GltfAsset::EncodePrimitiveExtensionsProperty(
primitive.compressed_mesh_info.buffer_view_index >= 0;
const bool has_materials_variants =
!primitive.material_variants_mappings.empty();
const bool has_structural_metadata = !primitive.property_attributes.empty();
const bool has_mesh_features = !primitive.mesh_features.empty();
if (!has_draco_mesh_compression && !has_materials_variants &&
!has_mesh_features) {
!has_mesh_features && !has_structural_metadata) {
return OkStatus();
}
@ -2216,7 +2304,10 @@ Status GltfAsset::EncodePrimitiveExtensionsProperty(
}
gltf_json_.OutputValue("featureCount", features->GetFeatureCount());
if (features->GetAttributeIndex() != -1) {
gltf_json_.OutputValue("attribute", features->GetAttributeIndex());
// Index referring to mesh feature ID attribute name like _FEATURE_ID_5.
const int index =
primitive.feature_id_name_indices.at(features->GetAttributeIndex());
gltf_json_.OutputValue("attribute", index);
}
if (features->GetPropertyTableIndex() != -1) {
gltf_json_.OutputValue("propertyTable",
@ -2250,6 +2341,16 @@ Status GltfAsset::EncodePrimitiveExtensionsProperty(
gltf_json_.EndArray(); // featureIds array.
gltf_json_.EndObject(); // EXT_mesh_features entry.
}
if (has_structural_metadata) {
structural_metadata_used_ = true;
gltf_json_.BeginObject("EXT_structural_metadata");
gltf_json_.BeginArray("propertyAttributes");
for (const int property_attribute_index : primitive.property_attributes) {
gltf_json_.OutputValue(property_attribute_index);
}
gltf_json_.EndArray(); // propertyAttributes array.
gltf_json_.EndObject(); // EXT_structural_metadata entry.
}
gltf_json_.EndObject(); // extensions entry.
return OkStatus();
}
@ -2963,7 +3064,8 @@ Status GltfAsset::EncodeSkinsProperty(EncoderBuffer *buf_out) {
Status GltfAsset::EncodeTopLevelExtensionsProperty(EncoderBuffer *buf_out) {
// Return if there are no top-level asset extensions to encode.
if (lights_.empty() && materials_variants_names_.empty() &&
property_tables_.empty()) {
structural_metadata_->NumPropertyTables() == 0 &&
structural_metadata_->NumPropertyAttributes() == 0) {
return OkStatus();
}
@ -3048,38 +3150,39 @@ Status GltfAsset::EncodeMaterialsVariantsNamesProperty(EncoderBuffer *buf_out) {
}
Status GltfAsset::EncodeStructuralMetadataProperty(EncoderBuffer *buf_out) {
if (property_table_schema_.Empty()) {
if (structural_metadata_->GetSchema().Empty()) {
return OkStatus();
}
structural_metadata_used_ = true;
gltf_json_.BeginObject("EXT_structural_metadata");
// Encodes property table schema.
// Encodes structural metadata schema.
struct SchemaWriter {
static void Write(const PropertyTable::Schema::Object &object,
JsonWriter *json_writer) {
typedef StructuralMetadataSchema::Object Object;
static void Write(const Object &object, JsonWriter *json_writer) {
switch (object.GetType()) {
case PropertyTable::Schema::Object::OBJECT:
case Object::OBJECT:
json_writer->BeginObject(object.GetName());
for (const PropertyTable::Schema::Object &obj : object.GetObjects()) {
for (const Object &obj : object.GetObjects()) {
Write(obj, json_writer);
}
json_writer->EndObject();
break;
case PropertyTable::Schema::Object::ARRAY:
case Object::ARRAY:
json_writer->BeginArray(object.GetName());
for (const PropertyTable::Schema::Object &obj : object.GetArray()) {
for (const Object &obj : object.GetArray()) {
Write(obj, json_writer);
}
json_writer->EndArray();
break;
case PropertyTable::Schema::Object::STRING:
case Object::STRING:
json_writer->OutputValue(object.GetName(), object.GetString());
break;
case PropertyTable::Schema::Object::INTEGER:
case Object::INTEGER:
json_writer->OutputValue(object.GetName(), object.GetInteger());
break;
case PropertyTable::Schema::Object::BOOLEAN:
case Object::BOOLEAN:
json_writer->OutputValue(object.GetName(), object.GetBoolean());
break;
}
@ -3087,11 +3190,13 @@ Status GltfAsset::EncodeStructuralMetadataProperty(EncoderBuffer *buf_out) {
};
// Encode property table schema.
SchemaWriter::Write(property_table_schema_.json, &gltf_json_);
SchemaWriter::Write(structural_metadata_->GetSchema().json, &gltf_json_);
// Encode all property tables.
gltf_json_.BeginArray("propertyTables");
for (const PropertyTable *const table : property_tables_) {
for (int i = 0; i < structural_metadata_->NumPropertyTables(); i++) {
const PropertyTable *const table =
&structural_metadata_->GetPropertyTable(i);
gltf_json_.BeginObject();
if (!table->GetName().empty()) {
gltf_json_.OutputValue("name", table->GetName());
@ -3139,6 +3244,32 @@ Status GltfAsset::EncodeStructuralMetadataProperty(EncoderBuffer *buf_out) {
gltf_json_.EndObject();
}
gltf_json_.EndArray(); // propertyTables entry.
// Encode all property attributes.
gltf_json_.BeginArray("propertyAttributes");
for (int i = 0; i < structural_metadata_->NumPropertyAttributes(); i++) {
const PropertyAttribute *const attribute =
&structural_metadata_->GetPropertyAttribute(i);
gltf_json_.BeginObject();
if (!attribute->GetName().empty()) {
gltf_json_.OutputValue("name", attribute->GetName());
}
if (!attribute->GetClass().empty()) {
gltf_json_.OutputValue("class", attribute->GetClass());
}
// Encoder all property attribute properties.
gltf_json_.BeginObject("properties");
for (int i = 0; i < attribute->NumProperties(); ++i) {
const PropertyAttribute::Property &property = attribute->GetProperty(i);
gltf_json_.BeginObject(property.GetName());
gltf_json_.OutputValue("attribute", property.GetAttributeName());
gltf_json_.EndObject(); // Named property entry.
}
gltf_json_.EndObject(); // properties entry.
gltf_json_.EndObject();
}
gltf_json_.EndArray(); // propertyAttributes entry.
gltf_json_.EndObject(); // EXT_structural_metadata entry.
return OkStatus();
}
@ -3246,7 +3377,7 @@ Status GltfAsset::EncodeExtensionsProperties(EncoderBuffer *buf_out) {
if (mesh_features_used_) {
extensions_used_.insert("EXT_mesh_features");
}
if (!property_table_schema_.Empty()) {
if (structural_metadata_used_) {
extensions_used_.insert("EXT_structural_metadata");
}
@ -3383,6 +3514,20 @@ int GltfAsset::AddAttribute(const PointAttribute &att, int num_points,
return static_cast<int>(accessors_.size() - 1);
}
void GltfAsset::SetCopyrightFromScene(const Scene &scene) {
std::string copyright;
scene.GetMetadata().GetEntryString("copyright", &copyright);
set_copyright(copyright);
}
void GltfAsset::SetCopyrightFromMesh(const Mesh &mesh) {
if (mesh.GetMetadata() != nullptr) {
std::string copyright;
mesh.GetMetadata()->GetEntryString("copyright", &copyright);
set_copyright(copyright);
}
}
const char GltfEncoder::kDracoMetadataGltfAttributeName[] =
"//GLTF/ApplicationSpecificAttributeName";
@ -3436,6 +3581,7 @@ Status GltfEncoder::EncodeFile(const T &geometry, const std::string &filename,
}
GltfAsset gltf_asset;
gltf_asset.set_copyright(copyright_);
gltf_asset.set_output_type(output_type_);
if (extension == "gltf") {
@ -3465,6 +3611,7 @@ Status GltfEncoder::EncodeToBuffer(const T &geometry,
gltf_asset.set_output_type(output_type_);
gltf_asset.buffer_name("");
gltf_asset.set_add_images_to_buffer(true);
gltf_asset.set_copyright(copyright_);
// Encode the geometry into a buffer.
EncoderBuffer buffer;

View File

@ -84,6 +84,9 @@ class GltfEncoder {
void set_output_type(OutputType type) { output_type_ = type; }
OutputType output_type() const { return output_type_; }
void set_copyright(const std::string &copyright) { copyright_ = copyright; }
std::string copyright() const { return copyright_; }
// The name of the attribute metadata that contains the glTF attribute
// name. For application-specific generic attributes, if the metadata for
// an attribute contains this key, then the value will be used as the
@ -126,6 +129,7 @@ class GltfEncoder {
EncoderBuffer *out_buffer_;
OutputType output_type_;
std::string copyright_;
};
} // namespace draco

View File

@ -15,10 +15,15 @@
#include "draco/io/gltf_encoder.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include <array>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
#include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h"
@ -1278,7 +1283,7 @@ TEST_F(GltfEncoderTest, PbrNextExtensions) {
const std::string tmp_name = draco::GetTestTempFileFullPath("tmp.gltf");
DRACO_ASSERT_OK(encoder.EncodeFile<Scene>(*original, tmp_name));
// Read model from the temporay file.
// Read model from the temporary file.
GltfDecoder decoder;
DRACO_ASSIGN_OR_ASSERT(auto encoded, decoder.DecodeFromFileToScene(tmp_name));
ASSERT_NE(encoded, nullptr);
@ -1483,6 +1488,44 @@ TEST_F(GltfEncoderTest, EncodeToBuffer) {
ASSERT_EQ(std::memcmp(file_data.data(), buffer.data(), buffer.size()), 0);
}
TEST_F(GltfEncoderTest, CopyrightAssetIsEncoded) {
// Load scene from file.
const std::string file_name = "CesiumMilkTruck/glTF/CesiumMilkTruck.gltf";
const std::unique_ptr<Scene> scene = ReadSceneFromTestFile(file_name);
ASSERT_NE(scene, nullptr);
std::array<std::pair<std::string, std::string>, 3> test_cases = {
{{"Google", "Google"}, {"", ""}, {"GMaps", ""}}};
for (const std::pair<std::string, std::string> &test_case : test_cases) {
// Encode scene to buffer in GLB format.
GltfEncoder encoder;
encoder.set_copyright(test_case.first);
EncoderBuffer buffer;
DRACO_ASSERT_OK(encoder.EncodeToBuffer(*scene, &buffer));
ASSERT_NE(buffer.size(), 0);
const std::string glb_file_path =
draco::GetTestTempFileFullPath(test_case.first + "temp.glb");
std::string folder_path;
std::string glb_file_name;
draco::SplitPath(glb_file_path, &folder_path, &glb_file_name);
encoder.set_copyright(test_case.second);
encoder.EncodeToFile<Scene>(*scene, glb_file_path, folder_path);
std::vector<char> file_data;
ASSERT_TRUE(ReadFileToBuffer(glb_file_path, &file_data));
if (test_case.first == test_case.second) {
ASSERT_EQ(buffer.size(), draco::GetFileSize(glb_file_path))
<< glb_file_path;
ASSERT_EQ(std::memcmp(file_data.data(), buffer.data(), buffer.size()), 0);
} else {
ASSERT_NE(buffer.size(), draco::GetFileSize(glb_file_path))
<< glb_file_path;
}
}
}
// Tests that a scene with lights can be encoded into a file.
TEST_F(GltfEncoderTest, EncodeLights) {
const std::string file_name = "sphere_lights.gltf";
@ -1565,9 +1608,9 @@ TEST_F(GltfEncoderTest, EncodeMaterialsVariants) {
// structural metadata property table.
TEST_F(GltfEncoderTest, EncodeSceneWithMeshFeaturesWithStructuralMetadata) {
const std::string file_name = "BoxMeta/glTF/BoxMeta.gltf";
constexpr bool kHasMeshFeatures = true;
constexpr bool kHasStructuralMetadata = true;
constexpr bool kHasDracoCompression = false;
GltfTestHelper::UseCase use_case;
use_case.has_mesh_features = true;
use_case.has_structural_metadata = true;
// Read test file from file.
const std::unique_ptr<Scene> scene(DecodeTestGltfFileToScene(file_name));
@ -1577,18 +1620,17 @@ TEST_F(GltfEncoderTest, EncodeSceneWithMeshFeaturesWithStructuralMetadata) {
std::unique_ptr<Scene> scene_from_gltf;
SceneToDecodedGltfScene(*scene, &scene_from_gltf);
ASSERT_NE(scene_from_gltf, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*scene_from_gltf,
kHasDracoCompression);
GltfTestHelper::CheckBoxMetaStructuralMetadata(*scene_from_gltf);
GltfTestHelper::CheckBoxMetaMeshFeatures(*scene_from_gltf, use_case);
GltfTestHelper::CheckBoxMetaStructuralMetadata(*scene_from_gltf, use_case);
}
// Tests encoding of draco::Scene with Draco compression to glTF with various
// mesh feature ID sets.
TEST_F(GltfEncoderTest, EncodeSceneWithMeshFeaturesWithDracoCompression) {
const std::string file_name = "BoxMetaDraco/glTF/BoxMetaDraco.gltf";
constexpr bool kHasMeshFeatures = true;
constexpr bool kHasStructuralMetadata = false;
constexpr bool kHasDracoCompression = true;
GltfTestHelper::UseCase use_case;
use_case.has_draco_compression = true;
use_case.has_mesh_features = true;
// Read test file from file.
const std::unique_ptr<Scene> scene(DecodeTestGltfFileToScene(file_name));
@ -1598,15 +1640,16 @@ TEST_F(GltfEncoderTest, EncodeSceneWithMeshFeaturesWithDracoCompression) {
std::unique_ptr<Scene> scene_from_gltf;
SceneToDecodedGltfScene(*scene, &scene_from_gltf);
ASSERT_NE(scene_from_gltf, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*scene_from_gltf,
kHasDracoCompression);
GltfTestHelper::CheckBoxMetaMeshFeatures(*scene_from_gltf, use_case);
}
// Tests encoding of draco::Mesh to glTF with various mesh feature ID sets and
// structural metadata property table.
TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithStructuralMetadata) {
const std::string file_name = "BoxMeta/glTF/BoxMeta.gltf";
constexpr bool kHasDracoCompression = false;
GltfTestHelper::UseCase use_case;
use_case.has_mesh_features = true;
use_case.has_structural_metadata = true;
// Read test file from file.
const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name));
@ -1616,16 +1659,17 @@ TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithStructuralMetadata) {
std::unique_ptr<Mesh> mesh_from_gltf;
MeshToDecodedGltfMesh(*mesh, &mesh_from_gltf);
ASSERT_NE(mesh_from_gltf, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh_from_gltf,
kHasDracoCompression);
GltfTestHelper::CheckBoxMetaStructuralMetadata(*mesh_from_gltf);
GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh_from_gltf, use_case);
GltfTestHelper::CheckBoxMetaStructuralMetadata(*mesh_from_gltf, use_case);
}
// Tests encoding of draco::Mesh with Draco compression to glTF with various
// mesh feature ID sets.
TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithDracoCompression) {
constexpr bool kHasDracoCompression = true;
const std::string file_name = "BoxMetaDraco/glTF/BoxMetaDraco.gltf";
GltfTestHelper::UseCase use_case;
use_case.has_draco_compression = true;
use_case.has_mesh_features = true;
// Read test file from file.
const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name));
@ -1635,8 +1679,37 @@ TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithDracoCompression) {
std::unique_ptr<Mesh> mesh_from_gltf;
MeshToDecodedGltfMesh(*mesh, &mesh_from_gltf);
ASSERT_NE(mesh_from_gltf, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh_from_gltf,
kHasDracoCompression);
GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh_from_gltf, use_case);
}
// This test verifies that b/245519530 is fixed. It loads mesh with various mesh
// feature ID sets, enables Draco compression, converts mesh to scene, and
// encodes the scene to glTF.
TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithDracoCompressionAsScene) {
// Note that although the mesh is loaded from file with no Draco compression,
// the compression is enabled later on.
const std::string file_name = "BoxMeta/glTF/BoxMeta.gltf";
GltfTestHelper::UseCase use_case;
use_case.has_draco_compression = true;
use_case.has_mesh_features = true;
use_case.has_structural_metadata = true;
// Read test file from file.
std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name));
ASSERT_NE(mesh, nullptr);
// Enable Draco compression.
mesh->SetCompressionEnabled(use_case.has_draco_compression);
// Convert mesh to scene.
std::unique_ptr<Scene> scene =
draco::SceneUtils::MeshToScene(std::move(mesh)).value();
// Encode the scene to glTF and decode it back to draco::Scene and check.
std::unique_ptr<Scene> scene_from_gltf;
SceneToDecodedGltfScene(*scene, &scene_from_gltf);
ASSERT_NE(scene_from_gltf, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*scene_from_gltf, use_case);
}
// Tests encoding of draco::Mesh with mesh features associated with different
@ -1688,6 +1761,49 @@ TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithMultiplePrimitives) {
ASSERT_EQ(scene_from_gltf->GetNonMaterialTextureLibrary().NumTextures(), 2);
}
// Tests encoding of draco::Mesh with property attributes associated with
// different mesh primitives.
TEST_F(GltfEncoderTest,
EncodeMeshWithPropertyAttributesWithMultiplePrimitives) {
const std::string file_name = "BoxesMeta/glTF/BoxesMeta.gltf";
// Read test file from file.
const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name));
ASSERT_NE(mesh, nullptr);
ASSERT_EQ(mesh->NumPropertyAttributesIndices(), 2);
// Encode the scene to glTF and decode it back to draco::Mesh and check.
std::unique_ptr<Mesh> mesh_from_gltf;
MeshToDecodedGltfMesh(*mesh, &mesh_from_gltf);
ASSERT_NE(mesh_from_gltf, nullptr);
ASSERT_EQ(mesh_from_gltf->GetMaterialLibrary().NumMaterials(), 2);
ASSERT_EQ(mesh_from_gltf->NumPropertyAttributesIndices(), 2);
// First property attribute should be used by material 0 and the second by
// material 1.
ASSERT_EQ(mesh_from_gltf->NumPropertyAttributesIndexMaterialMasks(0), 1);
ASSERT_EQ(mesh_from_gltf->NumPropertyAttributesIndexMaterialMasks(1), 1);
ASSERT_EQ(mesh_from_gltf->GetPropertyAttributesIndexMaterialMask(0, 0), 0);
ASSERT_EQ(mesh_from_gltf->GetPropertyAttributesIndexMaterialMask(1, 0), 1);
// Ensure it still works correctly when we re-encode the source |mesh| as a
// scene.
std::unique_ptr<Scene> scene_from_gltf;
MeshToDecodedGltfScene(*mesh, &scene_from_gltf);
ASSERT_NE(scene_from_gltf, nullptr);
ASSERT_EQ(scene_from_gltf->NumMeshes(), 2);
// Both meshes should have one property attributes indices.
ASSERT_EQ(scene_from_gltf->GetMesh(draco::MeshIndex(0))
.NumPropertyAttributesIndices(),
1);
ASSERT_EQ(scene_from_gltf->GetMesh(draco::MeshIndex(1))
.NumPropertyAttributesIndices(),
1);
}
// Tests encoding of draco::Mesh containing a point cloud and two materials.
TEST_F(GltfEncoderTest, EncodePointCloudWithMaterials) {
const std::string file_name =

View File

@ -20,8 +20,10 @@
#include <utility>
#include <vector>
#include "draco/attributes/geometry_attribute.h"
#include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h"
#include "draco/metadata/property_attribute.h"
#include "draco/metadata/property_table.h"
#include "draco/texture/texture_library.h"
@ -59,9 +61,6 @@ void GltfTestHelper::AddBoxMetaMeshFeatures(Scene *scene) {
pa->SetAttributeValue(avi, &val);
}
const int att_id = mesh.AddPerFaceAttribute(std::move(pa));
std::unique_ptr<AttributeMetadata> metadata(new AttributeMetadata());
metadata->AddEntryString("attribute_name", "_FEATURE_ID_0");
mesh.AddAttributeMetadata(att_id, std::move(metadata));
// Add feature ID set to the mesh.
std::unique_ptr<MeshFeatures> features(new MeshFeatures());
@ -69,7 +68,7 @@ void GltfTestHelper::AddBoxMetaMeshFeatures(Scene *scene) {
features->SetFeatureCount(num_faces);
features->SetNullFeatureId(100);
features->SetPropertyTableIndex(0);
features->SetAttributeIndex(0);
features->SetAttributeIndex(att_id);
mesh.AddMeshFeatures(std::move(features));
}
@ -84,9 +83,6 @@ void GltfTestHelper::AddBoxMetaMeshFeatures(Scene *scene) {
pa->SetAttributeValue(avi, &val);
}
const int att_id = mesh.AddPerVertexAttribute(std::move(pa));
std::unique_ptr<AttributeMetadata> metadata(new AttributeMetadata());
metadata->AddEntryString("attribute_name", "_FEATURE_ID_1");
mesh.AddAttributeMetadata(att_id, std::move(metadata));
// Add feature ID set to the mesh.
std::unique_ptr<MeshFeatures> features(new MeshFeatures());
@ -94,7 +90,7 @@ void GltfTestHelper::AddBoxMetaMeshFeatures(Scene *scene) {
features->SetFeatureCount(num_vertices);
features->SetNullFeatureId(101);
features->SetPropertyTableIndex(1);
features->SetAttributeIndex(1);
features->SetAttributeIndex(att_id);
mesh.AddMeshFeatures(std::move(features));
}
@ -113,14 +109,11 @@ void GltfTestHelper::AddBoxMetaMeshFeatures(Scene *scene) {
}
const int att_id =
mesh.AddAttributeWithConnectivity(std::move(pa), corner_to_value);
std::unique_ptr<AttributeMetadata> metadata(new AttributeMetadata());
metadata->AddEntryString("attribute_name", "_FEATURE_ID_2");
mesh.AddAttributeMetadata(att_id, std::move(metadata));
// Add feature ID set to the mesh.
std::unique_ptr<MeshFeatures> features(new MeshFeatures());
features->SetFeatureCount(num_corners);
features->SetAttributeIndex(2);
features->SetAttributeIndex(att_id);
mesh.AddMeshFeatures(std::move(features));
}
@ -163,7 +156,7 @@ void GltfTestHelper::AddBoxMetaMeshFeatures(Scene *scene) {
}
void GltfTestHelper::AddBoxMetaStructuralMetadata(Scene *scene) {
// Add structural metadata property table schema in the following JSON:
// Add structural metadata schema in the following JSON:
// "schema": {
// "id": "galaxy",
// "classes": {
@ -181,11 +174,30 @@ void GltfTestHelper::AddBoxMetaStructuralMetadata(Scene *scene) {
// "type": "STRING"
// }
// "sequence": {
// "componentType": "FLOAT32",
// "description": "The number sequence.",
// "required": false,
// "type": "SCALAR"
// }
// }
// },
// "movement": {
// "name": "The movement.",
// "description": "Vertex movement.",
// "properties": {
// "direction": {
// "description": "Movement direction.",
// "type": "VEC3",
// "componentType": "FLOAT32",
// "required": true
// },
// "magnitude": {
// "description": "Movement magnitude.",
// "type": "SCALAR",
// "componentType": "FLOAT32",
// "required": true
// }
// }
// }
// },
// "enums": {
@ -201,12 +213,27 @@ void GltfTestHelper::AddBoxMetaStructuralMetadata(Scene *scene) {
// ]
// }
// }
// },
// "propertyAttributes": [{
// "name": "The movement.",
// "class": "movement",
// "properties": {
// "direction": {
// "attribute": "_DIRECTION",
// },
// "magnitude": {
// "attribute": "_MAGNITUDE",
// }
typedef PropertyTable::Schema::Object Object;
PropertyTable::Schema schema;
// }
// }]
typedef StructuralMetadataSchema::Object Object;
StructuralMetadataSchema schema;
Object &json = schema.json;
json.SetObjects().emplace_back("id", "galaxy");
json.SetObjects().emplace_back("classes");
// Add class "planet" to schema.
{
json.SetObjects().back().SetObjects().emplace_back("planet");
Object &planet = json.SetObjects().back().SetObjects().back();
planet.SetObjects().emplace_back("properties");
@ -227,9 +254,35 @@ void GltfTestHelper::AddBoxMetaStructuralMetadata(Scene *scene) {
properties.SetObjects().emplace_back("sequence");
Object &sequence = properties.SetObjects().back();
sequence.SetObjects().emplace_back("componentType", "FLOAT32");
sequence.SetObjects().emplace_back("description", "The number sequence.");
sequence.SetObjects().emplace_back("required", false);
sequence.SetObjects().emplace_back("type", "SCALAR");
}
// Add class "movement" to schema.
{
json.SetObjects().back().SetObjects().emplace_back("movement");
Object &movement = json.SetObjects().back().SetObjects().back();
movement.SetObjects().emplace_back("name", "The movement.");
movement.SetObjects().emplace_back("description", "Vertex movement.");
movement.SetObjects().emplace_back("properties");
Object &properties = movement.SetObjects().back();
properties.SetObjects().emplace_back("direction");
Object &direction = properties.SetObjects().back();
direction.SetObjects().emplace_back("componentType", "FLOAT32");
direction.SetObjects().emplace_back("description", "Movement direction.");
direction.SetObjects().emplace_back("required", true);
direction.SetObjects().emplace_back("type", "VEC3");
properties.SetObjects().emplace_back("magnitude");
Object &mag = properties.SetObjects().back();
mag.SetObjects().emplace_back("componentType", "FLOAT32");
mag.SetObjects().emplace_back("description", "Movement magnitude.");
mag.SetObjects().emplace_back("required", true);
mag.SetObjects().emplace_back("type", "SCALAR");
}
json.SetObjects().emplace_back("enums");
json.SetObjects().back().SetObjects().emplace_back("classifications");
@ -260,8 +313,8 @@ void GltfTestHelper::AddBoxMetaStructuralMetadata(Scene *scene) {
values.SetArray().back().SetObjects().emplace_back("name", "Ordnance");
values.SetArray().back().SetObjects().emplace_back("value", 4);
// Add property table schema to the scene.
scene->GetStructuralMetadata().SetPropertyTableSchema(schema);
// Add structural metadata schema to the scene.
scene->GetStructuralMetadata().SetSchema(schema);
// Add structural metadata property table.
std::unique_ptr<PropertyTable> table(new PropertyTable());
@ -334,9 +387,9 @@ void GltfTestHelper::AddBoxMetaStructuralMetadata(Scene *scene) {
212, 0, 0, 0, // Mustafar
232, 0, 0, 0, // Bespin
250, 0, 0, 0, // Yavin
12, 1, 0, 0, // Geonosis
32, 1, 0, 0, // UNLABELED
41, 1, 0, 0};
11, 1, 0, 0, // Geonosis
31, 1, 0, 0, // UNLABELED
40, 1, 0, 0};
table->AddProperty(std::move(property));
}
@ -391,36 +444,95 @@ void GltfTestHelper::AddBoxMetaStructuralMetadata(Scene *scene) {
// Add property table to the scene.
scene->GetStructuralMetadata().AddPropertyTable(std::move(table));
// Add structural metadata property attribute.
std::unique_ptr<PropertyAttribute> attribute(new PropertyAttribute());
attribute->SetName("The movement.");
attribute->SetClass("movement");
{
std::unique_ptr<PropertyAttribute::Property> property(
new PropertyAttribute::Property());
property->SetName("direction");
property->SetAttributeName("_DIRECTION");
attribute->AddProperty(std::move(property));
}
{
std::unique_ptr<PropertyAttribute::Property> property(
new PropertyAttribute::Property());
property->SetName("magnitude");
property->SetAttributeName("_MAGNITUDE");
attribute->AddProperty(std::move(property));
}
scene->GetStructuralMetadata().AddPropertyAttribute(std::move(attribute));
// Get mesh element counts.
Mesh &mesh = scene->GetMesh(MeshIndex(0));
ASSERT_EQ(mesh.num_faces(), 12);
ASSERT_EQ(mesh.num_points(), 36);
const int num_vertices =
mesh.GetNamedAttribute(GeometryAttribute::POSITION)->size();
// Add per-vertex Float32 3D vector property attribute named _DIRECTION.
{
// Create property attribute.
constexpr DataType kType = DataType::DT_FLOAT32;
std::unique_ptr<PointAttribute> pa(new PointAttribute());
pa->Init(GeometryAttribute::GENERIC, 3, kType, false, num_vertices);
for (AttributeValueIndex avi(0); avi < num_vertices; ++avi) {
const std::array<float, 3> val = {
avi.value() + 0.10f, avi.value() + 0.20f, avi.value() + 0.30f};
pa->SetAttributeValue(avi, &val);
}
const int att_id = mesh.AddPerVertexAttribute(std::move(pa));
mesh.attribute(att_id)->set_name("_DIRECTION");
}
// Add per-vertex Float32 scalar property attribute named _MAGNITUDE.
{
// Create property attribute.
constexpr DataType kType = DataType::DT_FLOAT32;
std::unique_ptr<PointAttribute> pa(new PointAttribute());
pa->Init(GeometryAttribute::GENERIC, 1, kType, false, num_vertices);
for (AttributeValueIndex avi(0); avi < num_vertices; ++avi) {
const float val = avi.value();
pa->SetAttributeValue(avi, &val);
}
const int att_id = mesh.AddPerVertexAttribute(std::move(pa));
mesh.attribute(att_id)->set_name("_MAGNITUDE");
}
// Add property attribute to the mesh.
mesh.AddPropertyAttributesIndex(0);
}
template <>
void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &geometry,
bool has_draco_compression) {
const UseCase &use_case) {
CheckBoxMetaMeshFeatures(geometry, geometry.GetNonMaterialTextureLibrary(),
has_draco_compression);
use_case);
}
template <>
void GltfTestHelper::CheckBoxMetaMeshFeatures(const Scene &geometry,
bool has_draco_compression) {
const UseCase &use_case) {
ASSERT_EQ(geometry.NumMeshes(), 1);
CheckBoxMetaMeshFeatures(geometry.GetMesh(MeshIndex(0)),
geometry.GetNonMaterialTextureLibrary(),
has_draco_compression);
geometry.GetNonMaterialTextureLibrary(), use_case);
}
void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh,
const TextureLibrary &texture_lib,
bool has_draco_compression) {
const UseCase &use_case) {
// Check texture library.
ASSERT_EQ(texture_lib.NumTextures(), 2);
// Check basic mesh properties.
ASSERT_EQ(mesh.NumMeshFeatures(), 5);
ASSERT_EQ(mesh.num_faces(), 12);
ASSERT_EQ(mesh.num_attributes(), 7);
ASSERT_EQ(mesh.num_attributes(), use_case.has_structural_metadata ? 9 : 7);
ASSERT_EQ(mesh.num_points(), 36);
ASSERT_EQ(mesh.NumNamedAttributes(GeometryAttribute::GENERIC), 3);
ASSERT_EQ(mesh.NumNamedAttributes(GeometryAttribute::GENERIC),
use_case.has_structural_metadata ? 5 : 3);
ASSERT_EQ(mesh.NumNamedAttributes(GeometryAttribute::TEX_COORD), 2);
// Get mesh element counts.
@ -437,15 +549,15 @@ void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh,
ASSERT_EQ(features.GetFeatureCount(), num_faces);
ASSERT_EQ(features.GetNullFeatureId(), 100);
ASSERT_EQ(features.GetPropertyTableIndex(), 0);
ASSERT_EQ(features.GetAttributeIndex(), 0);
ASSERT_EQ(features.GetAttributeIndex(),
use_case.has_structural_metadata ? 5 : 4);
ASSERT_TRUE(features.GetTextureChannels().empty());
ASSERT_EQ(features.GetTextureMap().texture(), nullptr);
ASSERT_EQ(features.GetTextureMap().tex_coord_index(), -1);
// Check per-face Uint8 attribute named _FEATURE_ID_0.
const int att_id =
mesh.GetAttributeIdByMetadataEntry("attribute_name", "_FEATURE_ID_0");
auto att = mesh.GetAttributeByUniqueId(att_id);
const int att_id = features.GetAttributeIndex();
const auto att = mesh.attribute(att_id);
ASSERT_NE(att, nullptr);
ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC);
ASSERT_EQ(att->data_type(), DataType::DT_UINT8);
@ -455,7 +567,7 @@ void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh,
// Check that the values are all the numbers from 0 to 12.
const std::vector<uint8_t> expected_values =
has_draco_compression
use_case.has_draco_compression
? std::vector<uint8_t>{7, 11, 10, 3, 2, 5, 4, 1, 6, 9, 8, 0}
: std::vector<uint8_t>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
for (int i = 0; i < num_faces; i++) {
@ -482,15 +594,15 @@ void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh,
ASSERT_EQ(features.GetFeatureCount(), num_vertices);
ASSERT_EQ(features.GetNullFeatureId(), 101);
ASSERT_EQ(features.GetPropertyTableIndex(), 1);
ASSERT_EQ(features.GetAttributeIndex(), 1);
ASSERT_EQ(features.GetAttributeIndex(),
use_case.has_structural_metadata ? 6 : 5);
ASSERT_TRUE(features.GetTextureChannels().empty());
ASSERT_EQ(features.GetTextureMap().texture(), nullptr);
ASSERT_EQ(features.GetTextureMap().tex_coord_index(), -1);
// Check per-vertex Uint16 attribute named _FEATURE_ID_1.
const int att_id =
mesh.GetAttributeIdByMetadataEntry("attribute_name", "_FEATURE_ID_1");
auto att = mesh.GetAttributeByUniqueId(att_id);
const int att_id = features.GetAttributeIndex();
const auto att = mesh.attribute(att_id);
ASSERT_NE(att, nullptr);
ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC);
ASSERT_EQ(att->data_type(), DataType::DT_UINT16);
@ -500,7 +612,8 @@ void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh,
// Check that the values are all the numbers from 0 to 7.
const std::vector<uint16_t> expected_values =
has_draco_compression ? std::vector<uint16_t>{3, 6, 7, 4, 5, 0, 1, 2}
use_case.has_draco_compression
? std::vector<uint16_t>{3, 6, 7, 4, 5, 0, 1, 2}
: std::vector<uint16_t>{0, 1, 2, 3, 4, 5, 6, 7};
for (int i = 0; i < num_vertices; i++) {
uint16_t val;
@ -528,15 +641,15 @@ void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh,
ASSERT_EQ(features.GetFeatureCount(), num_corners);
ASSERT_EQ(features.GetNullFeatureId(), -1);
ASSERT_EQ(features.GetPropertyTableIndex(), -1);
ASSERT_EQ(features.GetAttributeIndex(), 2);
ASSERT_EQ(features.GetAttributeIndex(),
use_case.has_structural_metadata ? 7 : 6);
ASSERT_TRUE(features.GetTextureChannels().empty());
ASSERT_EQ(features.GetTextureMap().texture(), nullptr);
ASSERT_EQ(features.GetTextureMap().tex_coord_index(), -1);
// Check per-corner Float attribute named _FEATURE_ID_2.
const int att_id =
mesh.GetAttributeIdByMetadataEntry("attribute_name", "_FEATURE_ID_2");
auto att = mesh.GetAttributeByUniqueId(att_id);
const int att_id = features.GetAttributeIndex();
const auto att = mesh.attribute(att_id);
ASSERT_NE(att, nullptr);
ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC);
ASSERT_EQ(att->data_type(), DataType::DT_FLOAT32);
@ -547,7 +660,7 @@ void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh,
// Check that the values are from 0 to 35.
const std::vector<float> expected_values =
has_draco_compression
use_case.has_draco_compression
? std::vector<float>{23, 21, 22, 33, 34, 35, 31, 32, 30, 9, 10, 11,
7, 8, 6, 15, 16, 17, 14, 12, 13, 5, 3, 4,
19, 20, 18, 27, 28, 29, 26, 24, 25, 1, 2, 0}
@ -598,21 +711,67 @@ void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh,
}
void GltfTestHelper::CheckBoxMetaStructuralMetadata(
const StructuralMetadata &structural_metadata) {
// Check property table schema.
const Mesh &mesh, const StructuralMetadata &structural_metadata,
const UseCase &use_case) {
// Check structural metadata schema.
{
const PropertyTable::Schema &schema =
structural_metadata.GetPropertyTableSchema();
const StructuralMetadataSchema &schema = structural_metadata.GetSchema();
ASSERT_FALSE(schema.Empty());
const PropertyTable::Schema::Object &json = schema.json;
const StructuralMetadataSchema::Object &json = schema.json;
ASSERT_EQ(json.GetObjects().size(), 3);
ASSERT_EQ(json.GetObjects()[0].GetName(), "classes");
ASSERT_EQ(json.GetObjects()[0].GetObjects().size(), 1);
ASSERT_EQ(json.GetObjects()[0].GetObjects()[0].GetName(), "planet");
ASSERT_EQ(json.GetObjects()[0].GetObjects()[0].GetObjects().size(), 1);
ASSERT_EQ(json.GetObjects()[0].GetObjects().size(), 2);
const auto &properties =
json.GetObjects()[0].GetObjects()[0].GetObjects()[0];
// Check class "movement".
{
const auto item = json.GetObjects()[0].GetObjects()[0];
ASSERT_EQ(item.GetName(), "movement");
ASSERT_EQ(item.GetObjects().size(), 3);
const auto &description = item.GetObjects()[0];
ASSERT_EQ(description.GetName(), "description");
ASSERT_EQ(description.GetString(), "Vertex movement.");
const auto &name = item.GetObjects()[1];
ASSERT_EQ(name.GetName(), "name");
ASSERT_EQ(name.GetString(), "The movement.");
const auto &properties = item.GetObjects()[2];
ASSERT_EQ(properties.GetName(), "properties");
ASSERT_EQ(properties.GetObjects().size(), 2);
const auto &direction = properties.GetObjects()[0];
ASSERT_EQ(direction.GetName(), "direction");
ASSERT_EQ(direction.GetObjects().size(), 4);
ASSERT_EQ(direction.GetObjects()[0].GetName(), "componentType");
ASSERT_EQ(direction.GetObjects()[1].GetName(), "description");
ASSERT_EQ(direction.GetObjects()[2].GetName(), "required");
ASSERT_EQ(direction.GetObjects()[3].GetName(), "type");
ASSERT_EQ(direction.GetObjects()[0].GetString(), "FLOAT32");
ASSERT_EQ(direction.GetObjects()[1].GetString(), "Movement direction.");
ASSERT_EQ(direction.GetObjects()[2].GetBoolean(), true);
ASSERT_EQ(direction.GetObjects()[3].GetString(), "VEC3");
const auto &mag = properties.GetObjects()[1];
ASSERT_EQ(mag.GetName(), "magnitude");
ASSERT_EQ(mag.GetObjects().size(), 4);
ASSERT_EQ(mag.GetObjects()[0].GetName(), "componentType");
ASSERT_EQ(mag.GetObjects()[1].GetName(), "description");
ASSERT_EQ(mag.GetObjects()[2].GetName(), "required");
ASSERT_EQ(mag.GetObjects()[3].GetName(), "type");
ASSERT_EQ(mag.GetObjects()[0].GetString(), "FLOAT32");
ASSERT_EQ(mag.GetObjects()[1].GetString(), "Movement magnitude.");
ASSERT_EQ(mag.GetObjects()[2].GetBoolean(), true);
ASSERT_EQ(mag.GetObjects()[3].GetString(), "SCALAR");
}
// Check class "planet".
{
const auto item = json.GetObjects()[0].GetObjects()[1];
ASSERT_EQ(item.GetName(), "planet");
ASSERT_EQ(item.GetObjects().size(), 1);
const auto &properties = item.GetObjects()[0];
ASSERT_EQ(properties.GetName(), "properties");
ASSERT_EQ(properties.GetObjects().size(), 3);
@ -640,13 +799,16 @@ void GltfTestHelper::CheckBoxMetaStructuralMetadata(
const auto &sequence = properties.GetObjects()[2];
ASSERT_EQ(sequence.GetName(), "sequence");
ASSERT_EQ(sequence.GetObjects().size(), 3);
ASSERT_EQ(sequence.GetObjects()[0].GetName(), "description");
ASSERT_EQ(sequence.GetObjects()[1].GetName(), "required");
ASSERT_EQ(sequence.GetObjects()[2].GetName(), "type");
ASSERT_EQ(sequence.GetObjects()[0].GetString(), "The number sequence.");
ASSERT_FALSE(sequence.GetObjects()[1].GetBoolean());
ASSERT_EQ(sequence.GetObjects()[2].GetString(), "SCALAR");
ASSERT_EQ(sequence.GetObjects().size(), 4);
ASSERT_EQ(sequence.GetObjects()[0].GetName(), "componentType");
ASSERT_EQ(sequence.GetObjects()[1].GetName(), "description");
ASSERT_EQ(sequence.GetObjects()[2].GetName(), "required");
ASSERT_EQ(sequence.GetObjects()[3].GetName(), "type");
ASSERT_EQ(sequence.GetObjects()[0].GetString(), "FLOAT32");
ASSERT_EQ(sequence.GetObjects()[1].GetString(), "The number sequence.");
ASSERT_FALSE(sequence.GetObjects()[2].GetBoolean());
ASSERT_EQ(sequence.GetObjects()[3].GetString(), "SCALAR");
}
ASSERT_EQ(json.GetObjects()[1].GetName(), "enums");
const auto &classifications = json.GetObjects()[1].GetObjects()[0];
@ -736,11 +898,11 @@ void GltfTestHelper::CheckBoxMetaStructuralMetadata(
ASSERT_EQ(offsets[1], 0);
ASSERT_EQ(offsets[2], 0);
ASSERT_EQ(offsets[3], 0);
ASSERT_EQ(offsets[60], 32); // UNLABELED 287.
ASSERT_EQ(offsets[60], 31); // UNLABELED 287.
ASSERT_EQ(offsets[61], 1);
ASSERT_EQ(offsets[62], 0);
ASSERT_EQ(offsets[63], 0);
ASSERT_EQ(offsets[64], 41); // Beyond UNLABELED 296.
ASSERT_EQ(offsets[64], 40); // Beyond UNLABELED 296.
ASSERT_EQ(offsets[65], 1);
ASSERT_EQ(offsets[66], 0);
ASSERT_EQ(offsets[67], 0);
@ -748,8 +910,8 @@ void GltfTestHelper::CheckBoxMetaStructuralMetadata(
struct Name {
static std::string Extract(const std::vector<uint8_t> &data,
const std::vector<uint8_t> &offsets, int row) {
const int b = offsets[4 * (row + 0)] + 255 * offsets[4 * (row + 0) + 1];
const int e = offsets[4 * (row + 1)] + 255 * offsets[4 * (row + 1) + 1];
const int b = offsets[4 * (row + 0)] + 256 * offsets[4 * (row + 0) + 1];
const int e = offsets[4 * (row + 1)] + 256 * offsets[4 * (row + 1) + 1];
return std::string(data.begin() + b, data.begin() + e);
}
};
@ -757,6 +919,9 @@ void GltfTestHelper::CheckBoxMetaStructuralMetadata(
// Check that the names can be extracted from the data.
ASSERT_EQ(Name::Extract(data, offsets, 0), "named_class:Tatooine");
ASSERT_EQ(Name::Extract(data, offsets, 6), "named_class:Corellia");
ASSERT_EQ(Name::Extract(data, offsets, 12), "named_class:Bespin");
ASSERT_EQ(Name::Extract(data, offsets, 13), "named_class:Yavin");
ASSERT_EQ(Name::Extract(data, offsets, 14), "named_class:Geonosis");
ASSERT_EQ(Name::Extract(data, offsets, 15), "UNLABELED");
ASSERT_TRUE(property.GetArrayOffsets().type.empty());
@ -816,6 +981,101 @@ void GltfTestHelper::CheckBoxMetaStructuralMetadata(
ASSERT_TRUE(property.GetStringOffsets().data.data.empty());
ASSERT_EQ(property.GetStringOffsets().data.target, 0);
}
// Check property attributes in structural metadata.
ASSERT_EQ(structural_metadata.NumPropertyAttributes(), 1);
{
const PropertyAttribute &attribute =
structural_metadata.GetPropertyAttribute(0);
ASSERT_EQ(attribute.GetName(), "The movement.");
ASSERT_EQ(attribute.GetClass(), "movement");
ASSERT_EQ(attribute.NumProperties(), 2);
const PropertyAttribute::Property &direction = attribute.GetProperty(0);
ASSERT_EQ(direction.GetName(), "direction");
ASSERT_EQ(direction.GetAttributeName(), "_DIRECTION");
const PropertyAttribute::Property &magnitude = attribute.GetProperty(1);
ASSERT_EQ(magnitude.GetName(), "magnitude");
ASSERT_EQ(magnitude.GetAttributeName(), "_MAGNITUDE");
}
// Check property attributes in the |mesh|.
ASSERT_EQ(mesh.NumPropertyAttributesIndices(), 1);
ASSERT_EQ(mesh.GetPropertyAttributesIndex(0), 0);
ASSERT_EQ(mesh.num_faces(), 12);
ASSERT_EQ(mesh.num_attributes(), 9);
ASSERT_EQ(mesh.num_points(), 36);
ASSERT_EQ(mesh.NumNamedAttributes(GeometryAttribute::GENERIC), 5);
// Get mesh element counts.
const int num_corners = 3 * mesh.num_faces();
const int num_vertices =
mesh.GetNamedAttribute(GeometryAttribute::POSITION)->size();
// Check property attribute named _DIRECTION.
{
const auto att =
mesh.GetNamedAttributeByName(GeometryAttribute::GENERIC, "_DIRECTION");
ASSERT_NE(att, nullptr);
ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC);
ASSERT_EQ(att->data_type(), DataType::DT_FLOAT32);
ASSERT_EQ(att->num_components(), 3);
ASSERT_EQ(att->size(), num_vertices);
ASSERT_EQ(att->indices_map_size(), num_corners);
// Check attribute values.
// clang-format off
const std::vector<float> expected_values =
use_case.has_draco_compression
? std::vector<float>{3.1f, 3.2f, 3.3f,
6.1f, 6.2f, 6.3f,
7.1f, 7.2f, 7.3f,
4.1f, 4.2f, 4.3f,
5.1f, 5.2f, 5.3f,
0.1f, 0.2f, 0.3f,
1.1f, 1.2f, 1.3f,
2.1f, 2.2f, 2.3f}
: std::vector<float>{0.1f, 0.2f, 0.3f,
1.1f, 1.2f, 1.3f,
2.1f, 2.2f, 2.3f,
3.1f, 3.2f, 3.3f,
4.1f, 4.2f, 4.3f,
5.1f, 5.2f, 5.3f,
6.1f, 6.2f, 6.3f,
7.1f, 7.2f, 7.3f};
// clang-format on
for (int i = 0; i < num_vertices; i++) {
std::array<float, 3> val;
att->GetValue(AttributeValueIndex(i), &val);
ASSERT_EQ(val[0], expected_values[3 * i + 0]);
ASSERT_EQ(val[1], expected_values[3 * i + 1]);
ASSERT_EQ(val[2], expected_values[3 * i + 2]);
}
}
// Check property attribute named _MAGNITUDE.
{
const auto att =
mesh.GetNamedAttributeByName(GeometryAttribute::GENERIC, "_MAGNITUDE");
ASSERT_NE(att, nullptr);
ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC);
ASSERT_EQ(att->data_type(), DataType::DT_FLOAT32);
ASSERT_EQ(att->num_components(), 1);
ASSERT_EQ(att->size(), num_vertices);
ASSERT_EQ(att->indices_map_size(), num_corners);
// Check attribute values.
const std::vector<float> expected_values =
use_case.has_draco_compression
? std::vector<float>{3.f, 6.f, 7.f, 4.f, 5.f, 0.f, 1.f, 2.f}
: std::vector<float>{0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f};
for (int i = 0; i < num_vertices; i++) {
float val;
att->GetValue(AttributeValueIndex(i), &val);
ASSERT_EQ(val, expected_values[i]);
}
}
}
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -25,9 +25,15 @@ namespace draco {
// Helper class for testing Draco glTF encoder and decoder.
class GltfTestHelper {
public:
// Adds various mesh feature ID sets (via attributes and via textures) and
// structural metadata property table and property table schema to the box
// |scene| loaded from the test file testdata/Box/glTF/Box.gltf.
struct UseCase {
bool has_draco_compression = false;
bool has_mesh_features = false;
bool has_structural_metadata = false;
};
// Adds various mesh feature ID sets (via attributes and via textures),
// structural metadata schema, property table, and property attributes to the
// box |scene| loaded from the test file testdata/Box/glTF/Box.gltf.
static void AddBoxMetaMeshFeatures(Scene *scene);
static void AddBoxMetaStructuralMetadata(Scene *scene);
@ -37,22 +43,31 @@ class GltfTestHelper {
// 2. testdata/BoxMetaDraco/glTF/BoxMetaDraco.gltf
template <typename GeometryT>
static void CheckBoxMetaMeshFeatures(const GeometryT &geometry,
bool has_draco_compression);
const UseCase &use_case);
// Checks the box |geometry| (draco::Mesh or draco::Scene) with structural
// metadata that includes property table and property table schema loaded from
// test file testdata/BoxMeta/glTF/BoxMeta.gltf.
// metadata that includes schema, property table, and property attributes
// loaded from test file testdata/BoxMeta/glTF/BoxMeta.gltf.
template <typename GeometryT>
static void CheckBoxMetaStructuralMetadata(const GeometryT &geometry) {
CheckBoxMetaStructuralMetadata(geometry.GetStructuralMetadata());
static void CheckBoxMetaStructuralMetadata(const GeometryT &geometry,
const UseCase &use_case) {
if constexpr (std::is_same_v<GeometryT, Mesh>) {
CheckBoxMetaStructuralMetadata(geometry, geometry.GetStructuralMetadata(),
use_case);
} else {
CheckBoxMetaStructuralMetadata(geometry.GetMesh(MeshIndex(0)),
geometry.GetStructuralMetadata(),
use_case);
}
}
private:
static void CheckBoxMetaMeshFeatures(const Mesh &mesh,
const TextureLibrary &texture_lib,
bool has_draco_compression);
const UseCase &use_case);
static void CheckBoxMetaStructuralMetadata(
const StructuralMetadata &structural_metadata);
const Mesh &mesh, const StructuralMetadata &structural_metadata,
const UseCase &use_case);
};
} // namespace draco

View File

@ -79,6 +79,12 @@ void JsonWriter::EndObject() {
o_ << indent_ << "}";
}
void JsonWriter::BeginArray() {
FinishPreviousLine(BEGIN);
o_ << indent_ << "[";
indent_writer_.Increase();
}
void JsonWriter::BeginArray(const std::string &name) {
FinishPreviousLine(BEGIN);
o_ << indent_ << "\"" << name << "\":" << separator_ << "[";

View File

@ -90,6 +90,7 @@ class JsonWriter {
void EndObject();
// Every call to BeginArray should have a matching call to EndArray.
void BeginArray();
void BeginArray(const std::string &name);
void EndArray();

View File

@ -216,6 +216,13 @@ TEST_F(GltfUtilsTest, TestArrays) {
json_writer.EndArray();
json_writer.EndArray();
CompareGolden(&json_writer, "\"array1\": [\n \"array2\": [\n ]\n]");
json_writer.Reset();
json_writer.BeginArray("array1");
json_writer.BeginArray();
json_writer.EndArray();
json_writer.EndArray();
CompareGolden(&json_writer, "\"array1\": [\n [\n ]\n]");
}
TEST_F(GltfUtilsTest, TestGltfValues) {
@ -359,6 +366,13 @@ TEST_F(GltfUtilsTest, TestArraysCompact) {
json_writer.EndArray();
json_writer.EndArray();
CompareGolden(&json_writer, "\"array1\":[\"array2\":[]]");
json_writer.Reset();
json_writer.BeginArray("array1");
json_writer.BeginArray();
json_writer.EndArray();
json_writer.EndArray();
CompareGolden(&json_writer, "\"array1\":[[]]");
}
} // namespace draco

View File

@ -23,7 +23,13 @@
namespace draco {
// Enum defining image compression formats.
enum class ImageFormat { NONE, PNG, JPEG, BASIS, WEBP };
enum class ImageFormat {
NONE,
PNG,
JPEG,
BASIS,
WEBP,
};
} // namespace draco

View File

@ -541,7 +541,7 @@ bool ObjDecoder::ParseMaterial(Status * /* status */) {
parser::SkipWhitespace(&line_buffer);
std::string mat_name;
parser::ParseLine(&line_buffer, &mat_name);
if (mat_name.length() == 0) {
if (mat_name.empty()) {
return false;
}
auto it = material_name_to_id_.find(mat_name);
@ -572,7 +572,7 @@ bool ObjDecoder::ParseObject(Status *status) {
if (!parser::ParseString(&line_buffer, &obj_name)) {
return false;
}
if (obj_name.length() == 0) {
if (obj_name.empty()) {
return true; // Ignore empty name entries.
}
auto it = obj_name_to_id_.find(obj_name);

View File

@ -94,7 +94,7 @@ Status PlyDecoder::DecodeInternal() {
Status PlyDecoder::DecodeFaceData(const PlyElement *face_element) {
// We accept point clouds now.
if (face_element == nullptr) {
return Status(Status::INVALID_PARAMETER, "face_element is null");
return OkStatus();
}
const PlyProperty *vertex_indices =
face_element->GetPropertyByName("vertex_indices");

View File

@ -36,15 +36,12 @@ class PlyDecoderTest : public ::testing::Test {
void test_decoding(const std::string &file_name, int num_faces,
uint32_t num_points, std::unique_ptr<Mesh> *out_mesh) {
// Don't test mesh decoding when the input is point cloud.
if (num_faces > 0) {
std::unique_ptr<Mesh> mesh(DecodePly<Mesh>(file_name));
ASSERT_NE(mesh, nullptr) << "Failed to load test model " << file_name;
ASSERT_EQ(mesh->num_faces(), num_faces);
if (out_mesh) {
*out_mesh = std::move(mesh);
}
}
const std::unique_ptr<PointCloud> pc(DecodePly<PointCloud>(file_name));
ASSERT_NE(pc, nullptr) << "Failed to load test model " << file_name;

View File

@ -16,7 +16,11 @@
#ifdef DRACO_TRANSCODER_SUPPORTED
#include <algorithm>
#include <cstring>
#include "draco/io/file_utils.h"
#include "draco/texture/texture_utils.h"
namespace draco {
@ -25,12 +29,74 @@ namespace {
StatusOr<std::unique_ptr<Texture>> CreateDracoTextureInternal(
const std::vector<uint8_t> &image_data, SourceImage *out_source_image) {
std::unique_ptr<Texture> draco_texture(new Texture());
const auto format =
ImageFormatFromBuffer(image_data.data(), image_data.size());
out_source_image->MutableEncodedData() = image_data;
out_source_image->set_mime_type(TextureUtils::GetMimeType(format));
return std::move(draco_texture);
}
} // namespace
ImageFormat ImageFormatFromBuffer(const uint8_t *buffer, size_t buffer_size) {
if (buffer_size > 4) {
// These bytes are the Start of Image (SOI) and End of Image (EOI) markers
// in a JPEG data stream.
const std::array<uint8_t, 2> kJpegSOIMarker = {0xFF, 0xD8};
const std::array<uint8_t, 2> kJpegEOIMarker = {0xFF, 0xD9};
if (!memcmp(buffer, kJpegSOIMarker.data(), kJpegSOIMarker.size())) {
// Look for the last occurence of the end marker (allow trailing bytes).
if (std::find_end(buffer, buffer + buffer_size, kJpegEOIMarker.begin(),
kJpegEOIMarker.end()) != buffer + buffer_size) {
return ImageFormat::JPEG;
}
}
}
if (buffer_size > 2) {
// For Binomial Basis format input the stream always begins with
// the signature 'B' * 256 + 's', or 0x4273.
const std::array<uint8_t, 2> kBasisSignature = {0x42, 0x73};
if (!memcmp(buffer, kBasisSignature.data(), kBasisSignature.size())) {
return ImageFormat::BASIS;
}
}
if (buffer_size > 4) {
// For Binomial Basis/KTX2 format input the stream begins with 0xab 0x4b
// 0x54 0x58.
const std::array<uint8_t, 4> kKtx2Signature = {0xab, 0x4b, 0x54, 0x58};
if (!memcmp(buffer, kKtx2Signature.data(), kKtx2Signature.size())) {
return ImageFormat::BASIS;
}
}
if (buffer_size > 8) {
// The first eight bytes of a PNG stream always contain these values:
const std::array<uint8_t, 8> kPngSignature = {0x89, 0x50, 0x4e, 0x47,
0x0d, 0x0a, 0x1a, 0x0a};
if (!memcmp(buffer, kPngSignature.data(), kPngSignature.size())) {
return ImageFormat::PNG;
}
}
if (buffer_size > 12) {
// The WebP signature bytes are: RIFF 0 0 0 0 WEBP. The 0's are where WebP
// size information is encoded in the stream, but the check here just looks
// for RIFF and WEBP.
const std::array<uint8_t, 4> kRIFF = {0x52, 0x49, 0x46, 0x46};
const std::array<uint8_t, 4> kWEBP = {0x57, 0x45, 0x42, 0x50};
if (!memcmp(buffer, kRIFF.data(), kRIFF.size()) &&
!memcmp(buffer + 8, kWEBP.data(), kWEBP.size())) {
return ImageFormat::WEBP;
}
}
return ImageFormat::NONE;
}
StatusOr<std::unique_ptr<Texture>> ReadTextureFromFile(
const std::string &file_name) {
std::vector<uint8_t> image_data;
@ -42,23 +108,31 @@ StatusOr<std::unique_ptr<Texture>> ReadTextureFromFile(
DRACO_ASSIGN_OR_RETURN(auto texture,
CreateDracoTextureInternal(image_data, &source_image));
source_image.set_filename(file_name);
if (source_image.mime_type().empty()) {
// Try to set mime type from extension if we were not able to detect it
// automatically.
const std::string extension = LowercaseFileExtension(file_name);
const std::string mime_type =
"image/" + (extension == "jpg" ? "jpeg" : extension);
source_image.set_mime_type(mime_type);
}
texture->set_source_image(source_image);
return texture;
}
StatusOr<std::unique_ptr<Texture>> ReadTextureFromBuffer(const uint8_t *buffer,
size_t buffer_size) {
SourceImage source_image;
std::vector<uint8_t> image_data(buffer, buffer + buffer_size);
DRACO_ASSIGN_OR_RETURN(auto texture,
CreateDracoTextureInternal(image_data, &source_image));
texture->set_source_image(source_image);
return texture;
}
StatusOr<std::unique_ptr<Texture>> ReadTextureFromBuffer(
const uint8_t *buffer, size_t buffer_size, const std::string &mime_type) {
SourceImage source_image;
std::vector<uint8_t> image_data(buffer, buffer + buffer_size);
DRACO_ASSIGN_OR_RETURN(auto texture,
CreateDracoTextureInternal(image_data, &source_image));
source_image.set_mime_type(mime_type);
texture->set_source_image(source_image);
return texture;
return ReadTextureFromBuffer(buffer, buffer_size);
}
Status WriteTextureToFile(const std::string &file_name,

View File

@ -19,6 +19,8 @@
#ifdef DRACO_TRANSCODER_SUPPORTED
#include <memory>
#include <string>
#include <vector>
#include "draco/core/draco_types.h"
#include "draco/core/status_or.h"
@ -32,10 +34,10 @@ StatusOr<std::unique_ptr<Texture>> ReadTextureFromFile(
const std::string &file_name);
// Same as ReadTextureFromFile() but the texture data is parsed from a |buffer|.
// |mime_type| should be set to a type of the texture encoded in |buffer|.
// Supported mime types are "image/jpeg", "image/png" and "image/webp".
// TODO(ostava): We should be able to get the mime type directly from the
// |buffer| but our image decoding library doesn't support this at this time.
StatusOr<std::unique_ptr<Texture>> ReadTextureFromBuffer(const uint8_t *buffer,
size_t buffer_size);
// Deprecated: |mime_type| is currently ignored and it is deducted automatically
// from the content of the |buffer|.
StatusOr<std::unique_ptr<Texture>> ReadTextureFromBuffer(
const uint8_t *buffer, size_t buffer_size, const std::string &mime_type);
@ -50,6 +52,10 @@ Status WriteTextureToFile(const std::string &file_name, const Texture &texture);
Status WriteTextureToBuffer(const Texture &texture,
std::vector<uint8_t> *buffer);
// Returns the image format of an encoded texture stored in |buffer|.
// ImageFormat::NONE is returned for unknown image formats.
ImageFormat ImageFormatFromBuffer(const uint8_t *buffer, size_t buffer_size);
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -15,6 +15,7 @@
#include "draco/io/texture_io.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include <cstdint>
#include <memory>
#include <string>
#include <unordered_map>
@ -50,6 +51,29 @@ TEST(TextureIoTest, TestLoadFromBuffer) {
}
}
// Tests that we can set mime type correctly even when the source file had
// an incorrect extension.
TEST(TextureIoTest, TestWrongExtension) {
const std::string file_name = draco::GetTestFileFullPath("this_is_png.jpg");
DRACO_ASSIGN_OR_ASSERT(std::unique_ptr<draco::Texture> texture,
draco::ReadTextureFromFile(file_name));
ASSERT_NE(texture, nullptr);
// Ensure the mime type was set to png even though the file extension was jpg.
ASSERT_EQ(texture->source_image().mime_type(), "image/png");
}
// Tests that we can load jpeg files that have some trailing bytes after the
// jpeg end marker.
TEST(TextureIoTest, TestTrailingJpegBytes) {
const std::string file_name = draco::GetTestFileFullPath("trailing_zero.jpg");
DRACO_ASSIGN_OR_ASSERT(std::unique_ptr<draco::Texture> texture,
draco::ReadTextureFromFile(file_name));
ASSERT_NE(texture, nullptr);
}
} // namespace
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -115,6 +115,8 @@ interface PointCloudBuilder {
boolean AddMetadata(PointCloud pc, [Const] Metadata metadata);
boolean SetMetadataForAttribute(PointCloud pc, long attribute_id,
[Const] Metadata metadata);
boolean SetNormalizedFlagForAttribute(PointCloud pc, long attribute_id,
boolean normalized);
};
interface MeshBuilder : PointCloudBuilder {
@ -159,6 +161,8 @@ interface MeshBuilder : PointCloudBuilder {
boolean AddMetadata(PointCloud pc, [Const] Metadata metadata);
boolean SetMetadataForAttribute(PointCloud pc, long attribute_id,
[Const] Metadata metadata);
boolean SetNormalizedFlagForAttribute(PointCloud pc, long attribute_id,
boolean normalized);
};
interface Encoder {

View File

@ -179,6 +179,19 @@ bool PointCloudBuilder::SetMetadataForAttribute(PointCloud *pc,
return true;
}
bool PointCloudBuilder::SetNormalizedFlagForAttribute(draco::PointCloud *pc,
long attribute_id,
bool normalized) {
if (!pc) {
return false;
}
if (attribute_id < 0 || attribute_id >= pc->num_attributes()) {
return false;
}
pc->attribute(attribute_id)->set_normalized(normalized);
return true;
}
MeshBuilder::MeshBuilder() {}
bool MeshBuilder::AddFacesToMesh(Mesh *mesh, long num_faces, const int *faces) {

View File

@ -81,6 +81,8 @@ class PointCloudBuilder {
bool SetMetadataForAttribute(draco::PointCloud *pc, long attribute_id,
const draco::Metadata *metadata);
bool AddMetadata(draco::PointCloud *pc, const draco::Metadata *metadata);
bool SetNormalizedFlagForAttribute(draco::PointCloud *pc, long attribute_id,
bool normalized);
private:
template <typename DataTypeT>

View File

@ -26,11 +26,7 @@ namespace draco {
template <bool B, class T, class F>
using conditional_t = typename std::conditional<B, T, F>::type;
#ifdef DRACO_TRANSCODER_SUPPORTED
Mesh::Mesh() : compression_enabled_(false) {}
#else
Mesh::Mesh() {}
#endif
#ifdef DRACO_TRANSCODER_SUPPORTED
void Mesh::Copy(const Mesh &src) {
@ -39,8 +35,6 @@ void Mesh::Copy(const Mesh &src) {
faces_ = src.faces_;
attribute_data_ = src.attribute_data_;
material_library_.Copy(src.material_library_);
compression_enabled_ = src.compression_enabled_;
compression_options_ = src.compression_options_;
// Copy mesh feature ID sets.
mesh_features_.clear();
@ -67,6 +61,8 @@ void Mesh::Copy(const Mesh &src) {
// Copy structural metadata.
structural_metadata_.Copy(src.structural_metadata_);
property_attributes_ = src.property_attributes_;
property_attributes_material_mask_ = src.property_attributes_material_mask_;
}
namespace {
@ -338,6 +334,22 @@ void Mesh::RemoveUnusedMaterials(bool remove_unused_material_indices) {
}
}
// Check if any of the (unused) materials is used by property attributes
// indices. If so, user should remove unused property attributes indices
// first.
for (int i = 0; i < NumPropertyAttributesIndices(); ++i) {
for (int mask_index = 0;
mask_index < NumPropertyAttributesIndexMaterialMasks(i);
++mask_index) {
const int mat_index =
GetPropertyAttributesIndexMaterialMask(i, mask_index);
if (mat_index < num_materials && !is_material_used[mat_index]) {
is_material_used[mat_index] = true;
num_used_materials++;
}
}
}
if (num_used_materials == num_materials) {
return; // All materials are used, don't do anything.
}
@ -408,6 +420,30 @@ void Mesh::RemoveUnusedMaterials(bool remove_unused_material_indices) {
}
}
}
// Update material indices on property attributes incices.
for (int i = 0; i < NumPropertyAttributesIndices(); ++i) {
for (int mask_index = 0;
mask_index < NumPropertyAttributesIndexMaterialMasks(i);
++mask_index) {
const int old_mat_index =
GetPropertyAttributesIndexMaterialMask(i, mask_index);
if (old_mat_index < num_materials && is_material_used[old_mat_index]) {
property_attributes_material_mask_[i][mask_index] =
old_to_new_material_index_map[old_mat_index];
}
}
}
}
bool Mesh::IsAttributeUsedByMeshFeatures(int att_id) const {
for (MeshFeaturesIndex mfi(0); mfi < NumMeshFeatures(); ++mfi) {
const auto &mf = GetMeshFeatures(mfi);
if (mf.GetAttributeIndex() == att_id) {
return true;
}
}
return false;
}
void Mesh::UpdateMeshFeaturesTexturePointer(
@ -448,6 +484,43 @@ void Mesh::CopyMeshFeaturesForMaterial(const Mesh &source_mesh,
}
}
void Mesh::CopyPropertyAttributesIndicesForMaterial(const Mesh &source_mesh,
Mesh *target_mesh,
int material_index) {
for (int i = 0; i < source_mesh.NumPropertyAttributesIndices(); ++i) {
// Property attributes index is used if it doesn't have any material mask or
// if one of the material masks matches |material_index|.
bool is_used = source_mesh.NumPropertyAttributesIndexMaterialMasks(i) == 0;
for (int mask_index = 0;
!is_used &&
mask_index < source_mesh.NumPropertyAttributesIndexMaterialMasks(i);
++mask_index) {
if (source_mesh.GetPropertyAttributesIndexMaterialMask(i, mask_index) ==
material_index) {
is_used = true;
}
}
if (is_used) {
// Copy over the property attributes index to the target mesh.
target_mesh->AddPropertyAttributesIndex(
source_mesh.GetPropertyAttributesIndex(i));
}
}
}
void Mesh::UpdateMeshFeaturesAfterDeletedAttribute(int att_id) {
for (MeshFeaturesIndex mfi(0); mfi < NumMeshFeatures(); ++mfi) {
auto &mf = GetMeshFeatures(mfi);
if (mf.GetAttributeIndex() == att_id) {
// Mesh features is no longer associated with a vertex attribute.
mf.SetAttributeIndex(-1);
} else if (mf.GetAttributeIndex() > att_id) {
// Attribute index decremented by one.
mf.SetAttributeIndex(mf.GetAttributeIndex() - 1);
}
}
}
int32_t Mesh::AddPerFaceAttribute(std::unique_ptr<PointAttribute> att) {
IndexTypeVector<CornerIndex, AttributeValueIndex> corner_map(num_faces() * 3);
for (CornerIndex ci(0); ci < num_faces() * 3; ++ci) {

View File

@ -24,7 +24,6 @@
#include "draco/core/status.h"
#include "draco/draco_features.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include "draco/compression/draco_compression_options.h"
#include "draco/material/material_library.h"
#include "draco/mesh/mesh_features.h"
#include "draco/mesh/mesh_indices.h"
@ -94,6 +93,9 @@ class Mesh : public PointCloud {
if (att_id >= 0 && att_id < static_cast<int>(attribute_data_.size())) {
attribute_data_.erase(attribute_data_.begin() + att_id);
}
#ifdef DRACO_TRANSCODER_SUPPORTED
UpdateMeshFeaturesAfterDeletedAttribute(att_id);
#endif
}
#ifdef DRACO_TRANSCODER_SUPPORTED
@ -170,22 +172,6 @@ class Mesh : public PointCloud {
void RemoveUnusedMaterials();
void RemoveUnusedMaterials(bool remove_unused_material_indices);
// Enables or disables Draco geometry compression for this mesh.
void SetCompressionEnabled(bool enabled) { compression_enabled_ = enabled; }
bool IsCompressionEnabled() const { return compression_enabled_; }
// Sets |options| that configure Draco geometry compression. This does not
// enable or disable compression.
void SetCompressionOptions(const DracoCompressionOptions &options) {
compression_options_ = options;
}
const DracoCompressionOptions &GetCompressionOptions() const {
return compression_options_;
}
DracoCompressionOptions &GetCompressionOptions() {
return compression_options_;
}
// Library that contains non-material textures.
const TextureLibrary &GetNonMaterialTextureLibrary() const {
return non_material_texture_library_;
@ -208,12 +194,20 @@ class Mesh : public PointCloud {
MeshFeatures &GetMeshFeatures(MeshFeaturesIndex index) {
return *mesh_features_[index];
}
// Removes mesh features from the mesh. Note that removing a mesh feature does
// not delete any associated data such as vertex attributes or feature
// textures.
void RemoveMeshFeatures(MeshFeaturesIndex index) {
mesh_features_.erase(mesh_features_.begin() + index.value());
mesh_features_material_mask_.erase(mesh_features_material_mask_.begin() +
index.value());
}
// Returns true if an attribute with |att_id| is being used by any mesh
// features attached to the mesh.
bool IsAttributeUsedByMeshFeatures(int att_id) const;
// Restricts given mesh features to faces mapped to a material with
// |material_index|. Note that single mesh features can be restricted to
// multiple materials.
@ -249,6 +243,49 @@ class Mesh : public PointCloud {
return structural_metadata_;
}
StructuralMetadata &GetStructuralMetadata() { return structural_metadata_; }
// Property attributes indices as defined by EXT_structural_metadata glTF
// extension.
int AddPropertyAttributesIndex(int property_attribute_index) {
property_attributes_.push_back(property_attribute_index);
property_attributes_material_mask_.push_back({});
return property_attributes_.size() - 1;
}
int NumPropertyAttributesIndices() const {
return property_attributes_.size();
}
const int &GetPropertyAttributesIndex(int index) const {
return property_attributes_[index];
}
int &GetPropertyAttributesIndex(int index) {
return property_attributes_[index];
}
void RemovePropertyAttributesIndex(int index) {
property_attributes_.erase(property_attributes_.begin() + index);
property_attributes_material_mask_.erase(
property_attributes_material_mask_.begin() + index);
}
// Restricts given property attributes indices to faces mapped to a material
// with |material_index|. Note that single property attribute can be
// restricted to multiple materials.
void AddPropertyAttributesIndexMaterialMask(int index, int material_index) {
property_attributes_material_mask_[index].push_back(material_index);
}
size_t NumPropertyAttributesIndexMaterialMasks(int index) const {
return property_attributes_material_mask_[index].size();
}
int GetPropertyAttributesIndexMaterialMask(int index, int mask_index) const {
return property_attributes_material_mask_[index][mask_index];
}
// Copies over property attributes indices from |source_mesh| and stores them
// in |target_mesh| as long as the property attributes indices material mask
// is valid for given |material_index|.
static void CopyPropertyAttributesIndicesForMaterial(const Mesh &source_mesh,
Mesh *target_mesh,
int material_index);
#endif // DRACO_TRANSCODER_SUPPORTED
protected:
@ -266,6 +303,11 @@ class Mesh : public PointCloud {
IndexTypeVector<FaceIndex, Face> &faces() { return faces_; }
private:
#ifdef DRACO_TRANSCODER_SUPPORTED
// Updates attribute indices associated to all mesh features after a mesh
// attribute is deleted.
void UpdateMeshFeaturesAfterDeletedAttribute(int att_id);
#endif
// Mesh specific per-attribute data.
std::vector<AttributeData> attribute_data_;
@ -280,11 +322,6 @@ class Mesh : public PointCloud {
// Materials applied to to this mesh.
MaterialLibrary material_library_;
// Compression options for this mesh.
// TODO(vytyaz): Store encoded bitstream that this mesh compresses into.
bool compression_enabled_;
DracoCompressionOptions compression_options_;
// Sets of feature IDs as defined by EXT_mesh_features glTF extension.
IndexTypeVector<MeshFeaturesIndex, std::unique_ptr<MeshFeatures>>
mesh_features_;
@ -296,6 +333,15 @@ class Mesh : public PointCloud {
IndexTypeVector<MeshFeaturesIndex, std::vector<int>>
mesh_features_material_mask_;
// Indices pointing to property attributes in draco::StructuralMetadata.
std::vector<int> property_attributes_;
// When the Mesh contains multiple materials, this mask can be used to limit
// specific index into |property_attributes_| to a vector of material indices.
// If for a given property attributes index, the material indices are empty,
// the corresponding property attributes are applied to the entire mesh.
std::vector<std::vector<int>> property_attributes_material_mask_;
// Texture library for storing non-material textures used by this mesh, e.g.,
// textures containing mesh feature IDs of EXT_mesh_features glTF extension.
// If the mesh is part of the scene then the textures are stored in the scene.

View File

@ -75,35 +75,28 @@ void MeshCleanup::RemoveDegeneratedFaces(Mesh *mesh) {
}
void MeshCleanup::RemoveDuplicateFaces(Mesh *mesh) {
const PointAttribute *const pos_att =
mesh->GetNamedAttribute(GeometryAttribute::POSITION);
typedef std::array<AttributeValueIndex::ValueType, 3> PosTriplet;
PosTriplet pos_indices;
std::unordered_set<PosTriplet, HashArray<PosTriplet>> is_face_used;
std::unordered_set<Mesh::Face, HashArray<Mesh::Face>> is_face_used;
uint32_t num_duplicate_faces = 0;
for (FaceIndex fi(0); fi < mesh->num_faces(); ++fi) {
const auto f = mesh->face(fi);
for (int c = 0; c < 3; ++c) {
pos_indices[c] = pos_att->mapped_index(f[c]).value();
}
// Shift the position indices until the smallest index is the first one.
while (pos_indices[0] > pos_indices[1] || pos_indices[0] > pos_indices[2]) {
auto face = mesh->face(fi);
// Shift the face indices until the smallest index is the first one.
while (face[0] > face[1] || face[0] > face[2]) {
// Shift to the left.
std::swap(pos_indices[0], pos_indices[1]);
std::swap(pos_indices[1], pos_indices[2]);
std::swap(face[0], face[1]);
std::swap(face[1], face[2]);
}
// Check if have encountered the same position triplet on a different face.
if (is_face_used.find(pos_indices) != is_face_used.end()) {
// Check if have encountered the same face before.
if (is_face_used.find(face) != is_face_used.end()) {
// Duplicate face. Ignore it.
num_duplicate_faces++;
} else {
// Insert new face to the set.
is_face_used.insert(pos_indices);
is_face_used.insert(face);
if (num_duplicate_faces > 0) {
// Copy the face to its new location.
mesh->SetFace(fi - num_duplicate_faces, f);
mesh->SetFace(fi - num_duplicate_faces, face);
}
}
}

View File

@ -141,38 +141,62 @@ TEST_F(MeshCleanupTest, TestAttributes) {
}
TEST_F(MeshCleanupTest, TestDuplicateFaces) {
// This test verifies that the mesh cleanup tool removes duplicate faces.
TriangleSoupMeshBuilder mb;
mb.Start(5);
const int pos_att_id =
mb.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32);
const int norm_att_id =
mb.AddAttribute(GeometryAttribute::NORMAL, 3, DT_FLOAT32);
// Five faces where only two are unique.
// Five faces where only two are unique in spatial domain and three are unique
// when we take into account the normal attribute.
// clang-format off
mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(0),
Vector3f(0.f, 0.f, 0.f).data(),
Vector3f(1.f, 0.f, 0.f).data(),
Vector3f(0.f, 1.f, 0.f).data());
mb.SetAttributeValuesForFace(norm_att_id, FaceIndex(0),
Vector3f(0.f, 0.f, 1.f).data(),
Vector3f(0.f, 0.f, 1.f).data(),
Vector3f(0.f, 0.f, 1.f).data());
mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(1),
Vector3f(0.f, 0.f, 0.f).data(),
Vector3f(1.f, 0.f, 0.f).data(),
Vector3f(0.f, 1.f, 0.f).data());
mb.SetAttributeValuesForFace(norm_att_id, FaceIndex(1),
Vector3f(0.f, 1.f, 0.f).data(),
Vector3f(0.f, 1.f, 0.f).data(),
Vector3f(0.f, 1.f, 0.f).data());
mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(2),
Vector3f(0.f, 0.f, 0.f).data(),
Vector3f(1.f, 0.f, 0.f).data(),
Vector3f(0.f, 1.f, 1.f).data());
mb.SetAttributeValuesForFace(norm_att_id, FaceIndex(2),
Vector3f(0.f, 0.f, 1.f).data(),
Vector3f(0.f, 0.f, 1.f).data(),
Vector3f(0.f, 0.f, 1.f).data());
mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(3),
Vector3f(1.f, 0.f, 0.f).data(),
Vector3f(0.f, 1.f, 0.f).data(),
Vector3f(0.f, 0.f, 0.f).data());
mb.SetAttributeValuesForFace(norm_att_id, FaceIndex(3),
Vector3f(0.f, 0.f, 1.f).data(),
Vector3f(0.f, 0.f, 1.f).data(),
Vector3f(0.f, 0.f, 1.f).data());
mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(4),
Vector3f(0.f, 0.f, 0.f).data(),
Vector3f(1.f, 0.f, 0.f).data(),
Vector3f(0.f, 1.f, 1.f).data());
mb.SetAttributeValuesForFace(norm_att_id, FaceIndex(4),
Vector3f(0.f, 0.f, 1.f).data(),
Vector3f(0.f, 0.f, 1.f).data(),
Vector3f(0.f, 0.f, 1.f).data());
// clang-format on
std::unique_ptr<Mesh> mesh = mb.Finalize();
@ -180,7 +204,7 @@ TEST_F(MeshCleanupTest, TestDuplicateFaces) {
ASSERT_EQ(mesh->num_faces(), 5);
const MeshCleanupOptions cleanup_options;
DRACO_ASSERT_OK(MeshCleanup::Cleanup(mesh.get(), cleanup_options));
ASSERT_EQ(mesh->num_faces(), 2);
ASSERT_EQ(mesh->num_faces(), 3);
}
} // namespace draco

View File

@ -53,8 +53,8 @@ class MeshFeatures {
void SetNullFeatureId(int null_feature_id);
int GetNullFeatureId() const;
// Index of the feature ID vertex attribute, e.g., 5 for an attribute named
// _FEATURE_ID_5, or -1 if the feature ID is not associated with vertices.
// Index of the feature ID vertex attribute in draco::Mesh or -1 if the
// feature ID is not associated with vertices.
void SetAttributeIndex(int attribute_index);
int GetAttributeIndex() const;

View File

@ -18,6 +18,10 @@
#ifndef DRACO_MESH_MESH_MISC_FUNCTIONS_H_
#define DRACO_MESH_MESH_MISC_FUNCTIONS_H_
#include <array>
#include <cmath>
#include <memory>
#include "draco/mesh/corner_table.h"
#include "draco/mesh/mesh.h"
@ -77,6 +81,10 @@ InterpolatedVectorT ComputeInterpolatedAttributeValueOnMeshFace(
for (int c = 0; c < 3; ++c) {
attribute.GetMappedValue(face[c], &(val[c][0]));
}
if (val[1] == val[0] && val[2] == val[0]) {
// No need to interpolate anything if all values are the same.
return val[0];
}
// Return an interpolated value.
InterpolatedVectorT res;
for (int d = 0; d < InterpolatedVectorT::dimension; ++d) {

View File

@ -18,10 +18,13 @@
#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>
#include "draco/attributes/geometry_attribute.h"
#include "draco/mesh/mesh_utils.h"
#include "draco/mesh/triangle_soup_mesh_builder.h"
#include "draco/point_cloud/point_cloud_builder.h"
#include "draco/texture/texture_map.h"
namespace draco {
@ -46,8 +49,8 @@ class MeshSplitterInternal {
const PointAttribute *split_attribute,
WorkData *work_data) const;
// Builds the meshes from the data accumulated in the builders.
StatusOr<MeshSplitter::MeshVector> BuildMeshes(const Mesh &mesh,
WorkData *work_data) const;
StatusOr<MeshSplitter::MeshVector> BuildMeshes(
const Mesh &mesh, WorkData *work_data, bool deduplicate_vertices) const;
};
namespace {
@ -65,7 +68,9 @@ void AddElementToBuilder(
MeshSplitter::MeshSplitter()
: preserve_materials_(false),
remove_unused_material_indices_(true),
preserve_mesh_features_(false) {}
preserve_mesh_features_(false),
preserve_structural_metadata_(false),
deduplicate_vertices_(true) {}
StatusOr<MeshSplitter::MeshVector> MeshSplitter::SplitMesh(
const Mesh &mesh, uint32_t split_attribute_id) {
@ -86,7 +91,7 @@ StatusOr<MeshSplitter::MeshVector> MeshSplitter::SplitMeshInternal(
mesh.attribute(split_attribute_id);
// Preserve the split attribute only if it is the material attribute and the
// |preserve_materials_| flag is set. Othwerwise the split attribute will get
// |preserve_materials_| flag is set. Otherwise, the split attribute will get
// discarded.
// TODO(ostava): We may revisit this later and add an option to always
// preserve the split attribute.
@ -126,8 +131,9 @@ StatusOr<MeshSplitter::MeshVector> MeshSplitter::SplitMeshInternal(
splitter_internal.AddElementsToBuilder(mesh, split_attribute, &work_data);
DRACO_ASSIGN_OR_RETURN(MeshVector out_meshes,
splitter_internal.BuildMeshes(mesh, &work_data));
DRACO_ASSIGN_OR_RETURN(
MeshVector out_meshes,
splitter_internal.BuildMeshes(mesh, &work_data, deduplicate_vertices_));
return FinalizeMeshes(mesh, work_data, std::move(out_meshes));
}
@ -181,7 +187,9 @@ void MeshSplitterInternal<BuilderT>::InitializeBuilder(
const GeometryAttribute *const src_att = mesh.attribute(ai);
work_data->att_id_map[ai] = work_data->builders[b_index].AddAttribute(
src_att->attribute_type(), src_att->num_components(),
src_att->data_type());
src_att->data_type(), src_att->normalized());
work_data->builders[b_index].SetAttributeName(work_data->att_id_map[ai],
src_att->name());
}
}
@ -252,7 +260,7 @@ void AddElementToBuilder(
template <>
StatusOr<MeshSplitter::MeshVector>
MeshSplitterInternal<TriangleSoupMeshBuilder>::BuildMeshes(
const Mesh &mesh, WorkData *work_data) const {
const Mesh &mesh, WorkData *work_data, bool deduplicate_vertices) const {
const int num_out_meshes = work_data->builders.size();
MeshSplitter::MeshVector out_meshes(num_out_meshes);
for (int mi = 0; mi < num_out_meshes; ++mi) {
@ -270,7 +278,7 @@ MeshSplitterInternal<TriangleSoupMeshBuilder>::BuildMeshes(
template <>
StatusOr<MeshSplitter::MeshVector>
MeshSplitterInternal<PointCloudBuilder>::BuildMeshes(
const Mesh &mesh, WorkData *work_data) const {
const Mesh &mesh, WorkData *work_data, bool deduplicate_vertices) const {
const int num_out_meshes = work_data->builders.size();
MeshSplitter::MeshVector out_meshes(num_out_meshes);
for (int mi = 0; mi < num_out_meshes; ++mi) {
@ -279,7 +287,8 @@ MeshSplitterInternal<PointCloudBuilder>::BuildMeshes(
}
// For point clouds, we first build a point cloud and copy it over into
// a draco::Mesh.
std::unique_ptr<PointCloud> pc = work_data->builders[mi].Finalize(true);
std::unique_ptr<PointCloud> pc =
work_data->builders[mi].Finalize(deduplicate_vertices);
if (pc == nullptr) {
continue;
}
@ -311,8 +320,67 @@ StatusOr<MeshSplitter::MeshVector> MeshSplitter::FinalizeMeshes(
}
out_meshes[mi]->SetName(mesh.GetName());
if (preserve_materials_) {
if (work_data.split_by_materials) {
// When splitting by material, only copy the material in use.
if (out_meshes[mi]->num_points() != 0 &&
mesh.GetMaterialLibrary().NumMaterials() != 0) {
uint64_t material_index = 0;
out_meshes[mi]
->GetNamedAttribute(GeometryAttribute::MATERIAL)
->GetMappedValue(PointIndex(0), &material_index);
// Populate empty materials and textures. Unused materials and
// textures will be cleared later.
out_meshes[mi]->GetMaterialLibrary().MutableMaterial(
mesh.GetMaterialLibrary().NumMaterials() - 1);
for (int i = 0;
i < mesh.GetMaterialLibrary().GetTextureLibrary().NumTextures();
++i) {
out_meshes[mi]
->GetMaterialLibrary()
.MutableTextureLibrary()
.PushTexture(std::make_unique<Texture>());
}
// Copy the material that we're actually going to use.
out_meshes[mi]
->GetMaterialLibrary()
.MutableMaterial(material_index)
->Copy(*mesh.GetMaterialLibrary().GetMaterial(material_index));
std::unordered_map<const Texture *, int> texture_to_index =
mesh.GetMaterialLibrary()
.GetTextureLibrary()
.ComputeTextureToIndexMap();
for (int tmi = 0; tmi < mesh.GetMaterialLibrary()
.GetMaterial(material_index)
->NumTextureMaps();
++tmi) {
const TextureMap *const source_texture_map =
mesh.GetMaterialLibrary()
.GetMaterial(material_index)
->GetTextureMapByIndex(tmi);
// Get the texture map index
const int texture_index =
texture_to_index[source_texture_map->texture()];
// Use the index to assign texture to the corresponding texture map
// on the split mesh.
TextureMap *new_texture_map = out_meshes[mi]
->GetMaterialLibrary()
.MutableMaterial(material_index)
->GetTextureMapByIndex(tmi);
new_texture_map->SetTexture(out_meshes[mi]
->GetMaterialLibrary()
.MutableTextureLibrary()
.GetTexture(texture_index));
new_texture_map->texture()->Copy(*source_texture_map->texture());
}
}
} else {
out_meshes[mi]->GetMaterialLibrary().Copy(mesh.GetMaterialLibrary());
}
}
// Copy metadata of the original mesh to the output meshes.
if (mesh.GetMetadata() != nullptr) {
@ -404,6 +472,62 @@ StatusOr<MeshSplitter::MeshVector> MeshSplitter::FinalizeMeshes(
MeshUtils::RemoveUnusedMeshFeatures(out_meshes[mi].get()));
}
if (preserve_structural_metadata_) {
// Copy proeprty attributes indices from the source |mesh| to the
// |out_meshes[mi]|.
for (int i = 0; i < mesh.NumPropertyAttributesIndices(); ++i) {
if (work_data.split_by_materials) {
// Copy over only those property attribute indices that were masked to
// the material corresponding to |mi|.
bool is_used = false;
if (mesh.NumPropertyAttributesIndexMaterialMasks(i) == 0) {
is_used = true;
} else {
for (int mask_index = 0;
mask_index < mesh.NumPropertyAttributesIndexMaterialMasks(i);
++mask_index) {
if (mesh.GetPropertyAttributesIndexMaterialMask(i, mask_index) ==
mi) {
is_used = true;
break;
}
}
}
if (!is_used) {
// Ignore this property attributes index.
continue;
}
}
// Create a copy of source property attributes index.
const int new_i = out_meshes[mi]->AddPropertyAttributesIndex(
mesh.GetPropertyAttributesIndex(i));
if (work_data.split_by_materials && !preserve_materials_) {
// If the input |mesh| was split by materials and we didn't preserve
// the materials, all property attributes indices must be masked to
// material 0.
out_meshes[mi]->AddPropertyAttributesIndexMaterialMask(new_i, 0);
} else {
// Otherwise property attributes index uses same masking as the source
// mesh because the material attribute is still present in the split
// meshes. Note that this masking can be later changed in
// RemoveUnusedMaterials() call below.
for (int mask_index = 0;
mask_index < mesh.NumPropertyAttributesIndexMaterialMasks(i);
++mask_index) {
out_meshes[mi]->AddPropertyAttributesIndexMaterialMask(
new_i,
mesh.GetPropertyAttributesIndexMaterialMask(i, mask_index));
}
}
}
// This will remove any property attributes indices that may not be be
// actually used by this |out_meshes[mi]| (e.g. because corresponding
// material indices were not present in this split mesh).
DRACO_RETURN_IF_ERROR(MeshUtils::RemoveUnusedPropertyAttributesIndices(
out_meshes[mi].get()));
}
// Remove unused materials after we remove mesh features because some of
// the mesh features may have referenced old material indices.
if (preserve_materials_) {
@ -442,8 +566,9 @@ StatusOr<MeshSplitter::MeshVector> MeshSplitter::SplitMeshToComponents(
AddElementToBuilder(mi, fi, target_fi, mesh, &work_data);
}
}
DRACO_ASSIGN_OR_RETURN(auto out_meshes,
splitter_internal.BuildMeshes(mesh, &work_data));
DRACO_ASSIGN_OR_RETURN(
auto out_meshes,
splitter_internal.BuildMeshes(mesh, &work_data, deduplicate_vertices_));
return FinalizeMeshes(mesh, work_data, std::move(out_meshes));
}

View File

@ -63,6 +63,25 @@ class MeshSplitter {
// Default = false.
void SetPreserveMeshFeatures(bool flag) { preserve_mesh_features_ = flag; }
// Sets a flag that tells the splitter to preserve structural metadata on the
// input mesh during mesh splitting. When set, the structural metadata like
// property attributes indices used on sub-meshes are going to be copied over.
// Any redundant structural metadata on sub-meshes are going to be deleted.
// Default = false.
void SetPreserveStructuralMetadata(bool flag) {
preserve_structural_metadata_ = flag;
}
// By default, the splitter will attempt to deduplicate vertices after
// splitting the mesh. This means lower memory usage and smaller output glTFs
// after encoding. However, for very large meshes, this may become an
// expensive operation. If that becomes an issue, you might want to consider
// disabling deduplication with |SetDeduplicateVertices(false)|.
//
// Note that at this moment, disabling deduplication works ONLY for point
// clouds.
void SetDeduplicateVertices(bool flag) { deduplicate_vertices_ = flag; }
// Splits the input |mesh| according to attribute values stored in the
// specified attribute. If the |mesh| contains faces, the attribute values
// need to be defined per-face, that is, all points attached to a single face
@ -98,6 +117,8 @@ class MeshSplitter {
bool preserve_materials_;
bool remove_unused_material_indices_;
bool preserve_mesh_features_;
bool preserve_structural_metadata_;
bool deduplicate_vertices_;
template <typename BuilderT>
friend class MeshSplitterInternal;

View File

@ -15,14 +15,21 @@
#include "draco/mesh/mesh_splitter.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include <cstdint>
#include <memory>
#include <unordered_set>
#include <utility>
#include <vector>
#include "draco/attributes/geometry_attribute.h"
#include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h"
#include "draco/core/draco_types.h"
#include "draco/core/vector_d.h"
#include "draco/io/mesh_io.h"
#include "draco/material/material.h"
#include "draco/mesh/mesh_misc_functions.h"
#include "draco/point_cloud/point_cloud_builder.h"
namespace {} // namespace
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -16,6 +16,7 @@
#include <memory>
#include <utility>
#include <vector>
#include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h"
@ -539,6 +540,57 @@ TEST(MeshTest, MeshCopyWithMeshFeatures) {
library_copy.GetTexture(1));
}
// Tests that mesh features are updated properly after a mesh attribute is
// deleted.
TEST(MeshTest, TestMeshFeaturesAttributeDeletion) {
const std::unique_ptr<draco::Mesh> mesh =
draco::ReadMeshFromTestFile("cube_att.obj");
ASSERT_NE(mesh, nullptr);
// Add feature ID set referring to an attribute.
const draco::MeshFeaturesIndex index_0 = mesh->AddMeshFeatures(
std::unique_ptr<draco::MeshFeatures>(new draco::MeshFeatures()));
mesh->GetMeshFeatures(index_0).SetLabel("planet");
mesh->GetMeshFeatures(index_0).SetFeatureCount(2);
mesh->GetMeshFeatures(index_0).SetAttributeIndex(1);
// Delete mesh attribute 0. This should update attribute index associated with
// mesh features |index_0| by one (to 0).
ASSERT_EQ(mesh->GetMeshFeatures(index_0).GetAttributeIndex(), 1);
mesh->DeleteAttribute(0);
ASSERT_EQ(mesh->GetMeshFeatures(index_0).GetAttributeIndex(), 0);
// Delete the new mesh attribute 0 and the mesh features |index_0| should not
// be associated with any attribute anymore.
mesh->DeleteAttribute(0);
ASSERT_EQ(mesh->GetMeshFeatures(index_0).GetAttributeIndex(), -1);
}
// Tests that we can identify which attributes are used by mesh features.
TEST(MeshTest, TestAttributeUsedByMeshFeatures) {
const std::unique_ptr<draco::Mesh> mesh =
draco::ReadMeshFromTestFile("cube_att.obj");
ASSERT_NE(mesh, nullptr);
// Add feature ID set referring to an attribute.
const draco::MeshFeaturesIndex index_0 = mesh->AddMeshFeatures(
std::unique_ptr<draco::MeshFeatures>(new draco::MeshFeatures()));
mesh->GetMeshFeatures(index_0).SetLabel("planet");
mesh->GetMeshFeatures(index_0).SetFeatureCount(2);
mesh->GetMeshFeatures(index_0).SetAttributeIndex(1);
// Ensure we can tell that attribute 1 is used by mesh features.
ASSERT_TRUE(mesh->IsAttributeUsedByMeshFeatures(1));
// Attribute 0 should not be used by mesh features.
ASSERT_FALSE(mesh->IsAttributeUsedByMeshFeatures(0));
// If the mesh features is deleted, attribute 1 should not be used by mesh
// features any more.
mesh->DeleteAttribute(1);
ASSERT_FALSE(mesh->IsAttributeUsedByMeshFeatures(1));
}
// Tests copying of a mesh with structural metadata.
TEST(MeshTest, TestCopyWithStructuralMetadata) {
const std::unique_ptr<draco::Mesh> mesh =
@ -546,22 +598,32 @@ TEST(MeshTest, TestCopyWithStructuralMetadata) {
ASSERT_NE(mesh, nullptr);
// Add structural metadata to the mesh.
draco::PropertyTable::Schema schema;
draco::StructuralMetadataSchema schema;
schema.json.SetString("Data");
mesh->GetStructuralMetadata().SetPropertyTableSchema(schema);
mesh->GetStructuralMetadata().SetSchema(schema);
mesh->AddPropertyAttributesIndex(0);
mesh->AddPropertyAttributesIndex(1);
// Copy the mesh.
draco::Mesh copy;
copy.Copy(*mesh);
// Check that the structural metadata has been copied.
ASSERT_EQ(
copy.GetStructuralMetadata().GetPropertyTableSchema().json.GetString(),
"Data");
ASSERT_EQ(copy.GetStructuralMetadata().GetSchema().json.GetString(), "Data");
ASSERT_EQ(copy.NumPropertyAttributesIndices(), 2);
ASSERT_EQ(copy.GetPropertyAttributesIndex(0), 0);
ASSERT_EQ(copy.GetPropertyAttributesIndex(1), 1);
// Check that property attributes index can be removed.
copy.RemovePropertyAttributesIndex(0);
ASSERT_EQ(copy.NumPropertyAttributesIndices(), 1);
ASSERT_EQ(copy.GetPropertyAttributesIndex(0), 1);
}
// Tests removing of unused materials for a mesh with mesh features.
TEST(MeshTest, RemoveUnusedMaterialsWithMeshFeatures) {
// Tests removing of unused materials for a mesh with mesh features and property
// attributes indices.
TEST(MeshTest,
RemoveUnusedMaterialsWithMeshFeaturesAndPropertyAttributesIndices) {
const std::unique_ptr<draco::Mesh> mesh =
draco::ReadMeshFromTestFile("BoxesMeta/glTF/BoxesMeta.gltf");
ASSERT_NE(mesh, nullptr);
@ -580,6 +642,12 @@ TEST(MeshTest, RemoveUnusedMaterialsWithMeshFeatures) {
ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(4), 0),
1);
// Input has two property attributes, one associated with each of the two
// materials.
ASSERT_EQ(mesh->NumPropertyAttributesIndices(), 2);
ASSERT_EQ(mesh->GetPropertyAttributesIndexMaterialMask(0, 0), 0);
ASSERT_EQ(mesh->GetPropertyAttributesIndexMaterialMask(1, 0), 1);
// Remove material 0.
draco::PointAttribute *mat_att = mesh->attribute(
mesh->GetNamedAttributeId(draco::GeometryAttribute::MATERIAL));
@ -588,17 +656,21 @@ TEST(MeshTest, RemoveUnusedMaterialsWithMeshFeatures) {
mat_att->SetAttributeValue(draco::AttributeValueIndex(0), &new_mat_index);
// This should not do anything because we still have the material 0 referenced
// by mesh features 0 and 1.
// by mesh features 0 and 1, as well as by property attributes at index 0.
mesh->RemoveUnusedMaterials();
ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 2);
ASSERT_EQ(mesh->NumMeshFeatures(), 5);
ASSERT_EQ(mesh->NumPropertyAttributesIndices(), 2);
// Now remove unused mesh features (should be 0 and 1).
// Now remove unused mesh features (should be 0 and 1) and property attributes
// indices (should be 0).
DRACO_ASSERT_OK(draco::MeshUtils::RemoveUnusedMeshFeatures(mesh.get()));
DRACO_ASSERT_OK(
draco::MeshUtils::RemoveUnusedPropertyAttributesIndices(mesh.get()));
ASSERT_EQ(mesh->NumMeshFeatures(), 3);
// All remaining mesh features should be still mapped to material 1.
ASSERT_EQ(mesh->NumMeshFeatures(), 3);
ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(0), 0),
1);
ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(1), 0),
@ -606,18 +678,50 @@ TEST(MeshTest, RemoveUnusedMaterialsWithMeshFeatures) {
ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(2), 0),
1);
// Remaining property attributes index should be still mapped to material 1.
ASSERT_EQ(mesh->NumPropertyAttributesIndices(), 1);
ASSERT_EQ(mesh->GetPropertyAttributesIndexMaterialMask(0, 0), 1);
// Now remove the unused materials (0).
mesh->RemoveUnusedMaterials();
// Only one material should be remaining and all the mesh features should now
// be mapped to material 0.
// Only one material should be remaining.
ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 1);
// All the mesh features should now be mapped to material 0.
ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(0), 0),
0);
ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(1), 0),
0);
ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(2), 0),
0);
// Property attributes index should now be mapped to material 0.
ASSERT_EQ(mesh->GetPropertyAttributesIndexMaterialMask(0, 0), 0);
}
// Tests that when we remove mesh features from a mesh, the associated vertex
// attributes and textures are not removed.
TEST(MeshTest, TestDeleteMeshFeatures) {
// The loaded mesh has several vertex attributes and textures used by the
// mesh features.
const std::unique_ptr<draco::Mesh> mesh =
draco::ReadMeshFromTestFile("BoxesMeta/glTF/BoxesMeta.gltf");
ASSERT_NE(mesh, nullptr);
ASSERT_GT(mesh->NumMeshFeatures(), 0);
draco::Mesh mesh_copy;
mesh_copy.Copy(*mesh);
// Delete all mesh features from the copy and ensure all vertex attributes
// and textures stay the same.
while (mesh_copy.NumMeshFeatures() > 0) {
mesh_copy.RemoveMeshFeatures(draco::MeshFeaturesIndex(0));
}
ASSERT_EQ(mesh_copy.NumMeshFeatures(), 0);
ASSERT_EQ(mesh_copy.num_attributes(), mesh->num_attributes());
ASSERT_EQ(mesh_copy.GetNonMaterialTextureLibrary().NumTextures(),
mesh->GetNonMaterialTextureLibrary().NumTextures());
}
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -167,15 +167,10 @@ void MeshUtils::MergeMetadata(const Mesh &src_mesh, Mesh *dst_mesh) {
}
}
Status MeshUtils::RemoveUnusedMeshFeatures(Mesh *mesh) {
// Unused mesh features are features that are not used by any face / vertex
// of the |mesh|. Currently, each mesh feature can be "masked" for specific
// materials, in which case we need to check whether the mask materials
// are present in the |mesh|. If not, we can remove the mesh features from the
// mesh.
// Returns indices of all used materials on the |mesh|.
std::unordered_set<int> FindUsedMaterials(const Mesh &mesh) {
const PointAttribute *const mat_att =
mesh->GetNamedAttribute(GeometryAttribute::MATERIAL);
// Find which materials are used.
mesh.GetNamedAttribute(GeometryAttribute::MATERIAL);
std::unordered_set<int> used_materials;
if (mat_att == nullptr) {
// Only material with index 0 is assumed to be used.
@ -187,7 +182,16 @@ Status MeshUtils::RemoveUnusedMeshFeatures(Mesh *mesh) {
used_materials.insert(mat_index);
}
}
return used_materials;
}
Status MeshUtils::RemoveUnusedMeshFeatures(Mesh *mesh) {
// Unused mesh features are features that are not used by any face / vertex
// of the |mesh|. Currently, each mesh feature can be "masked" for specific
// materials, in which case we need to check whether the mask materials
// are present in the |mesh|. If not, we can remove the mesh features from the
// mesh.
const std::unordered_set<int> used_materials = FindUsedMaterials(*mesh);
std::vector<MeshFeaturesIndex> unused_mesh_features;
for (MeshFeaturesIndex mfi(0); mfi < mesh->NumMeshFeatures(); ++mfi) {
bool is_used = false;
@ -247,6 +251,44 @@ Status MeshUtils::RemoveUnusedMeshFeatures(Mesh *mesh) {
return OkStatus();
}
Status MeshUtils::RemoveUnusedPropertyAttributesIndices(Mesh *mesh) {
// Unused property attributes indices are indices that are not used by any
// face / vertex of the |mesh|. Currently, each property attributes index can
// be "masked" for specific materials, in which case we need to check whether
// the mask materials are present in the |mesh|. If not, we can remove the
// property attributes from the mesh.
const std::unordered_set<int> used_materials = FindUsedMaterials(*mesh);
std::vector<int> unused_property_attributes_indices;
for (int i = 0; i < mesh->NumPropertyAttributesIndices(); ++i) {
bool is_used = false;
if (mesh->NumPropertyAttributesIndexMaterialMasks(i) == 0) {
is_used = true;
} else {
for (int mask_i = 0;
mask_i < mesh->NumPropertyAttributesIndexMaterialMasks(i);
++mask_i) {
const int material_index =
mesh->GetPropertyAttributesIndexMaterialMask(i, mask_i);
if (used_materials.count(material_index)) {
is_used = true;
break;
}
}
}
if (!is_used) {
unused_property_attributes_indices.push_back(i);
}
}
// Remove the unused property attributes indices (from back).
for (auto it = unused_property_attributes_indices.rbegin();
it != unused_property_attributes_indices.rend(); ++it) {
const int i = *it;
mesh->RemovePropertyAttributesIndex(i);
}
return OkStatus();
}
bool MeshUtils::FlipTextureUvValues(bool flip_u, bool flip_v,
PointAttribute *att) {
if (att->attribute_type() != GeometryAttribute::TEX_COORD) {

View File

@ -42,6 +42,9 @@ class MeshUtils {
// error is returned.
static Status RemoveUnusedMeshFeatures(Mesh *mesh);
// Removes unused property attributes indices from |mesh|.
static Status RemoveUnusedPropertyAttributesIndices(Mesh *mesh);
// Flips the UV values of |att|.
static bool FlipTextureUvValues(bool flip_u, bool flip_v,
PointAttribute *att);

View File

@ -386,6 +386,47 @@ TEST(MeshUtilsTest, RemoveUnusedMeshFeatures) {
}
}
TEST(MeshUtilsTest, RemoveUnusedPropertyAttributesIndices) {
// Test verifies that MeshUtils::RemoveUnusedPropertyAttributesIndices works
// as intended.
std::unique_ptr<draco::Mesh> mesh =
draco::ReadMeshFromTestFile("BoxesMeta/glTF/BoxesMeta.gltf");
ASSERT_NE(mesh, nullptr);
// The input mesh should have two property attributes indices.
ASSERT_EQ(mesh->NumPropertyAttributesIndices(), 2);
ASSERT_EQ(mesh->GetPropertyAttributesIndex(0), 0);
ASSERT_EQ(mesh->GetPropertyAttributesIndex(1), 1);
ASSERT_EQ(mesh->NumPropertyAttributesIndexMaterialMasks(0), 1);
ASSERT_EQ(mesh->NumPropertyAttributesIndexMaterialMasks(1), 1);
ASSERT_EQ(mesh->GetPropertyAttributesIndexMaterialMask(0, 0), 0);
ASSERT_EQ(mesh->GetPropertyAttributesIndexMaterialMask(1, 0), 1);
// Both indices should be used so calling the method below shouldn't do
// anything.
draco::MeshUtils::RemoveUnusedPropertyAttributesIndices(mesh.get());
ASSERT_EQ(mesh->NumPropertyAttributesIndices(), 2);
// Now remove material 1 that is mapped to second property attributes index.
draco::PointAttribute *mat_att = mesh->attribute(
mesh->GetNamedAttributeId(draco::GeometryAttribute::MATERIAL));
// This basically remaps all faces from material 1 to material 0.
uint32_t mat_index = 0;
mat_att->SetAttributeValue(draco::AttributeValueIndex(1), &mat_index);
// Try to remove the property attributes indices again.
draco::MeshUtils::RemoveUnusedPropertyAttributesIndices(mesh.get());
// One of the property attributes indices should have been removed.
ASSERT_EQ(mesh->NumPropertyAttributesIndices(), 1);
// Ensure the remaining property attributes index is mapped to the correct
// material.
ASSERT_EQ(mesh->NumPropertyAttributesIndexMaterialMasks(0), 1);
ASSERT_EQ(mesh->GetPropertyAttributesIndexMaterialMask(0, 0), 0);
}
} // namespace
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -14,6 +14,8 @@
//
#include "draco/mesh/triangle_soup_mesh_builder.h"
#include <string>
namespace draco {
void TriangleSoupMeshBuilder::Start(int num_faces) {
@ -96,4 +98,16 @@ std::unique_ptr<Mesh> TriangleSoupMeshBuilder::Finalize() {
return std::move(mesh_);
}
void TriangleSoupMeshBuilder::SetAttributeUniqueId(int att_id,
uint32_t unique_id) {
mesh_->attribute(att_id)->set_unique_id(unique_id);
}
#ifdef DRACO_TRANSCODER_SUPPORTED
void TriangleSoupMeshBuilder::SetAttributeName(int att_id,
const std::string &name) {
mesh_->attribute(att_id)->set_name(name);
}
#endif // DRACO_TRANSCODER_SUPPORTED
} // namespace draco

View File

@ -80,6 +80,14 @@ class TriangleSoupMeshBuilder {
mesh_->AddMetadata(std::move(metadata));
}
// Sets the unique ID for an attribute created with AddAttribute().
void SetAttributeUniqueId(int att_id, uint32_t unique_id);
#ifdef DRACO_TRANSCODER_SUPPORTED
// Sets attribute name.
void SetAttributeName(int att_id, const std::string &name);
#endif // DRACO_TRANSCODER_SUPPORTED
// Add metadata for an attribute.
void AddAttributeMetadata(int32_t att_id,
std::unique_ptr<AttributeMetadata> metadata) {

View File

@ -96,11 +96,15 @@ TEST_F(TriangleSoupMeshBuilderTest, CubeTest) {
Vector3f(0.f, 0.f, 1.f).data(),
Vector3f(0.f, 1.f, 0.f).data());
// clang-format on
#ifdef DRACO_TRANSCODER_SUPPORTED
mb.SetAttributeName(pos_att_id, "Bob");
#endif
std::unique_ptr<Mesh> mesh = mb.Finalize();
ASSERT_NE(mesh, nullptr) << "Failed to build the cube mesh.";
#ifdef DRACO_TRANSCODER_SUPPORTED
EXPECT_EQ(mesh->GetName(), "Cube");
EXPECT_EQ(mesh->attribute(pos_att_id)->name(), "Bob");
#endif
EXPECT_EQ(mesh->num_points(), 8) << "Unexpected number of vertices.";
EXPECT_EQ(mesh->num_faces(), 12) << "Unexpected number of faces.";
@ -207,6 +211,22 @@ TEST_F(TriangleSoupMeshBuilderTest, TestPerFaceAttribs) {
<< "Unexpected attribute element type.";
}
TEST_F(TriangleSoupMeshBuilderTest, PropagatesAttributeUniqueIds) {
// This test verifies that TriangleSoupMeshBuilder correctly applies
// unique IDs to attributes.
TriangleSoupMeshBuilder mb;
mb.Start(1);
const int pos_att_id =
mb.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32);
mb.SetAttributeValuesForFace(
pos_att_id, FaceIndex(0), Vector3f(0.f, 0.f, 0.f).data(),
Vector3f(1.f, 0.f, 0.f).data(), Vector3f(0.f, 1.f, 0.f).data());
mb.SetAttributeUniqueId(pos_att_id, 1234);
std::unique_ptr<Mesh> mesh = mb.Finalize();
ASSERT_NE(mesh, nullptr);
ASSERT_EQ(mesh->GetAttributeByUniqueId(1234), mesh->attribute(pos_att_id));
}
#ifdef DRACO_TRANSCODER_SUPPORTED
TEST_F(TriangleSoupMeshBuilderTest, NormalizedColor) {
// This tests, verifies that the mesh builder constructs a valid model with

View File

@ -0,0 +1,106 @@
// Copyright 2023 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.
//
#include "draco/metadata/property_attribute.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#ifdef DRACO_TRANSCODER_SUPPORTED
namespace draco {
bool PropertyAttribute::Property::operator==(const Property &other) const {
return name_ == other.name_ && attribute_name_ == other.attribute_name_;
}
void PropertyAttribute::Property::Copy(const Property &src) {
name_ = src.name_;
attribute_name_ = src.attribute_name_;
}
void PropertyAttribute::Property::SetName(const std::string &name) {
name_ = name;
}
const std::string &PropertyAttribute::Property::GetName() const {
return name_;
}
void PropertyAttribute::Property::SetAttributeName(const std::string &name) {
attribute_name_ = name;
}
const std::string &PropertyAttribute::Property::GetAttributeName() const {
return attribute_name_;
}
bool PropertyAttribute::operator==(const PropertyAttribute &other) const {
if (name_ != other.name_ || class_ != other.class_ ||
properties_.size() != other.properties_.size()) {
return false;
}
for (int i = 0; i < properties_.size(); ++i) {
if (*properties_[i] != *other.properties_[i]) {
return false;
}
}
return true;
}
void PropertyAttribute::Copy(const PropertyAttribute &src) {
name_ = src.name_;
class_ = src.class_;
properties_.clear();
properties_.reserve(src.properties_.size());
for (int i = 0; i < src.properties_.size(); ++i) {
std::unique_ptr<Property> property(new Property());
property->Copy(src.GetProperty(i));
properties_.push_back(std::move(property));
}
}
void PropertyAttribute::SetName(const std::string &value) { name_ = value; }
const std::string &PropertyAttribute::GetName() const { return name_; }
void PropertyAttribute::SetClass(const std::string &value) { class_ = value; }
const std::string &PropertyAttribute::GetClass() const { return class_; }
int PropertyAttribute::AddProperty(std::unique_ptr<Property> property) {
properties_.push_back(std::move(property));
return properties_.size() - 1;
}
int PropertyAttribute::NumProperties() const { return properties_.size(); }
const PropertyAttribute::Property &PropertyAttribute::GetProperty(
int index) const {
return *properties_[index];
}
PropertyAttribute::Property &PropertyAttribute::GetProperty(int index) {
return *properties_[index];
}
void PropertyAttribute::RemoveProperty(int index) {
properties_.erase(properties_.begin() + index);
}
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -0,0 +1,107 @@
// Copyright 2023 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.
//
#ifndef DRACO_METADATA_PROPERTY_ATTRIBUTE_H_
#define DRACO_METADATA_PROPERTY_ATTRIBUTE_H_
#include "draco/draco_features.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include <memory>
#include <string>
#include <vector>
#include "draco/core/status_or.h"
namespace draco {
// Describes a property attribute as defined in the EXT_structural_metadata glTF
// extension.
class PropertyAttribute {
public:
// Describes where property is stored (as an attribute).
class Property {
public:
// Creates an empty property.
Property() = default;
// Methods for comparing two properties.
bool operator==(const Property &other) const;
bool operator!=(const Property &other) const { return !(*this == other); }
// Copies all data from |src| property.
void Copy(const Property &src);
// Name of this property.
void SetName(const std::string &name);
const std::string &GetName() const;
// Name of glTF attribute containing property values, like "_DIRECTION".
void SetAttributeName(const std::string &name);
const std::string &GetAttributeName() const;
private:
// Name of this property as in structural metadata schema class property.
std::string name_;
// Name of glTF attribute containing property values, like "_DIRECTION".
std::string attribute_name_;
// TODO(vytyaz): Support property value modifiers min, max, offset, scale.
};
// Creates an empty property attribute.
PropertyAttribute() = default;
// Methods for comparing two property attributes.
bool operator==(const PropertyAttribute &other) const;
bool operator!=(const PropertyAttribute &other) const {
return !(*this == other);
}
// Copies all data from |src| property attribute.
void Copy(const PropertyAttribute &src);
// Name of this property attribute.
void SetName(const std::string &value);
const std::string &GetName() const;
// Class of this property attribute.
void SetClass(const std::string &value);
const std::string &GetClass() const;
// Properties.
int AddProperty(std::unique_ptr<Property> property);
int NumProperties() const;
const Property &GetProperty(int index) const;
Property &GetProperty(int index);
void RemoveProperty(int index);
private:
// The name of the property attribute, e.g., for display purposes.
std::string name_;
// The class in structural metadata schema that property values conform to.
std::string class_;
// Properties corresponding to schema class properties, describing where the
// property values are stored (as attributes).
std::vector<std::unique_ptr<Property>> properties_;
};
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED
#endif // DRACO_METADATA_PROPERTY_ATTRIBUTE_H_

View File

@ -0,0 +1,247 @@
// Copyright 2023 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.
//
#include "draco/metadata/property_attribute.h"
#include <memory>
#include <string>
#include <utility>
#include "draco/core/draco_test_utils.h"
namespace {
#ifdef DRACO_TRANSCODER_SUPPORTED
TEST(PropertyAttributeTest, TestPropertyDefaults) {
// Test construction of an empty property attribute property.
draco::PropertyAttribute::Property property;
ASSERT_TRUE(property.GetName().empty());
ASSERT_TRUE(property.GetAttributeName().empty());
}
TEST(PropertyAttributeTest, TestPropertyAttributeDefaults) {
// Test construction of an empty property attribute.
draco::PropertyAttribute attribute;
ASSERT_TRUE(attribute.GetName().empty());
ASSERT_TRUE(attribute.GetClass().empty());
ASSERT_EQ(attribute.NumProperties(), 0);
}
TEST(PropertyAttributeTest, TestPropertySettersAndGetters) {
// Test setter and getter methods of the property attribute property.
draco::PropertyAttribute::Property property;
property.SetName("The magnitude.");
property.SetAttributeName("_MAGNITUDE");
// Check that property members can be accessed via getters.
ASSERT_EQ(property.GetName(), "The magnitude.");
ASSERT_EQ(property.GetAttributeName(), "_MAGNITUDE");
}
TEST(PropertyAttributeTest, TestPropertyAttributeSettersAndGetters) {
// Test setter and getter methods of the property attribute.
draco::PropertyAttribute attribute;
attribute.SetName("The movement.");
attribute.SetClass("movement");
{
std::unique_ptr<draco::PropertyAttribute::Property> property(
new draco::PropertyAttribute::Property());
property->SetName("The magnitude.");
property->SetAttributeName("_MAGNITUDE");
ASSERT_EQ(attribute.AddProperty(std::move(property)), 0);
}
{
std::unique_ptr<draco::PropertyAttribute::Property> property(
new draco::PropertyAttribute::Property());
property->SetName("The direction.");
property->SetAttributeName("_DIRECTION");
ASSERT_EQ(attribute.AddProperty(std::move(property)), 1);
}
// Check that property attribute members can be accessed via getters.
ASSERT_EQ(attribute.GetName(), "The movement.");
ASSERT_EQ(attribute.GetClass(), "movement");
ASSERT_EQ(attribute.NumProperties(), 2);
ASSERT_EQ(attribute.GetProperty(0).GetName(), "The magnitude.");
ASSERT_EQ(attribute.GetProperty(0).GetAttributeName(), "_MAGNITUDE");
ASSERT_EQ(attribute.GetProperty(1).GetName(), "The direction.");
ASSERT_EQ(attribute.GetProperty(1).GetAttributeName(), "_DIRECTION");
// Check that properties can be removed.
attribute.RemoveProperty(0);
ASSERT_EQ(attribute.NumProperties(), 1);
ASSERT_EQ(attribute.GetProperty(0).GetName(), "The direction.");
ASSERT_EQ(attribute.GetProperty(0).GetAttributeName(), "_DIRECTION");
attribute.RemoveProperty(0);
ASSERT_EQ(attribute.NumProperties(), 0);
}
TEST(PropertyAttributeTest, TestPropertyCopy) {
// Test that property attribute property can be copied.
draco::PropertyAttribute::Property property;
property.SetName("The direction.");
property.SetAttributeName("_DIRECTION");
// Make a copy.
draco::PropertyAttribute::Property copy;
copy.Copy(property);
// Check the copy.
ASSERT_EQ(copy.GetName(), "The direction.");
ASSERT_EQ(copy.GetAttributeName(), "_DIRECTION");
}
TEST(PropertyAttributeTest, TestPropertyAttributeCopy) {
// Test that property attribute can be copied.
draco::PropertyAttribute attribute;
attribute.SetName("The movement.");
attribute.SetClass("movement");
{
std::unique_ptr<draco::PropertyAttribute::Property> property(
new draco::PropertyAttribute::Property());
property->SetName("The magnitude.");
property->SetAttributeName("_MAGNITUDE");
ASSERT_EQ(attribute.AddProperty(std::move(property)), 0);
}
{
std::unique_ptr<draco::PropertyAttribute::Property> property(
new draco::PropertyAttribute::Property());
property->SetName("The direction.");
property->SetAttributeName("_DIRECTION");
ASSERT_EQ(attribute.AddProperty(std::move(property)), 1);
}
// Make a copy.
draco::PropertyAttribute copy;
copy.Copy(attribute);
// Check the copy.
ASSERT_EQ(attribute.GetName(), "The movement.");
ASSERT_EQ(attribute.GetClass(), "movement");
ASSERT_EQ(attribute.NumProperties(), 2);
ASSERT_EQ(attribute.GetProperty(0).GetName(), "The magnitude.");
ASSERT_EQ(attribute.GetProperty(0).GetAttributeName(), "_MAGNITUDE");
ASSERT_EQ(attribute.GetProperty(1).GetName(), "The direction.");
ASSERT_EQ(attribute.GetProperty(1).GetAttributeName(), "_DIRECTION");
}
TEST(PropertyAttributeTest, TestPropertyCompare) {
// Test comparison of two properties.
typedef draco::PropertyAttribute::Property Property;
{
// Compare the same property object.
Property a;
ASSERT_TRUE(a == a);
ASSERT_FALSE(a != a);
}
{
// Compare two default property objects.
Property a;
Property b;
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two property objects with different names.
Property a;
Property b;
a.SetName("The magnitude.");
b.SetName("The direction.");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two property objects with different attribute names.
Property a;
Property b;
a.SetAttributeName("_MAGNITUDE");
b.SetAttributeName("_DIRECTION");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
}
TEST(PropertyAttributeTest, TestPropertyAttributeCompare) {
// Test comparison of two property attributes.
typedef draco::PropertyAttribute PropertyAttribute;
typedef draco::PropertyAttribute::Property Property;
{
// Compare the same property attribute object.
PropertyAttribute a;
ASSERT_TRUE(a == a);
ASSERT_FALSE(a != a);
}
{
// Compare two default property attributes.
PropertyAttribute a;
PropertyAttribute b;
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two property attributes with different names.
PropertyAttribute a;
PropertyAttribute b;
a.SetName("The movement.");
b.SetName("The reflection.");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two property attributes with different classes.
PropertyAttribute a;
PropertyAttribute b;
a.SetClass("movement");
b.SetClass("reflection");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two property attributes with identical properties.
PropertyAttribute a;
PropertyAttribute b;
a.AddProperty(std::unique_ptr<Property>(new Property));
b.AddProperty(std::unique_ptr<Property>(new Property));
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two property attributes with different number of properties.
PropertyAttribute a;
PropertyAttribute b;
a.AddProperty(std::unique_ptr<Property>(new Property));
b.AddProperty(std::unique_ptr<Property>(new Property));
b.AddProperty(std::unique_ptr<Property>(new Property));
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two property attributes with different properties.
PropertyAttribute a;
PropertyAttribute b;
std::unique_ptr<Property> p1(new Property);
std::unique_ptr<Property> p2(new Property);
p1->SetName("The magnitude.");
p2->SetName("The direction.");
a.AddProperty(std::move(p1));
b.AddProperty(std::move(p2));
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
}
#endif // DRACO_TRANSCODER_SUPPORTED
} // namespace

View File

@ -23,61 +23,6 @@
namespace draco {
bool PropertyTable::Schema::Object::operator==(const Object& other) const {
if (type_ != other.type_ || name_ != other.name_) {
return false;
}
switch (type_) {
case OBJECT:
if (objects_.size() != other.objects_.size()) {
return false;
}
for (int i = 0; i < objects_.size(); ++i) {
if (objects_[i] != other.objects_[i]) {
return false;
}
}
break;
case ARRAY:
if (array_.size() != other.array_.size()) {
return false;
}
for (int i = 0; i < array_.size(); ++i) {
if (array_[i] != other.array_[i]) {
return false;
}
}
break;
case STRING:
return string_ == other.string_;
case INTEGER:
return integer_ == other.integer_;
case BOOLEAN:
return boolean_ == other.boolean_;
}
return true;
}
void PropertyTable::Schema::Object::Copy(const Object& src) {
name_ = src.name_;
type_ = src.type_;
objects_.reserve(src.objects_.size());
for (const Object& obj : src.objects_) {
objects_.emplace_back();
objects_.back().Copy(obj);
}
array_.reserve(src.array_.size());
for (const Object& obj : src.array_) {
array_.emplace_back();
array_.back().Copy(obj);
}
string_ = src.string_;
integer_ = src.integer_;
boolean_ = src.boolean_;
}
PropertyTable::Property::Property() {}
bool PropertyTable::Property::Data::operator==(const Data &other) const {
return data == other.data && target == other.target;
}

View File

@ -19,108 +19,21 @@
#ifdef DRACO_TRANSCODER_SUPPORTED
#include <cstdint>
#include <cstring>
#include <limits>
#include <memory>
#include <string>
#include <vector>
#include "draco/core/status_or.h"
namespace draco {
// Describes a property table as defined in the EXT_structural_metadata glTF
// extension, including property table schema and table properties (columns).
// Describes a property table (properties are table columns) as defined in the
// EXT_structural_metadata glTF extension.
class PropertyTable {
public:
// Describes property table schema in the form of a JSON object.
struct Schema {
// JSON object of the schema.
// TODO(vytyaz): Consider using a third_party/json library. Currently there
// is a conflict between Filament's assert_invariant() macro and JSON
// library's assert_invariant() method that causes compile errors in Draco
// visualization library.
class Object {
public:
enum Type { OBJECT, ARRAY, STRING, INTEGER, BOOLEAN };
// Constructors.
Object() : Object("") {}
explicit Object(const std::string& name)
: name_(name), type_(OBJECT), integer_(0), boolean_(false) {}
Object(const std::string& name, const std::string& value) : Object(name) {
SetString(value);
}
Object(const std::string& name, const char* value) : Object(name) {
SetString(value);
}
Object(const std::string& name, int value) : Object(name) {
SetInteger(value);
}
Object(const std::string& name, bool value) : Object(name) {
SetBoolean(value);
}
// Methods for comparing two objects.
bool operator==(const Object& other) const;
bool operator!=(const Object& other) const { return !(*this == other); }
// Method for copying the object.
void Copy(const Object& src);
// Methods for getting object name and type.
const std::string& GetName() const { return name_; }
Type GetType() const { return type_; }
// Methods for getting object value.
const std::vector<Object>& GetObjects() const { return objects_; }
const std::vector<Object>& GetArray() const { return array_; }
const std::string& GetString() const { return string_; }
int GetInteger() const { return integer_; }
bool GetBoolean() const { return boolean_; }
// Methods for setting object value.
std::vector<Object>& SetObjects() {
type_ = OBJECT;
return objects_;
}
std::vector<Object>& SetArray() {
type_ = ARRAY;
return array_;
}
void SetString(const std::string& value) {
type_ = STRING;
string_ = value;
}
void SetInteger(int value) {
type_ = INTEGER;
integer_ = value;
}
void SetBoolean(bool value) {
type_ = BOOLEAN;
boolean_ = value;
}
private:
std::string name_;
Type type_;
std::vector<Object> objects_;
std::vector<Object> array_;
std::string string_;
int integer_;
bool boolean_;
};
// Valid schema top-level JSON object name is "schema".
Schema() : json("schema") {}
// Methods for comparing two schemas.
bool operator==(const Schema& other) const { return json == other.json; }
bool operator!=(const Schema& other) const { return !(*this == other); }
// Valid schema top-level JSON object is required to have child objects.
bool Empty() const { return json.GetObjects().empty(); }
// Top-level JSON object of the schema.
Object json;
};
// Describes a property (column) of a property table.
class Property {
public:
@ -151,10 +64,76 @@ class PropertyTable {
// Data type of the offset entries.
std::string type;
// Builds a new Offsets object given the offsets in |ints|. The resultant
// offsets will choose the smallest possible result.type that can contain
// all of the input |ints|.
static Offsets MakeFromInts(const std::vector<uint64_t> &ints) {
uint64_t max_value = 0;
for (uint64_t i = 0; i < ints.size(); ++i) {
if (ints[i] > max_value) {
max_value = ints[i];
}
}
Offsets result;
int bytes_per_int = 0;
if (max_value <= std::numeric_limits<uint8_t>::max()) {
result.type = "UINT8";
bytes_per_int = 1;
} else if (max_value <= std::numeric_limits<uint16_t>::max()) {
result.type = "UINT16";
bytes_per_int = 2;
} else if (max_value <= std::numeric_limits<uint32_t>::max()) {
result.type = "UINT32";
bytes_per_int = 4;
} else {
result.type = "UINT64";
bytes_per_int = 8;
}
result.data.data.resize(ints.size() * bytes_per_int);
for (uint64_t i = 0; i < ints.size(); ++i) {
// This assumes execution on a little endian platform.
memcpy(&result.data.data[i * bytes_per_int], &ints[i], bytes_per_int);
}
return result;
}
// Decodes the binary data in Offsets::data into offset integers as
// defined by the EXT_structural_metadata extension. Returns an error if
// Offsets::type is not one of the types allowed by the spec.
StatusOr<std::vector<uint64_t>> ParseToInts() const {
if (data.data.empty()) {
return std::vector<uint64_t>();
}
int bytes_per_int = 0;
if (type == "UINT8") {
bytes_per_int = 1;
} else if (type == "UINT16") {
bytes_per_int = 2;
} else if (type == "UINT32") {
bytes_per_int = 4;
} else if (type == "UINT64") {
bytes_per_int = 8;
} else {
return Status(Status::DRACO_ERROR, "Offsets data type invalid");
}
const int count = data.data.size() / bytes_per_int;
std::vector<uint64_t> result(count);
for (int i = 0; i < count; ++i) {
// This assumes execution on a little endian platform.
memcpy(&result[i], &data.data[i * bytes_per_int], bytes_per_int);
}
return result;
}
};
// Creates an empty property.
Property();
Property() = default;
// Methods for comparing two properties.
bool operator==(const Property &other) const;

View File

@ -14,11 +14,12 @@
//
#include "draco/metadata/property_table.h"
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h"
namespace {
@ -60,266 +61,6 @@ TEST(PropertyTableTest, TestPropertyTableDefaults) {
ASSERT_EQ(table.NumProperties(), 0);
}
TEST(PropertyTableTest, TestSchemaDefaults) {
// Test construction of an empty property table schema.
draco::PropertyTable::Schema schema;
ASSERT_TRUE(schema.Empty());
ASSERT_EQ(schema.json.GetName(), "schema");
ASSERT_EQ(schema.json.GetType(),
draco::PropertyTable::Schema::Object::OBJECT);
ASSERT_TRUE(schema.json.GetObjects().empty());
ASSERT_TRUE(schema.json.GetArray().empty());
ASSERT_TRUE(schema.json.GetString().empty());
ASSERT_EQ(schema.json.GetInteger(), 0);
ASSERT_FALSE(schema.json.GetBoolean());
}
TEST(PropertyTableTest, TestSchemaObjectDefaultConstructor) {
// Test construction of an empty property table schema object.
draco::PropertyTable::Schema::Object object;
ASSERT_TRUE(object.GetName().empty());
ASSERT_EQ(object.GetType(), draco::PropertyTable::Schema::Object::OBJECT);
ASSERT_TRUE(object.GetObjects().empty());
ASSERT_TRUE(object.GetArray().empty());
ASSERT_TRUE(object.GetString().empty());
ASSERT_EQ(object.GetInteger(), 0);
ASSERT_FALSE(object.GetBoolean());
}
TEST(PropertyTableTest, TestSchemaObjectNamedConstructor) {
// Test construction of a named property table schema object.
draco::PropertyTable::Schema::Object object("Flexible Demeanour");
ASSERT_EQ(object.GetName(), "Flexible Demeanour");
ASSERT_EQ(object.GetType(), draco::PropertyTable::Schema::Object::OBJECT);
ASSERT_TRUE(object.GetObjects().empty());
}
TEST(PropertyTableTest, TestSchemaObjectStringConstructor) {
// Test construction of property table schema object storing a string.
draco::PropertyTable::Schema::Object object("Flexible Demeanour", "GCU");
ASSERT_EQ(object.GetName(), "Flexible Demeanour");
ASSERT_EQ(object.GetType(), draco::PropertyTable::Schema::Object::STRING);
ASSERT_EQ(object.GetString(), "GCU");
}
TEST(PropertyTableTest, TestSchemaObjectIntegerConstructor) {
// Test construction of property table schema object storing an integer.
draco::PropertyTable::Schema::Object object("Flexible Demeanour", 12);
ASSERT_EQ(object.GetName(), "Flexible Demeanour");
ASSERT_EQ(object.GetType(), draco::PropertyTable::Schema::Object::INTEGER);
ASSERT_EQ(object.GetInteger(), 12);
}
TEST(PropertyTableTest, TestSchemaObjectBooleanConstructor) {
// Test construction of property table schema object storing a boolean.
draco::PropertyTable::Schema::Object object("Flexible Demeanour", true);
ASSERT_EQ(object.GetName(), "Flexible Demeanour");
ASSERT_EQ(object.GetType(), draco::PropertyTable::Schema::Object::BOOLEAN);
ASSERT_TRUE(object.GetBoolean());
}
TEST(PropertyTableTest, TestSchemaObjectSettersAndGetters) {
// Test value setters and getters of property table schema object.
typedef draco::PropertyTable::Schema::Object Object;
Object object;
ASSERT_EQ(object.GetType(), Object::OBJECT);
object.SetArray().push_back(Object("entry", 12));
ASSERT_EQ(object.GetType(), Object::ARRAY);
ASSERT_EQ(object.GetArray().size(), 1);
ASSERT_EQ(object.GetArray()[0].GetName(), "entry");
ASSERT_EQ(object.GetArray()[0].GetInteger(), 12);
object.SetObjects().push_back(Object("object", 9));
ASSERT_EQ(object.GetType(), Object::OBJECT);
ASSERT_EQ(object.GetObjects().size(), 1);
ASSERT_EQ(object.GetObjects()[0].GetName(), "object");
ASSERT_EQ(object.GetObjects()[0].GetInteger(), 9);
object.SetString("matter");
ASSERT_EQ(object.GetType(), Object::STRING);
ASSERT_EQ(object.GetString(), "matter");
object.SetInteger(5);
ASSERT_EQ(object.GetType(), Object::INTEGER);
ASSERT_EQ(object.GetInteger(), 5);
object.SetBoolean(true);
ASSERT_EQ(object.GetType(), Object::BOOLEAN);
ASSERT_EQ(object.GetBoolean(), true);
}
TEST(PropertyTableTest, TestSchemaCompare) {
typedef draco::PropertyTable::Schema Schema;
// Test comparison of two schema objects.
{
// Compare the same empty schema object.
Schema a;
ASSERT_TRUE(a == a);
ASSERT_FALSE(a != a);
}
{
// Compare two empty schema objects.
Schema a;
Schema b;
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two schema objects with different JSON objects.
Schema a;
Schema b;
a.json.SetBoolean(true);
b.json.SetBoolean(false);
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
}
TEST(PropertyTableTest, TestSchemaObjectCompare) {
// Test comparison of two schema JSON objects.
typedef draco::PropertyTable::Schema::Object Object;
{
// Compare the same object.
Object a;
ASSERT_TRUE(a == a);
ASSERT_FALSE(a != a);
}
{
// Compare two default objects.
Object a;
Object b;
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two objects with different names.
Object a("one");
Object b("two");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two objects with different types.
Object a;
Object b;
a.SetInteger(1);
b.SetString("one");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two identical string-type objects.
Object a;
Object b;
a.SetString("one");
b.SetString("one");
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two different string-type objects.
Object a;
Object b;
a.SetString("one");
b.SetString("two");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two identical integer-type objects.
Object a;
Object b;
a.SetInteger(1);
b.SetInteger(1);
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two different integer-type objects.
Object a;
Object b;
a.SetInteger(1);
b.SetInteger(2);
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two identical boolean-type objects.
Object a;
Object b;
a.SetBoolean(true);
b.SetBoolean(true);
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two different boolean-type objects.
Object a;
Object b;
a.SetBoolean(true);
b.SetBoolean(false);
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two identical object-type objects.
Object a;
Object b;
a.SetObjects().emplace_back("one");
b.SetObjects().emplace_back("one");
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two different object-type objects.
Object a;
Object b;
a.SetObjects().emplace_back("one");
b.SetObjects().emplace_back("two");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two object-type objects with different counts.
Object a;
Object b;
a.SetObjects().emplace_back("one");
b.SetObjects().emplace_back("one");
b.SetObjects().emplace_back("two");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two identical array-type objects.
Object a;
Object b;
a.SetArray().emplace_back("", 1);
b.SetArray().emplace_back("", 1);
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two different array-type objects.
Object a;
Object b;
a.SetArray().emplace_back("", 1);
b.SetArray().emplace_back("", 2);
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two array-type objects with different counts.
Object a;
Object b;
a.SetArray().emplace_back("", 1);
b.SetArray().emplace_back("", 1);
b.SetArray().emplace_back("", 2);
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
}
TEST(PropertyTableTest, TestPropertySettersAndGetters) {
// Test setter and getter methods of the property table property.
draco::PropertyTable::Property property;
@ -619,6 +360,78 @@ TEST(PropertyTableTest, TestPropertyTableCompare) {
}
}
TEST(PropertyTableTest, EnodesAndDecodesOffsetBuffers) {
{
// Encoding an offset buffer from small integers that should fit in an 8 bit
// integer.
std::vector<uint64_t> sample_offsets = {0x5u, 0x21u, 0x7u, 0x32u, 0xffu};
auto encoded_offsets =
draco::PropertyTable::Property::Offsets::MakeFromInts(sample_offsets);
ASSERT_EQ(encoded_offsets.data.data,
std::vector<uint8_t>({0x5u, 0x21u, 0x7u, 0x32u, 0xffu}));
ASSERT_EQ(encoded_offsets.type, "UINT8");
DRACO_ASSIGN_OR_ASSERT(std::vector<uint64_t> decoded_offsets,
encoded_offsets.ParseToInts());
ASSERT_EQ(decoded_offsets, sample_offsets);
}
{
// Encoding an offset buffer from medium sized integers that should fit in a
// 16 bit integer.
std::vector<uint64_t> sample_offsets = {0x5u, 0x21u, 0xffffu};
auto encoded_offsets =
draco::PropertyTable::Property::Offsets::MakeFromInts(sample_offsets);
ASSERT_EQ(encoded_offsets.data.data,
std::vector<uint8_t>({0x5u, 0u, 0x21u, 0u, 0xffu, 0xffu}));
ASSERT_EQ(encoded_offsets.type, "UINT16");
DRACO_ASSIGN_OR_ASSERT(std::vector<uint64_t> decoded_offsets,
encoded_offsets.ParseToInts());
ASSERT_EQ(decoded_offsets, sample_offsets);
}
{
// Encoding an offset buffer from medium sized integers that should fit in a
// 32 bit integer.
std::vector<uint64_t> sample_offsets = {0x5u, 0x21u, 0xffffffffu};
auto encoded_offsets =
draco::PropertyTable::Property::Offsets::MakeFromInts(sample_offsets);
ASSERT_EQ(encoded_offsets.data.data,
std::vector<uint8_t>({0x5u, 0u, 0u, 0u, 0x21u, 0u, 0u, 0u, 0xffu,
0xffu, 0xffu, 0xffu}));
ASSERT_EQ(encoded_offsets.type, "UINT32");
DRACO_ASSIGN_OR_ASSERT(std::vector<uint64_t> decoded_offsets,
encoded_offsets.ParseToInts());
ASSERT_EQ(decoded_offsets, sample_offsets);
}
{
// Encoding an offset buffer from large integers that won't fit in a 32 bit
// integer.
std::vector<uint64_t> sample_offsets = {0x5u, 0x21u, 0x100000000u};
auto encoded_offsets =
draco::PropertyTable::Property::Offsets::MakeFromInts(sample_offsets);
ASSERT_EQ(encoded_offsets.data.data,
std::vector<uint8_t>({0x5u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
0x21u, 0u, 0u, 0u, 0u, 0u, 0u, 0u,
0u, 0u, 0u, 0u, 1u, 0u, 0u, 0u}));
ASSERT_EQ(encoded_offsets.type, "UINT64");
DRACO_ASSIGN_OR_ASSERT(std::vector<uint64_t> decoded_offsets,
encoded_offsets.ParseToInts());
ASSERT_EQ(decoded_offsets, sample_offsets);
}
{
// Decoding a malformed buffer should return an error.
draco::PropertyTable::Property::Offsets broken_offsets;
broken_offsets.data.data = std::vector<uint8_t>({0, 0, 0, 0});
broken_offsets.type = "BROKEN_TYPE";
draco::StatusOr<std::vector<uint64_t>> decoded_offsets =
broken_offsets.ParseToInts();
ASSERT_FALSE(decoded_offsets.ok());
}
}
#endif // DRACO_TRANSCODER_SUPPORTED
} // namespace

View File

@ -21,30 +21,54 @@
namespace draco {
StructuralMetadata::StructuralMetadata() {}
// Returns true if vectors |a| and |b| have the same size and their entries
// (unique pointers) point to objects that compare equally.
template <typename T>
bool VectorsAreEqual(const std::vector<std::unique_ptr<T>> &a,
const std::vector<std::unique_ptr<T>> &b) {
if (a.size() != b.size()) {
return false;
}
for (int i = 0; i < a.size(); ++i) {
if (*a[i] != *b[i]) {
return false;
}
}
return true;
}
bool StructuralMetadata::operator==(const StructuralMetadata &other) const {
return property_table_schema_ == other.property_table_schema_ &&
property_tables_ == other.property_tables_;
return schema_ == other.schema_ &&
VectorsAreEqual(property_tables_, other.property_tables_) &&
VectorsAreEqual(property_attributes_, other.property_attributes_);
}
void StructuralMetadata::Copy(const StructuralMetadata &src) {
property_table_schema_.json.Copy(src.property_table_schema_.json);
// Copy schema.
schema_.json.Copy(src.schema_.json);
// Copy property tables.
property_tables_.resize(src.property_tables_.size());
for (int i = 0; i < property_tables_.size(); ++i) {
property_tables_[i] = std::unique_ptr<PropertyTable>(new PropertyTable());
property_tables_[i]->Copy(*src.property_tables_[i]);
}
// Copy property attributes.
property_attributes_.resize(src.property_attributes_.size());
for (int i = 0; i < property_attributes_.size(); ++i) {
property_attributes_[i] =
std::unique_ptr<PropertyAttribute>(new PropertyAttribute());
property_attributes_[i]->Copy(*src.property_attributes_[i]);
}
}
void StructuralMetadata::SetPropertyTableSchema(
const PropertyTable::Schema &schema) {
property_table_schema_ = schema;
void StructuralMetadata::SetSchema(const StructuralMetadataSchema &schema) {
schema_ = schema;
}
const PropertyTable::Schema &StructuralMetadata::GetPropertyTableSchema()
const {
return property_table_schema_;
const StructuralMetadataSchema &StructuralMetadata::GetSchema() const {
return schema_;
}
int StructuralMetadata::AddPropertyTable(
@ -69,6 +93,29 @@ void StructuralMetadata::RemovePropertyTable(int index) {
property_tables_.erase(property_tables_.begin() + index);
}
int StructuralMetadata::AddPropertyAttribute(
std::unique_ptr<PropertyAttribute> property_attribute) {
property_attributes_.push_back(std::move(property_attribute));
return property_attributes_.size() - 1;
}
int StructuralMetadata::NumPropertyAttributes() const {
return property_attributes_.size();
}
const PropertyAttribute &StructuralMetadata::GetPropertyAttribute(
int index) const {
return *property_attributes_[index];
}
PropertyAttribute &StructuralMetadata::GetPropertyAttribute(int index) {
return *property_attributes_[index];
}
void StructuralMetadata::RemovePropertyAttribute(int index) {
property_attributes_.erase(property_attributes_.begin() + index);
}
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -20,17 +20,18 @@
#ifdef DRACO_TRANSCODER_SUPPORTED
#include <memory>
#include <string>
#include <vector>
#include "draco/metadata/property_attribute.h"
#include "draco/metadata/property_table.h"
#include "draco/metadata/structural_metadata_schema.h"
namespace draco {
// Holds data associated with EXT_structural_metadata glTF extension.
class StructuralMetadata {
public:
StructuralMetadata();
StructuralMetadata() = default;
// Methods for comparing two structural metadata objects.
bool operator==(const StructuralMetadata &other) const;
@ -41,9 +42,9 @@ class StructuralMetadata {
// Copies |src| structural metadata into this object.
void Copy(const StructuralMetadata &src);
// Property table schema.
void SetPropertyTableSchema(const PropertyTable::Schema &schema);
const PropertyTable::Schema &GetPropertyTableSchema() const;
// Schema of the structural metadata.
void SetSchema(const StructuralMetadataSchema &schema);
const StructuralMetadataSchema &GetSchema() const;
// Property tables.
int AddPropertyTable(std::unique_ptr<PropertyTable> property_table);
@ -52,10 +53,23 @@ class StructuralMetadata {
PropertyTable &GetPropertyTable(int index);
void RemovePropertyTable(int index);
// Property attributes.
int AddPropertyAttribute(
std::unique_ptr<PropertyAttribute> property_attribute);
int NumPropertyAttributes() const;
const PropertyAttribute &GetPropertyAttribute(int index) const;
PropertyAttribute &GetPropertyAttribute(int index);
void RemovePropertyAttribute(int index);
private:
// Property table schema and property tables.
PropertyTable::Schema property_table_schema_;
// Schema of the structural metadata.
StructuralMetadataSchema schema_;
// Property tables.
std::vector<std::unique_ptr<PropertyTable>> property_tables_;
// Property attributes.
std::vector<std::unique_ptr<PropertyAttribute>> property_attributes_;
};
} // namespace draco

View File

@ -0,0 +1,158 @@
// Copyright 2023 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.
//
#include "draco/metadata/structural_metadata_schema.h"
#include <string>
#include <vector>
#ifdef DRACO_TRANSCODER_SUPPORTED
namespace draco {
StructuralMetadataSchema::Object::Object() : Object("") {}
StructuralMetadataSchema::Object::Object(const std::string &name)
: name_(name), type_(OBJECT), integer_(0), boolean_(false) {}
StructuralMetadataSchema::Object::Object(const std::string &name,
const std::string &value)
: Object(name) {
SetString(value);
}
StructuralMetadataSchema::Object::Object(const std::string &name,
const char *value)
: Object(name) {
SetString(value);
}
StructuralMetadataSchema::Object::Object(const std::string &name, int value)
: Object(name) {
SetInteger(value);
}
StructuralMetadataSchema::Object::Object(const std::string &name, bool value)
: Object(name) {
SetBoolean(value);
}
bool StructuralMetadataSchema::Object::operator==(const Object &other) const {
if (type_ != other.type_ || name_ != other.name_) {
return false;
}
switch (type_) {
case OBJECT:
if (objects_.size() != other.objects_.size()) {
return false;
}
for (int i = 0; i < objects_.size(); ++i) {
if (objects_[i] != other.objects_[i]) {
return false;
}
}
break;
case ARRAY:
if (array_.size() != other.array_.size()) {
return false;
}
for (int i = 0; i < array_.size(); ++i) {
if (array_[i] != other.array_[i]) {
return false;
}
}
break;
case STRING:
return string_ == other.string_;
case INTEGER:
return integer_ == other.integer_;
case BOOLEAN:
return boolean_ == other.boolean_;
}
return true;
}
bool StructuralMetadataSchema::Object::operator!=(const Object &other) const {
return !(*this == other);
}
void StructuralMetadataSchema::Object::Copy(const Object &src) {
name_ = src.name_;
type_ = src.type_;
objects_.reserve(src.objects_.size());
for (const Object &obj : src.objects_) {
objects_.emplace_back();
objects_.back().Copy(obj);
}
array_.reserve(src.array_.size());
for (const Object &obj : src.array_) {
array_.emplace_back();
array_.back().Copy(obj);
}
string_ = src.string_;
integer_ = src.integer_;
boolean_ = src.boolean_;
}
const StructuralMetadataSchema::Object *
StructuralMetadataSchema::Object::GetObjectByName(
const std::string &name) const {
for (const Object &obj : objects_) {
if (obj.GetName() == name) {
return &obj;
}
}
return nullptr;
}
std::vector<StructuralMetadataSchema::Object> &
StructuralMetadataSchema::Object::SetObjects() {
type_ = OBJECT;
return objects_;
}
std::vector<StructuralMetadataSchema::Object> &
StructuralMetadataSchema::Object::SetArray() {
type_ = ARRAY;
return array_;
}
void StructuralMetadataSchema::Object::SetString(const std::string &value) {
type_ = STRING;
string_ = value;
}
void StructuralMetadataSchema::Object::SetInteger(int value) {
type_ = INTEGER;
integer_ = value;
}
void StructuralMetadataSchema::Object::SetBoolean(bool value) {
type_ = BOOLEAN;
boolean_ = value;
}
bool StructuralMetadataSchema::operator==(
const StructuralMetadataSchema &other) const {
return json == other.json;
}
bool StructuralMetadataSchema::operator!=(
const StructuralMetadataSchema &other) const {
return !(*this == other);
}
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -0,0 +1,118 @@
// Copyright 2023 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.
//
#ifndef DRACO_METADATA_SCHEMA_H_
#define DRACO_METADATA_SCHEMA_H_
#include "draco/draco_features.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include <cstdint>
#include <cstring>
#include <limits>
#include <memory>
#include <string>
#include <vector>
#include "draco/core/status_or.h"
namespace draco {
// Defines schema that describes the structure of the metadata as defined in the
// EXT_structural_metadata glTF extension, in the form of a JSON object.
struct StructuralMetadataSchema {
// JSON object of the schema.
// TODO(vytyaz): Consider using a third_party/json library. Currently there
// is a conflict between Filament's assert_invariant() macro and JSON
// library's assert_invariant() method that causes compile errors in Draco
// visualization library.
class Object {
public:
enum Type { OBJECT, ARRAY, STRING, INTEGER, BOOLEAN };
// Constructors.
Object();
explicit Object(const std::string &name);
Object(const std::string &name, const std::string &value);
Object(const std::string &name, const char *value);
Object(const std::string &name, int value);
Object(const std::string &name, bool value);
// Methods for comparing two objects.
bool operator==(const Object &other) const;
bool operator!=(const Object &other) const;
// Method for copying the object.
void Copy(const Object &src);
// Methods for getting object name and type.
const std::string &GetName() const { return name_; }
Type GetType() const { return type_; }
// Methods for getting object value.
const std::vector<Object> &GetObjects() const { return objects_; }
const std::vector<Object> &GetArray() const { return array_; }
const std::string &GetString() const { return string_; }
int GetInteger() const { return integer_; }
bool GetBoolean() const { return boolean_; }
// Looks for a child object matching the given |name|. If no object is
// found, returns nullptr.
//
// Note that this is not recursive. I.e., for the following object:
//
// { "level1": { "level2": "value" } }
//
// GetObjectByName("level1") will return '{ "level2": "value" }', but
// GetObjectByName("level2") will return nullptr. Instead, the user should
// use GetObjectByName("level1")->GetObjectByName("level2") to get the
// nested child. Note that this follows the typical JSON semantics.
const Object *GetObjectByName(const std::string &name) const;
// Methods for setting object value.
std::vector<Object> &SetObjects();
std::vector<Object> &SetArray();
void SetString(const std::string &value);
void SetInteger(int value);
void SetBoolean(bool value);
private:
std::string name_;
Type type_;
std::vector<Object> objects_;
std::vector<Object> array_;
std::string string_;
int integer_;
bool boolean_;
};
// Valid schema top-level JSON object name is "schema".
StructuralMetadataSchema() : json("schema") {}
// Methods for comparing two schemas.
bool operator==(const StructuralMetadataSchema &other) const;
bool operator!=(const StructuralMetadataSchema &other) const;
// Valid schema top-level JSON object is required to have child objects.
bool Empty() const { return json.GetObjects().empty(); }
// Top-level JSON object of the schema.
Object json;
};
} // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED
#endif // DRACO_METADATA_SCHEMA_H_

View File

@ -0,0 +1,323 @@
// Copyright 2023 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.
//
#include "draco/metadata/structural_metadata_schema.h"
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "draco/core/draco_test_utils.h"
namespace {
#ifdef DRACO_TRANSCODER_SUPPORTED
TEST(StructuralMetadataSchemaTest, TestSchemaDefaults) {
// Test construction of an empty schema.
draco::StructuralMetadataSchema schema;
ASSERT_TRUE(schema.Empty());
ASSERT_EQ(schema.json.GetName(), "schema");
ASSERT_EQ(schema.json.GetType(),
draco::StructuralMetadataSchema::Object::OBJECT);
ASSERT_TRUE(schema.json.GetObjects().empty());
ASSERT_TRUE(schema.json.GetArray().empty());
ASSERT_TRUE(schema.json.GetString().empty());
ASSERT_EQ(schema.json.GetInteger(), 0);
ASSERT_FALSE(schema.json.GetBoolean());
}
TEST(StructuralMetadataSchemaTest, TestSchemaObjectDefaultConstructor) {
// Test construction of an empty schema object.
draco::StructuralMetadataSchema::Object object;
ASSERT_TRUE(object.GetName().empty());
ASSERT_EQ(object.GetType(), draco::StructuralMetadataSchema::Object::OBJECT);
ASSERT_TRUE(object.GetObjects().empty());
ASSERT_TRUE(object.GetArray().empty());
ASSERT_TRUE(object.GetString().empty());
ASSERT_EQ(object.GetInteger(), 0);
ASSERT_FALSE(object.GetBoolean());
}
TEST(StructuralMetadataSchemaTest, TestSchemaObjectNamedConstructor) {
// Test construction of a named schema object.
draco::StructuralMetadataSchema::Object object("Flexible Demeanour");
ASSERT_EQ(object.GetName(), "Flexible Demeanour");
ASSERT_EQ(object.GetType(), draco::StructuralMetadataSchema::Object::OBJECT);
ASSERT_TRUE(object.GetObjects().empty());
}
TEST(StructuralMetadataSchemaTest, TestSchemaObjectStringConstructor) {
// Test construction of schema object storing a string.
draco::StructuralMetadataSchema::Object object("Flexible Demeanour", "GCU");
ASSERT_EQ(object.GetName(), "Flexible Demeanour");
ASSERT_EQ(object.GetType(), draco::StructuralMetadataSchema::Object::STRING);
ASSERT_EQ(object.GetString(), "GCU");
}
TEST(StructuralMetadataSchemaTest, TestSchemaObjectIntegerConstructor) {
// Test construction of schema object storing an integer.
draco::StructuralMetadataSchema::Object object("Flexible Demeanour", 12);
ASSERT_EQ(object.GetName(), "Flexible Demeanour");
ASSERT_EQ(object.GetType(), draco::StructuralMetadataSchema::Object::INTEGER);
ASSERT_EQ(object.GetInteger(), 12);
}
TEST(StructuralMetadataSchemaTest, TestSchemaObjectBooleanConstructor) {
// Test construction of schema object storing a boolean.
draco::StructuralMetadataSchema::Object object("Flexible Demeanour", true);
ASSERT_EQ(object.GetName(), "Flexible Demeanour");
ASSERT_EQ(object.GetType(), draco::StructuralMetadataSchema::Object::BOOLEAN);
ASSERT_TRUE(object.GetBoolean());
}
TEST(StructuralMetadataSchemaTest, TestSchemaObjectSettersAndGetters) {
// Test value setters and getters of schema object.
typedef draco::StructuralMetadataSchema::Object Object;
Object object;
ASSERT_EQ(object.GetType(), Object::OBJECT);
object.SetArray().push_back(Object("entry", 12));
ASSERT_EQ(object.GetType(), Object::ARRAY);
ASSERT_EQ(object.GetArray().size(), 1);
ASSERT_EQ(object.GetArray()[0].GetName(), "entry");
ASSERT_EQ(object.GetArray()[0].GetInteger(), 12);
object.SetObjects().push_back(Object("object", 9));
ASSERT_EQ(object.GetType(), Object::OBJECT);
ASSERT_EQ(object.GetObjects().size(), 1);
ASSERT_EQ(object.GetObjects()[0].GetName(), "object");
ASSERT_EQ(object.GetObjects()[0].GetInteger(), 9);
object.SetString("matter");
ASSERT_EQ(object.GetType(), Object::STRING);
ASSERT_EQ(object.GetString(), "matter");
object.SetInteger(5);
ASSERT_EQ(object.GetType(), Object::INTEGER);
ASSERT_EQ(object.GetInteger(), 5);
object.SetBoolean(true);
ASSERT_EQ(object.GetType(), Object::BOOLEAN);
ASSERT_EQ(object.GetBoolean(), true);
}
TEST(StructuralMetadataSchemaTest, TestSchemaObjectLookupByName) {
// Test the GetObjectByName() getter.
typedef draco::StructuralMetadataSchema::Object Object;
Object object;
ASSERT_EQ(object.GetType(), Object::OBJECT);
auto &objects = object.SetObjects();
objects.push_back(Object("object1", 1));
objects.push_back(Object("object2", "two"));
Object object3("object3");
object3.SetObjects().push_back(Object("child_object", "child"));
objects.push_back(object3);
ASSERT_EQ(object.GetObjectByName("child_object"), nullptr);
ASSERT_NE(object.GetObjectByName("object1"), nullptr);
ASSERT_EQ(object.GetObjectByName("object1")->GetInteger(), 1);
ASSERT_NE(object.GetObjectByName("object2"), nullptr);
ASSERT_EQ(object.GetObjectByName("object2")->GetString(), "two");
ASSERT_NE(object.GetObjectByName("object3"), nullptr);
ASSERT_NE(object.GetObjectByName("object3")->GetObjectByName("child_object"),
nullptr);
ASSERT_EQ(object.GetObjectByName("object3")
->GetObjectByName("child_object")
->GetString(),
"child");
}
TEST(StructuralMetadataSchemaTest, TestSchemaCompare) {
typedef draco::StructuralMetadataSchema Schema;
// Test comparison of two schema objects.
{
// Compare the same empty schema object.
Schema a;
ASSERT_TRUE(a == a);
ASSERT_FALSE(a != a);
}
{
// Compare two empty schema objects.
Schema a;
Schema b;
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two schema objects with different JSON objects.
Schema a;
Schema b;
a.json.SetBoolean(true);
b.json.SetBoolean(false);
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
}
TEST(StructuralMetadataSchemaTest, TestSchemaObjectCompare) {
// Test comparison of two schema JSON objects.
typedef draco::StructuralMetadataSchema::Object Object;
{
// Compare the same object.
Object a;
ASSERT_TRUE(a == a);
ASSERT_FALSE(a != a);
}
{
// Compare two default objects.
Object a;
Object b;
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two objects with different names.
Object a("one");
Object b("two");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two objects with different types.
Object a;
Object b;
a.SetInteger(1);
b.SetString("one");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two identical string-type objects.
Object a;
Object b;
a.SetString("one");
b.SetString("one");
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two different string-type objects.
Object a;
Object b;
a.SetString("one");
b.SetString("two");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two identical integer-type objects.
Object a;
Object b;
a.SetInteger(1);
b.SetInteger(1);
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two different integer-type objects.
Object a;
Object b;
a.SetInteger(1);
b.SetInteger(2);
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two identical boolean-type objects.
Object a;
Object b;
a.SetBoolean(true);
b.SetBoolean(true);
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two different boolean-type objects.
Object a;
Object b;
a.SetBoolean(true);
b.SetBoolean(false);
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two identical object-type objects.
Object a;
Object b;
a.SetObjects().emplace_back("one");
b.SetObjects().emplace_back("one");
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two different object-type objects.
Object a;
Object b;
a.SetObjects().emplace_back("one");
b.SetObjects().emplace_back("two");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two object-type objects with different counts.
Object a;
Object b;
a.SetObjects().emplace_back("one");
b.SetObjects().emplace_back("one");
b.SetObjects().emplace_back("two");
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two identical array-type objects.
Object a;
Object b;
a.SetArray().emplace_back("", 1);
b.SetArray().emplace_back("", 1);
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two different array-type objects.
Object a;
Object b;
a.SetArray().emplace_back("", 1);
b.SetArray().emplace_back("", 2);
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two array-type objects with different counts.
Object a;
Object b;
a.SetArray().emplace_back("", 1);
b.SetArray().emplace_back("", 1);
b.SetArray().emplace_back("", 2);
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
}
#endif // DRACO_TRANSCODER_SUPPORTED
} // namespace

View File

@ -16,7 +16,6 @@
#include <memory>
#include <utility>
#include <vector>
#include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h"
@ -30,9 +29,9 @@ TEST(StructuralMetadataTest, TestCopy) {
draco::StructuralMetadata structural_metadata;
// Add property table schema to structural metadata.
draco::PropertyTable::Schema schema;
draco::StructuralMetadataSchema schema;
schema.json.SetString("Culture");
structural_metadata.SetPropertyTableSchema(schema);
structural_metadata.SetSchema(schema);
// Add property table to structural metadata.
std::unique_ptr<draco::PropertyTable> table(new draco::PropertyTable());
@ -58,7 +57,7 @@ TEST(StructuralMetadataTest, TestCopy) {
copy.Copy(structural_metadata);
// Check that the structural metadata property table schema has been copied.
ASSERT_EQ(copy.GetPropertyTableSchema().json.GetString(), "Culture");
ASSERT_EQ(copy.GetSchema().json.GetString(), "Culture");
// Check that the structural metadata property table has been copied.
ASSERT_EQ(copy.NumPropertyTables(), 1);
@ -114,6 +113,7 @@ TEST(StructuralMetadataTest, TestPropertyTables) {
TEST(StructuralMetadataTest, TestCompare) {
// Test comparison of two structural metadata objects.
typedef draco::PropertyTable PropertyTable;
typedef draco::PropertyAttribute PropertyAttribute;
{
// Compare the same structural metadata object.
draco::StructuralMetadata a;
@ -131,17 +131,17 @@ TEST(StructuralMetadataTest, TestCompare) {
// Compare two structural metadata objects with different schemas.
draco::StructuralMetadata a;
draco::StructuralMetadata b;
PropertyTable::Schema s1;
PropertyTable::Schema s2;
draco::StructuralMetadataSchema s1;
draco::StructuralMetadataSchema s2;
s1.json.SetString("one");
s2.json.SetString("two");
a.SetPropertyTableSchema(s1);
b.SetPropertyTableSchema(s2);
a.SetSchema(s1);
b.SetSchema(s2);
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two objects with different number of proeprty tables.
// Compare two objects with different number of property tables.
draco::StructuralMetadata a;
draco::StructuralMetadata b;
a.AddPropertyTable(std::unique_ptr<PropertyTable>(new PropertyTable()));
@ -151,7 +151,20 @@ TEST(StructuralMetadataTest, TestCompare) {
ASSERT_TRUE(a != b);
}
{
// Compare two objects with different proeprty tables.
// Compare two objects with identical property tables.
draco::StructuralMetadata a;
draco::StructuralMetadata b;
auto p1 = std::unique_ptr<PropertyTable>(new PropertyTable());
auto p2 = std::unique_ptr<PropertyTable>(new PropertyTable());
p1->SetName("one");
p2->SetName("one");
a.AddPropertyTable(std::move(p1));
b.AddPropertyTable(std::move(p2));
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two objects with different property tables.
draco::StructuralMetadata a;
draco::StructuralMetadata b;
auto p1 = std::unique_ptr<PropertyTable>(new PropertyTable());
@ -163,6 +176,32 @@ TEST(StructuralMetadataTest, TestCompare) {
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
{
// Compare two objects with identical property attributes.
draco::StructuralMetadata a;
draco::StructuralMetadata b;
auto p1 = std::unique_ptr<PropertyAttribute>(new PropertyAttribute());
auto p2 = std::unique_ptr<PropertyAttribute>(new PropertyAttribute());
p1->SetName("one");
p2->SetName("one");
a.AddPropertyAttribute(std::move(p1));
b.AddPropertyAttribute(std::move(p2));
ASSERT_TRUE(a == b);
ASSERT_FALSE(a != b);
}
{
// Compare two objects with identical property attributes.
draco::StructuralMetadata a;
draco::StructuralMetadata b;
auto p1 = std::unique_ptr<PropertyAttribute>(new PropertyAttribute());
auto p2 = std::unique_ptr<PropertyAttribute>(new PropertyAttribute());
p1->SetName("one");
p2->SetName("two");
a.AddPropertyAttribute(std::move(p1));
b.AddPropertyAttribute(std::move(p2));
ASSERT_FALSE(a == b);
ASSERT_TRUE(a != b);
}
}
#endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -37,6 +37,8 @@ void PointCloud::Copy(const PointCloud &src) {
attributes_[i] = std::unique_ptr<PointAttribute>(new PointAttribute());
attributes_[i]->CopyFrom(*src.attributes_[i]);
}
compression_enabled_ = src.compression_enabled_;
compression_options_ = src.compression_options_;
CopyMetadata(src);
}
@ -106,6 +108,20 @@ const PointAttribute *PointCloud::GetAttributeByUniqueId(
return attributes_[att_id].get();
}
#ifdef DRACO_TRANSCODER_SUPPORTED
const PointAttribute *PointCloud::GetNamedAttributeByName(
GeometryAttribute::Type type, const std::string &name) const {
const auto &index = named_attribute_index_;
for (size_t i = 0; i < index[type].size(); ++i) {
const PointAttribute *const att = attributes_[index[type][i]].get();
if (att->name() == name) {
return att;
}
}
return nullptr;
}
#endif // DRACO_TRANSCODER_SUPPORTED
int32_t PointCloud::GetAttributeIdByUniqueId(uint32_t unique_id) const {
for (size_t att_id = 0; att_id < attributes_.size(); ++att_id) {
if (attributes_[att_id]->unique_id() == unique_id) {

View File

@ -21,6 +21,10 @@
#include "draco/draco_features.h"
#include "draco/metadata/geometry_metadata.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include "draco/compression/draco_compression_options.h"
#endif
namespace draco {
// PointCloud is a collection of n-dimensional points that are described by a
@ -62,6 +66,12 @@ class PointCloud {
const PointAttribute *GetAttributeByUniqueId(uint32_t id) const;
int32_t GetAttributeIdByUniqueId(uint32_t unique_id) const;
#ifdef DRACO_TRANSCODER_SUPPORTED
// Returns the named attribute with a given name.
const PointAttribute *GetNamedAttributeByName(GeometryAttribute::Type type,
const std::string &name) const;
#endif // DRACO_TRANSCODER_SUPPORTED
int32_t num_attributes() const {
return static_cast<int32_t>(attributes_.size());
}
@ -189,6 +199,24 @@ class PointCloud {
// cloud.
void set_num_points(PointIndex::ValueType num) { num_points_ = num; }
#ifdef DRACO_TRANSCODER_SUPPORTED
// Enables or disables Draco geometry compression for this mesh.
void SetCompressionEnabled(bool enabled) { compression_enabled_ = enabled; }
bool IsCompressionEnabled() const { return compression_enabled_; }
// Sets |options| that configure Draco geometry compression. This does not
// enable or disable compression.
void SetCompressionOptions(const DracoCompressionOptions &options) {
compression_options_ = options;
}
const DracoCompressionOptions &GetCompressionOptions() const {
return compression_options_;
}
DracoCompressionOptions &GetCompressionOptions() {
return compression_options_;
}
#endif // DRACO_TRANSCODER_SUPPORTED
protected:
#ifdef DRACO_TRANSCODER_SUPPORTED
// Copies metadata from the |src| point cloud.
@ -217,6 +245,13 @@ class PointCloud {
// in corresponding PointAttribute instances in the |attributes_| array.
PointIndex::ValueType num_points_;
#ifdef DRACO_TRANSCODER_SUPPORTED
// Compression options for this geometry.
// TODO(vytyaz): Store encoded bitstream that this geometry compresses into.
bool compression_enabled_ = false;
DracoCompressionOptions compression_options_;
#endif // DRACO_TRANSCODER_SUPPORTED
friend struct PointCloudHasher;
};

View File

@ -14,6 +14,7 @@
//
#include "draco/point_cloud/point_cloud_builder.h"
#include <string>
#include <utility>
namespace draco {
@ -27,8 +28,14 @@ void PointCloudBuilder::Start(PointIndex::ValueType num_points) {
int PointCloudBuilder::AddAttribute(GeometryAttribute::Type attribute_type,
int8_t num_components, DataType data_type) {
return AddAttribute(attribute_type, num_components, data_type, false);
}
int PointCloudBuilder::AddAttribute(GeometryAttribute::Type attribute_type,
int8_t num_components, DataType data_type,
bool normalized) {
GeometryAttribute ga;
ga.Init(attribute_type, nullptr, num_components, data_type, false,
ga.Init(attribute_type, nullptr, num_components, data_type, normalized,
DataTypeLength(data_type) * num_components, 0);
return point_cloud_->AddAttribute(ga, true, point_cloud_->num_points());
}
@ -75,4 +82,14 @@ std::unique_ptr<PointCloud> PointCloudBuilder::Finalize(
return std::move(point_cloud_);
}
void PointCloudBuilder::SetAttributeUniqueId(int att_id, uint32_t unique_id) {
point_cloud_->attribute(att_id)->set_unique_id(unique_id);
}
#ifdef DRACO_TRANSCODER_SUPPORTED
void PointCloudBuilder::SetAttributeName(int att_id, const std::string &name) {
point_cloud_->attribute(att_id)->set_name(name);
}
#endif // DRACO_TRANSCODER_SUPPORTED
} // namespace draco

View File

@ -50,6 +50,8 @@ class PointCloudBuilder {
int AddAttribute(GeometryAttribute::Type attribute_type,
int8_t num_components, DataType data_type);
int AddAttribute(GeometryAttribute::Type attribute_type,
int8_t num_components, DataType data_type, bool normalized);
// Sets attribute value for a specific point.
// |attribute_value| must contain data in the format specified by the
@ -65,6 +67,14 @@ class PointCloudBuilder {
void SetAttributeValuesForAllPoints(int att_id, const void *attribute_values,
int stride);
// Sets the unique ID for an attribute created with AddAttribute().
void SetAttributeUniqueId(int att_id, uint32_t unique_id);
#ifdef DRACO_TRANSCODER_SUPPORTED
// Sets attribute name.
void SetAttributeName(int att_id, const std::string &name);
#endif // DRACO_TRANSCODER_SUPPORTED
// Finalizes the PointCloud or returns nullptr on error.
// If |deduplicate_points| is set to true, the following happens:
// 1. Attribute values with duplicate entries are deduplicated.

View File

@ -60,9 +60,15 @@ TEST_F(PointCloudBuilderTest, IndividualTest_NoDedup) {
builder.SetAttributeValueForPoint(intensity_att_id, i,
intensity_data_.data() + i.value());
}
#ifdef DRACO_TRANSCODER_SUPPORTED
builder.SetAttributeName(pos_att_id, "Bob");
#endif
std::unique_ptr<PointCloud> res = builder.Finalize(false);
ASSERT_TRUE(res != nullptr);
ASSERT_EQ(res->num_points(), 10);
#ifdef DRACO_TRANSCODER_SUPPORTED
EXPECT_EQ(res->attribute(pos_att_id)->name(), "Bob");
#endif
}
TEST_F(PointCloudBuilderTest, IndividualTest_Dedup) {
@ -168,4 +174,25 @@ TEST_F(PointCloudBuilderTest, MultiUse) {
}
}
TEST_F(PointCloudBuilderTest, PropagatesAttributeUniqueIds) {
// This test verifies that PointCloudBuilder correctly applies unique IDs to
// attributes.
PointCloudBuilder builder;
builder.Start(10);
const int pos_att_id =
builder.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32);
const int intensity_att_id =
builder.AddAttribute(GeometryAttribute::GENERIC, 1, DT_INT16);
for (PointIndex i(0); i < 10; ++i) {
builder.SetAttributeValueForPoint(pos_att_id, i,
pos_data_.data() + 3 * i.value());
builder.SetAttributeValueForPoint(intensity_att_id, i,
intensity_data_.data() + i.value());
}
builder.SetAttributeUniqueId(pos_att_id, 1234);
std::unique_ptr<PointCloud> res = builder.Finalize(false);
ASSERT_TRUE(res != nullptr);
ASSERT_EQ(res->GetAttributeByUniqueId(1234), res->attribute(pos_att_id));
}
} // namespace draco

View File

@ -77,6 +77,71 @@ TEST_F(PointCloudTest, PointCloudCopy) {
ASSERT_TRUE(att_metadata_copy->GetEntryInt("attribute_test", &att_test));
ASSERT_EQ(att_test, 3);
}
TEST_F(PointCloudTest, TestCompressionSettings) {
// Tests compression settings of a point cloud.
draco::PointCloud pc;
// Check that compression is disabled and compression settings are default.
ASSERT_FALSE(pc.IsCompressionEnabled());
const draco::DracoCompressionOptions default_compression_options;
ASSERT_EQ(pc.GetCompressionOptions(), default_compression_options);
// Check that compression options can be set without enabling compression.
draco::DracoCompressionOptions compression_options;
compression_options.quantization_bits_normal = 12;
pc.SetCompressionOptions(compression_options);
ASSERT_EQ(pc.GetCompressionOptions(), compression_options);
ASSERT_FALSE(pc.IsCompressionEnabled());
// Check that compression can be enabled.
pc.SetCompressionEnabled(true);
ASSERT_TRUE(pc.IsCompressionEnabled());
// Check that individual compression options can be updated.
pc.GetCompressionOptions().compression_level++;
pc.GetCompressionOptions().compression_level--;
// Check that compression settings can be copied.
draco::PointCloud pc_copy;
pc_copy.Copy(pc);
ASSERT_TRUE(pc_copy.IsCompressionEnabled());
ASSERT_EQ(pc_copy.GetCompressionOptions(), compression_options);
}
TEST_F(PointCloudTest, TestGetNamedAttributeByName) {
draco::PointCloud pc;
// Test whether we can get named attributes by name.
constexpr auto kPosition = draco::GeometryAttribute::POSITION;
constexpr auto kGeneric = draco::GeometryAttribute::GENERIC;
draco::GeometryAttribute pos_att;
draco::GeometryAttribute gen_att0;
draco::GeometryAttribute gen_att1;
pos_att.Init(kPosition, nullptr, 3, draco::DT_FLOAT32, false, 12, 0);
gen_att0.Init(kGeneric, nullptr, 3, draco::DT_FLOAT32, false, 12, 0);
gen_att1.Init(kGeneric, nullptr, 3, draco::DT_FLOAT32, false, 12, 0);
pos_att.set_name("Zero");
gen_att0.set_name("Zero");
gen_att1.set_name("One");
// Add one position, and two generic attributes.
pc.AddAttribute(pos_att, false, 0);
pc.AddAttribute(gen_att0, false, 0);
pc.AddAttribute(gen_att1, false, 0);
// Check added attributes.
ASSERT_EQ(pc.attribute(0)->attribute_type(), kPosition);
ASSERT_EQ(pc.attribute(1)->attribute_type(), kGeneric);
ASSERT_EQ(pc.attribute(2)->attribute_type(), kGeneric);
ASSERT_EQ(pc.attribute(0)->name(), "Zero");
ASSERT_EQ(pc.attribute(1)->name(), "Zero");
ASSERT_EQ(pc.attribute(2)->name(), "One");
// Check that we can get correct attributes by name.
ASSERT_EQ(pc.GetNamedAttributeByName(kPosition, "Zero"), pc.attribute(0));
ASSERT_EQ(pc.GetNamedAttributeByName(kGeneric, "Zero"), pc.attribute(1));
ASSERT_EQ(pc.GetNamedAttributeByName(kGeneric, "One"), pc.attribute(2));
}
#endif
TEST_F(PointCloudTest, TestAttributeDeletion) {

Some files were not shown because too many files have changed in this diff Show More