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. # the License.
cmake_minimum_required(VERSION 3.12 FATAL_ERROR) cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
if(DRACO_TRANSCODER_SUPPORTED)
set(CMAKE_CXX_STANDARD 17)
endif()
project(draco C CXX) project(draco C CXX)
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
@ -479,10 +482,14 @@ list(
"${draco_src_root}/metadata/geometry_metadata.h" "${draco_src_root}/metadata/geometry_metadata.h"
"${draco_src_root}/metadata/metadata.cc" "${draco_src_root}/metadata/metadata.cc"
"${draco_src_root}/metadata/metadata.h" "${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.cc"
"${draco_src_root}/metadata/property_table.h" "${draco_src_root}/metadata/property_table.h"
"${draco_src_root}/metadata/structural_metadata.cc" "${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 list(APPEND draco_metadata_enc_sources
"${draco_src_root}/metadata/metadata_encoder.cc" "${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 new Draco releases are launched. To avoid the issue pin your sites to a
versioned release. 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: ### Version 1.5.6 release:
* Using the versioned www.gstatic.com WASM and Javascript decoders continues * Using the versioned www.gstatic.com WASM and Javascript decoders continues
to be recommended. To use v1.5.6, use this URL: 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. # passed to libtool.
# #
# We set DRACO_SOVERSION = [c-a].a.r # We set DRACO_SOVERSION = [c-a].a.r
set(LT_CURRENT 8) set(LT_CURRENT 9)
set(LT_REVISION 0) set(LT_REVISION 0)
set(LT_AGE 0) set(LT_AGE 0)
math(EXPR DRACO_SOVERSION_MAJOR "${LT_CURRENT} - ${LT_AGE}") 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}/io/texture_io_test.cc"
"${draco_src_root}/material/material_library_test.cc" "${draco_src_root}/material/material_library_test.cc"
"${draco_src_root}/material/material_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/property_table_test.cc"
"${draco_src_root}/metadata/structural_metadata_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/instance_array_test.cc"
"${draco_src_root}/scene/light_test.cc" "${draco_src_root}/scene/light_test.cc"
"${draco_src_root}/scene/mesh_group_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); 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= 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&& 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: "+ 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(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= 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= 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(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(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(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= 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=
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= 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_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)}, 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,
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= 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_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= 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___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= 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=
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.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=
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= 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_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._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.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._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_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.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_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.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_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.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,
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= 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,
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= 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_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.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.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.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();
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+= 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,
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__= 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),
{};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= 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__=
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= 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 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= 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__=
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); 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=
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(){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=
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.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=
"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? 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;
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= 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);
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"=== 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 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, 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=
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, 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&&
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= (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);
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&& 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"===
(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; 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=
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&& 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=
(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= 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=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= 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));
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); 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"===
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 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();
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= 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=
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"=== 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&&
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= (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,
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= 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;
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"=== 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,
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); 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__=
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= 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&&
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}}(); (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=
"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(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 ```js
// (Optional) Change decoder source directory (defaults to // (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 // 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 // will benefit from having the Draco decoder in cache as more sites start using
// the static URL. // the static URL.

View File

@ -38,7 +38,7 @@
// It is recommended to always pull your Draco JavaScript and WASM decoders // 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 // from this URL. Users will benefit from having the Draco decoder in cache
// as more sites start using the static URL. // 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; var camera, cameraTarget, scene, renderer;

View File

@ -6,7 +6,7 @@
News 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. file for news about this release.
Description 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); 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= 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&& 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: "+ 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(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= 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= 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(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(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(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= 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=
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= 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_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)}, 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,
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= 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_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= 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___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= 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=
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.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=
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= 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_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._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.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._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_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.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_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.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_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.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,
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= 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,
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= 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_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.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.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.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();
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+= 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,
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__= 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),
{};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= 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__=
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= 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 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= 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__=
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); 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=
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(){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=
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.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=
"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? 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;
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= 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);
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"=== 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 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, 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=
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, 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&&
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= (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);
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&& 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"===
(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; 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=
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&& 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=
(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= 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=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= 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));
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); 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"===
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 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();
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= 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=
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"=== 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&&
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= (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,
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= 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;
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"=== 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,
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); 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__=
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= 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&&
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}}(); (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=
"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(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", "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.", "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", "main": "draco3d.js",
"scripts": { "scripts": {

View File

@ -24,7 +24,7 @@ Draco github glTF branch URL: https://github.com/google/draco/tree/gltf_2.0_drac
News 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. file for news about this release.
NPM Package 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); 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= 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&& 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: "+ 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(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= 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= 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(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(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(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= 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=
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= 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_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)}, 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,
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= 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_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= 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___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= 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=
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.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=
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= 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_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._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.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._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_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.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_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.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_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.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,
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= 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,
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= 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_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.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.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.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();
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+= 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,
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__= 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),
{};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= 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__=
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= 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 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= 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__=
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); 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=
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(){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=
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.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=
"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? 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;
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= 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);
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"=== 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 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, 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=
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, 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&&
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= (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);
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&& 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"===
(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; 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=
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&& 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=
(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= 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=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= 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));
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); 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"===
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 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();
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= 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=
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"=== 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&&
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= (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,
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= 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;
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"=== 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,
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); 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__=
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= 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&&
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}}(); (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=
"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(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", "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.", "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", "main": "draco3dgltf.js",
"scripts": { "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 "draco/attributes/attribute_quantization_transform.h"
#include <cmath>
#include <cstring>
#include <memory>
#include <vector>
#include "draco/attributes/attribute_transform_type.h" #include "draco/attributes/attribute_transform_type.h"
#include "draco/core/quantization_utils.h" #include "draco/core/quantization_utils.h"
@ -144,6 +149,9 @@ bool AttributeQuantizationTransform::ComputeParameters(
++i) { ++i) {
attribute.GetValue(i, att_val.get()); attribute.GetValue(i, att_val.get());
for (int c = 0; c < num_components; ++c) { for (int c = 0; c < num_components; ++c) {
if (std::isnan(att_val[c])) {
return false;
}
if (min_values_[c] > att_val[c]) { if (min_values_[c] > att_val[c]) {
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()); buffer_->Update(src_att.buffer_->data(), src_att.buffer_->data_size());
} }
#ifdef DRACO_TRANSCODER_SUPPORTED
name_ = src_att.name_;
#endif
return true; return true;
} }
@ -87,6 +90,11 @@ bool GeometryAttribute::operator==(const GeometryAttribute &va) const {
if (byte_offset_ != va.byte_offset_) { if (byte_offset_ != va.byte_offset_) {
return false; return false;
} }
#ifdef DRACO_TRANSCODER_SUPPORTED
if (name_ != va.name_) {
return false;
}
#endif
return true; return true;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -48,6 +48,11 @@ bool SequentialIntegerAttributeDecoder::DecodeValues(
if (!in_buffer->Decode(&prediction_scheme_method)) { if (!in_buffer->Decode(&prediction_scheme_method)) {
return false; 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) { if (prediction_scheme_method != PREDICTION_NONE) {
int8_t prediction_transform_type; int8_t prediction_transform_type;
if (!in_buffer->Decode(&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); 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_F(EncodeTest, TestDracoCompressionOptionsGridQuantizationWithOffset) {
// Test verifies that we can set position quantization via grid spacing when // Test verifies that we can set position quantization via grid spacing when
// the geometry is not perfectly aligned with the quantization grid. // 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, Status ExpertEncoder::EncodePointCloudToBuffer(const PointCloud &pc,
EncoderBuffer *out_buffer) { EncoderBuffer *out_buffer) {
#ifdef DRACO_POINT_CLOUD_COMPRESSION_SUPPORTED #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; std::unique_ptr<PointCloudEncoder> encoder;
const int encoding_method = options().GetGlobalInt("encoding_method", -1); const int encoding_method = options().GetGlobalInt("encoding_method", -1);
@ -195,11 +200,11 @@ Status ExpertEncoder::SetAttributePredictionScheme(
} }
#ifdef DRACO_TRANSCODER_SUPPORTED #ifdef DRACO_TRANSCODER_SUPPORTED
Status ExpertEncoder::ApplyCompressionOptions(const Mesh &mesh) { Status ExpertEncoder::ApplyCompressionOptions(const PointCloud &pc) {
if (!mesh.IsCompressionEnabled()) { if (!pc.IsCompressionEnabled()) {
return OkStatus(); 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 // Set any encoder options that haven't been explicitly set by users (don't
// override existing options). // override existing options).
@ -208,12 +213,12 @@ Status ExpertEncoder::ApplyCompressionOptions(const Mesh &mesh) {
10 - compression_options.compression_level); 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")) { if (options().IsAttributeOptionSet(ai, "quantization_bits")) {
continue; // Don't override options that have been set. continue; // Don't override options that have been set.
} }
int quantization_bits = 0; int quantization_bits = 0;
const auto type = mesh.attribute(ai)->attribute_type(); const auto type = pc.attribute(ai)->attribute_type();
switch (type) { switch (type) {
case GeometryAttribute::POSITION: case GeometryAttribute::POSITION:
if (compression_options.quantization_position if (compression_options.quantization_position
@ -221,7 +226,7 @@ Status ExpertEncoder::ApplyCompressionOptions(const Mesh &mesh) {
quantization_bits = quantization_bits =
compression_options.quantization_position.quantization_bits(); compression_options.quantization_position.quantization_bits();
} else { } else {
DRACO_RETURN_IF_ERROR(ApplyGridQuantization(mesh, ai)); DRACO_RETURN_IF_ERROR(ApplyGridQuantization(pc, ai));
} }
break; break;
case GeometryAttribute::TEX_COORD: case GeometryAttribute::TEX_COORD:
@ -252,17 +257,29 @@ Status ExpertEncoder::ApplyCompressionOptions(const Mesh &mesh) {
return OkStatus(); return OkStatus();
} }
Status ExpertEncoder::ApplyGridQuantization(const Mesh &mesh, Status ExpertEncoder::ApplyGridQuantization(const PointCloud &pc,
int attribute_index) { int attribute_index) {
const auto compression_options = mesh.GetCompressionOptions(); const auto compression_options = pc.GetCompressionOptions();
if (mesh.attribute(attribute_index)->num_components() != 3) { 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( return ErrorStatus(
"Invalid number of components: Grid quantization is currently " "Invalid number of components: Grid quantization is currently "
"supported only for 3D positions."); "supported only for 3D positions.");
} }
const float spacing = compression_options.quantization_position.spacing();
// Compute quantization properties based on the grid 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. // Snap min and max points of the |bbox| to the quantization grid vertices.
Vector3f min_pos; Vector3f min_pos;
int num_values = 0; // Number of values that we need to encode. 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, Status SetAttributePredictionScheme(int32_t attribute_id,
int prediction_scheme_method); 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: private:
Status EncodePointCloudToBuffer(const PointCloud &pc, Status EncodePointCloudToBuffer(const PointCloud &pc,
EncoderBuffer *out_buffer); EncoderBuffer *out_buffer);
@ -139,9 +146,9 @@ class ExpertEncoder : public EncoderBase<EncoderOptions> {
Status EncodeMeshToBuffer(const Mesh &m, EncoderBuffer *out_buffer); Status EncodeMeshToBuffer(const Mesh &m, EncoderBuffer *out_buffer);
#ifdef DRACO_TRANSCODER_SUPPORTED #ifdef DRACO_TRANSCODER_SUPPORTED
// Applies compression options stored in |mesh|. // Applies compression options stored in |pc|.
Status ApplyCompressionOptions(const Mesh &mesh); Status ApplyCompressionOptions(const PointCloud &pc);
Status ApplyGridQuantization(const Mesh &mesh, int attribute_index); Status ApplyGridQuantization(const PointCloud &pc, int attribute_index);
#endif // DRACO_TRANSCODER_SUPPORTED #endif // DRACO_TRANSCODER_SUPPORTED
const PointCloud *point_cloud_; const PointCloud *point_cloud_;

View File

@ -15,6 +15,7 @@
#include "draco/compression/mesh/mesh_edgebreaker_decoder_impl.h" #include "draco/compression/mesh/mesh_edgebreaker_decoder_impl.h"
#include <algorithm> #include <algorithm>
#include <cstdint>
#include "draco/compression/attributes/sequential_attribute_decoders_controller.h" #include "draco/compression/attributes/sequential_attribute_decoders_controller.h"
#include "draco/compression/mesh/mesh_edgebreaker_decoder.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) { if (static_cast<uint32_t>(num_encoded_vertices_) > num_faces * 3) {
return false; // There cannot be more vertices than 3 * num_faces. 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; uint8_t num_attribute_data;
if (!decoder_->buffer()->Decode(&num_attribute_data)) { if (!decoder_->buffer()->Decode(&num_attribute_data)) {
return false; return false;

View File

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

View File

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

View File

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

View File

@ -15,6 +15,9 @@
#include "draco/io/gltf_decoder.h" #include "draco/io/gltf_decoder.h"
#ifdef DRACO_TRANSCODER_SUPPORTED #ifdef DRACO_TRANSCODER_SUPPORTED
#include <array>
#include <cstdint>
#include <map>
#include <memory> #include <memory>
#include <set> #include <set>
#include <string> #include <string>
@ -27,11 +30,15 @@
#include "draco/core/hash_utils.h" #include "draco/core/hash_utils.h"
#include "draco/core/status.h" #include "draco/core/status.h"
#include "draco/core/status_or.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/io/tiny_gltf_utils.h"
#include "draco/material/material_library.h" #include "draco/material/material_library.h"
#include "draco/mesh/mesh.h" #include "draco/mesh/mesh.h"
#include "draco/mesh/mesh_features.h" #include "draco/mesh/mesh_features.h"
#include "draco/mesh/triangle_soup_mesh_builder.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/metadata/property_table.h"
#include "draco/point_cloud/point_cloud_builder.h" #include "draco/point_cloud/point_cloud_builder.h"
#include "draco/scene/scene_indices.h" #include "draco/scene/scene_indices.h"
@ -80,6 +87,10 @@ GeometryAttribute::Type GltfAttributeToDracoAttribute(
} else if (attribute_name.rfind("_FEATURE_ID_") == 0) { } else if (attribute_name.rfind("_FEATURE_ID_") == 0) {
// Feature ID attribute like _FEATURE_ID_5 from EXT_mesh_features extension. // Feature ID attribute like _FEATURE_ID_5 from EXT_mesh_features extension.
return GeometryAttribute::GENERIC; 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; return GeometryAttribute::INVALID;
} }
@ -540,6 +551,9 @@ StatusOr<std::unique_ptr<Mesh>> GltfDecoder::BuildMesh() {
DRACO_RETURN_IF_ERROR(AddAttributesToDracoMesh(&pb_)); 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 (const tinygltf::Scene &scene : gltf_model_.scenes) {
for (int i = 0; i < scene.nodes.size(); ++i) { for (int i = 0; i < scene.nodes.size(); ++i) {
const Eigen::Matrix4d parent_matrix = Eigen::Matrix4d::Identity(); const Eigen::Matrix4d parent_matrix = Eigen::Matrix4d::Identity();
@ -548,43 +562,59 @@ StatusOr<std::unique_ptr<Mesh>> GltfDecoder::BuildMesh() {
} }
DRACO_ASSIGN_OR_RETURN( DRACO_ASSIGN_OR_RETURN(
std::unique_ptr<Mesh> mesh, 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())); DRACO_RETURN_IF_ERROR(CopyTextures<Mesh>(mesh.get()));
SetAttributePropertiesOnDracoMesh(mesh.get()); SetAttributePropertiesOnDracoMesh(mesh.get());
DRACO_RETURN_IF_ERROR(AddMaterialsToDracoMesh(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())); DRACO_RETURN_IF_ERROR(AddStructuralMetadataToGeometry(mesh.get()));
MoveNonMaterialTextures(mesh.get()); MoveNonMaterialTextures(mesh.get());
DRACO_RETURN_IF_ERROR(AddAssetMetadata(mesh.get()));
return mesh; return mesh;
} }
Status GltfDecoder::AddMeshFeaturesToDracoMesh(Mesh *mesh) { Status GltfDecoder::AddPrimitiveExtensionsToDracoMesh(Mesh *mesh) {
for (const tinygltf::Scene &scene : gltf_model_.scenes) { for (const tinygltf::Scene &scene : gltf_model_.scenes) {
for (int i = 0; i < scene.nodes.size(); ++i) { 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(); 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]; const tinygltf::Node &node = gltf_model_.nodes[node_index];
if (node.mesh >= 0) { if (node.mesh >= 0) {
const tinygltf::Mesh &gltf_mesh = gltf_model_.meshes[node.mesh]; const tinygltf::Mesh &gltf_mesh = gltf_model_.meshes[node.mesh];
for (const auto &primitive : gltf_mesh.primitives) { for (const auto &primitive : gltf_mesh.primitives) {
// Decode mesh feature ID sets if present in this primitive. // Decode extensions present in this primitive.
DRACO_RETURN_IF_ERROR(DecodeMeshFeatures( DRACO_RETURN_IF_ERROR(AddPrimitiveExtensionsToDracoMesh(
primitive, &mesh->GetMaterialLibrary().MutableTextureLibrary(), primitive, &mesh->GetMaterialLibrary().MutableTextureLibrary(),
mesh)); mesh));
} }
} }
for (int i = 0; i < node.children.size(); ++i) { 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(); 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() { Status GltfDecoder::CheckUnsupportedFeatures() {
// Check for morph targets. // Check for morph targets.
for (const auto &mesh : gltf_model_.meshes) { 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; attribute_name_to_draco_mesh_attribute_id_[attribute.first] = -1;
continue; 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( DRACO_ASSIGN_OR_RETURN(
const int att_id, const int att_id,
AddAttribute(draco_att_type, attribute.second.component_type, AddAttribute(draco_att_type, attribute.second.component_type,
@ -875,6 +911,13 @@ Status GltfDecoder::AddAttributesToDracoMesh(BuilderT *builder) {
return OkStatus(); 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> template <typename BuilderT>
Status GltfDecoder::AddAttributeValuesToBuilder( Status GltfDecoder::AddAttributeValuesToBuilder(
const std::string &attribute_name, const tinygltf::Accessor &accessor, const std::string &attribute_name, const tinygltf::Accessor &accessor,
@ -903,6 +946,16 @@ Status GltfDecoder::AddAttributeValuesToBuilder(
DRACO_RETURN_IF_ERROR(AddFeatureIdToBuilder( DRACO_RETURN_IF_ERROR(AddFeatureIdToBuilder(
accessor, indices_data, att_id, number_of_elements, reverse_winding, accessor, indices_data, att_id, number_of_elements, reverse_winding,
attribute_name, builder)); 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 { } else {
DRACO_RETURN_IF_ERROR(AddAttributeDataByTypes(accessor, indices_data, DRACO_RETURN_IF_ERROR(AddAttributeDataByTypes(accessor, indices_data,
att_id, number_of_elements, att_id, number_of_elements,
@ -987,12 +1040,22 @@ Status GltfDecoder::AddFeatureIdToBuilder(
DRACO_RETURN_IF_ERROR(AddAttributeDataByTypes(accessor, indices_data, att_id, DRACO_RETURN_IF_ERROR(AddAttributeDataByTypes(accessor, indices_data, att_id,
number_of_elements, number_of_elements,
reverse_winding, builder)); reverse_winding, builder));
return OkStatus();
}
// Store feature ID attribute name with index like _FEATURE_ID_5 in Draco template <typename BuilderT>
// attribute metadata. Status GltfDecoder::AddPropertyAttributeToBuilder(
std::unique_ptr<AttributeMetadata> metadata(new draco::AttributeMetadata()); const tinygltf::Accessor &accessor,
metadata->AddEntryString("attribute_name", attribute_name); const std::vector<uint32_t> &indices_data, int att_id,
builder->AddAttributeMetadata(att_id, std::move(metadata)); 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(); return OkStatus();
} }
@ -1277,7 +1340,7 @@ Status GltfDecoder::AddMaterialsToDracoMesh(Mesh *mesh) {
bool is_normal_map_used = false; bool is_normal_map_used = false;
int default_material_index = -1; 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()) { if (it != gltf_primitive_material_to_draco_material_.end()) {
default_material_index = it->second; default_material_index = it->second;
} }
@ -1286,14 +1349,19 @@ Status GltfDecoder::AddMaterialsToDracoMesh(Mesh *mesh) {
for (int input_material_index = 0; for (int input_material_index = 0;
input_material_index < gltf_model_.materials.size(); input_material_index < gltf_model_.materials.size();
++input_material_index) { ++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) { if (default_material_index == input_material_index) {
// Insert a default material here for primitives that did not have a // Insert a default material here for primitives that did not have a
// material index. // material index.
mesh->GetMaterialLibrary().MutableMaterial(output_material_index++); mesh->GetMaterialLibrary().MutableMaterial(output_material_index);
} }
Material *const output_material = Material *const output_material =
mesh->GetMaterialLibrary().MutableMaterial(output_material_index++); mesh->GetMaterialLibrary().MutableMaterial(output_material_index);
DRACO_RETURN_IF_ERROR( DRACO_RETURN_IF_ERROR(
AddGltfMaterial(input_material_index, output_material)); AddGltfMaterial(input_material_index, output_material));
if (output_material->GetTextureMapByType( if (output_material->GetTextureMapByType(
@ -1415,6 +1483,7 @@ Status GltfDecoder::DecodeGltfToScene() {
DRACO_RETURN_IF_ERROR(AddMaterialsToScene()); DRACO_RETURN_IF_ERROR(AddMaterialsToScene());
DRACO_RETURN_IF_ERROR(AddSkinsToScene()); DRACO_RETURN_IF_ERROR(AddSkinsToScene());
MoveNonMaterialTextures(scene_.get()); MoveNonMaterialTextures(scene_.get());
DRACO_RETURN_IF_ERROR(AddAssetMetadata(scene_.get()));
return OkStatus(); return OkStatus();
} }
@ -1511,172 +1580,278 @@ Status GltfDecoder::AddStructuralMetadataToGeometry(GeometryT *geometry) {
} }
const tinygltf::Value::Object &o = e->second.Get<tinygltf::Value::Object>(); const tinygltf::Value::Object &o = e->second.Get<tinygltf::Value::Object>();
// Decode property table schema. // Decode structural metadata schema.
{ DRACO_RETURN_IF_ERROR(AddStructuralMetadataSchemaToGeometry(o, geometry));
const auto &value = o.find("schema");
if (value == o.end()) {
return ErrorStatus("Structural metadata extension has no schema.");
}
const tinygltf::Value &object = value->second;
if (!object.IsObject()) {
return ErrorStatus("Structural metadata extension schema is malformed.");
}
// Decodes tinygltf::Value into PropertyTable::Schema::Object. // Decode structural metadata property tables.
struct SchemaParser { DRACO_RETURN_IF_ERROR(AddPropertyTablesToGeometry(o, geometry));
static Status Parse(const tinygltf::Value &value,
PropertyTable::Schema::Object *object) {
switch (value.Type()) {
case tinygltf::OBJECT_TYPE: {
for (auto &it : value.Get<tinygltf::Value::Object>()) {
object->SetObjects().emplace_back(it.first);
DRACO_RETURN_IF_ERROR(
Parse(it.second, &object->SetObjects().back()));
}
} break;
case tinygltf::ARRAY_TYPE: {
for (int i = 0; i < value.ArrayLen(); ++i) {
object->SetArray().emplace_back();
DRACO_RETURN_IF_ERROR(
Parse(value.Get(i), &object->SetArray().back()));
}
} break;
case tinygltf::STRING_TYPE:
object->SetString(value.Get<std::string>());
break;
case tinygltf::INT_TYPE:
object->SetInteger(value.Get<int>());
break;
case tinygltf::BOOL_TYPE:
object->SetBoolean(value.Get<bool>());
break;
case tinygltf::REAL_TYPE:
case tinygltf::BINARY_TYPE:
case tinygltf::NULL_TYPE:
default:
// Not used in the schema JSON.
return ErrorStatus("Unsupported JSON type in schema.");
}
return OkStatus();
}
};
// Parse property table schema and set it to |geometry|. // Decode structural metadata property attributes.
PropertyTable::Schema schema; DRACO_RETURN_IF_ERROR(AddPropertyAttributesToGeometry(o, geometry));
DRACO_RETURN_IF_ERROR(SchemaParser::Parse(object, &schema.json));
geometry->GetStructuralMetadata().SetPropertyTableSchema(schema); // 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;
if (!object.IsObject()) {
return ErrorStatus("Structural metadata extension schema is malformed.");
} }
// Decode property tables. // Decodes tinygltf::Value into StructuralMetadataSchema::Object.
{ struct SchemaParser {
const auto &tables = o.find("propertyTables"); static Status Parse(const tinygltf::Value &value,
if (tables == o.end()) { StructuralMetadataSchema::Object *object) {
return ErrorStatus( switch (value.Type()) {
"Structural metadata extension has no property tables."); case tinygltf::OBJECT_TYPE: {
for (auto &it : value.Get<tinygltf::Value::Object>()) {
object->SetObjects().emplace_back(it.first);
DRACO_RETURN_IF_ERROR(
Parse(it.second, &object->SetObjects().back()));
}
} break;
case tinygltf::ARRAY_TYPE: {
for (int i = 0; i < value.ArrayLen(); ++i) {
object->SetArray().emplace_back();
DRACO_RETURN_IF_ERROR(
Parse(value.Get(i), &object->SetArray().back()));
}
} break;
case tinygltf::STRING_TYPE:
object->SetString(value.Get<std::string>());
break;
case tinygltf::INT_TYPE:
object->SetInteger(value.Get<int>());
break;
case tinygltf::BOOL_TYPE:
object->SetBoolean(value.Get<bool>());
break;
case tinygltf::REAL_TYPE:
case tinygltf::BINARY_TYPE:
case tinygltf::NULL_TYPE:
default:
// Not used in the schema JSON.
return ErrorStatus("Unsupported JSON type in schema.");
}
return OkStatus();
} }
const tinygltf::Value &tables_array = tables->second; };
if (!tables_array.IsArray()) {
return ErrorStatus("Property tables array is malformed."); // Parse schema of the structural metadata and set it to |geometry|.
StructuralMetadataSchema schema;
DRACO_RETURN_IF_ERROR(SchemaParser::Parse(object, &schema.json));
geometry->GetStructuralMetadata().SetSchema(schema);
return OkStatus();
}
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()) {
return ErrorStatus("Property tables array is malformed.");
}
// Loop over all property tables.
for (int i = 0; i < tables_array.Size(); i++) {
// Create a property table and populate it below.
std::unique_ptr<PropertyTable> property_table(new PropertyTable());
const auto &object = tables_array.Get(i);
if (!object.IsObject()) {
return ErrorStatus("Property table 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_table->SetClass(str_value);
} else {
return ErrorStatus("Property class is malformed.");
} }
// Loop over all property tables. // The "count" property is required.
for (int i = 0; i < tables_array.Size(); i++) { int int_value;
// Create a property table and populate it below. DRACO_ASSIGN_OR_RETURN(success, DecodeInt("count", o, &int_value));
std::unique_ptr<PropertyTable> property_table(new PropertyTable()); if (success) {
property_table->SetCount(int_value);
} else {
return ErrorStatus("Property count is malformed.");
}
const auto &object = tables_array.Get(i); // The "name" property is optional.
if (!object.IsObject()) { DRACO_ASSIGN_OR_RETURN(success, DecodeString("name", o, &str_value));
if (success) {
property_table->SetName(str_value);
}
// Decode property table properties (columns).
{
constexpr char kName[] = "properties";
if (!object.Has(kName)) {
return ErrorStatus("Property table is malformed."); return ErrorStatus("Property table is malformed.");
} }
const auto o = object.Get<tinygltf::Value::Object>(); const tinygltf::Value &value = object.Get(kName);
if (!value.IsObject()) {
// The "class" property is required. return ErrorStatus("Property table properties property is malformed.");
bool success;
std::string str_value;
DRACO_ASSIGN_OR_RETURN(success, DecodeString("class", o, &str_value));
if (success) {
property_table->SetClass(str_value);
} else {
return ErrorStatus("Property class is malformed.");
} }
// The "count" property is required. // Loop over property table properties.
int int_value; for (const auto &key : value.Keys()) {
DRACO_ASSIGN_OR_RETURN(success, DecodeInt("count", o, &int_value)); // Create a property table property and populate it below.
if (success) { std::unique_ptr<PropertyTable::Property> property(
property_table->SetCount(int_value); new PropertyTable::Property());
} else {
return ErrorStatus("Property count is malformed.");
}
// The "name" property is optional. const auto &property_object = value.Get(key);
DRACO_ASSIGN_OR_RETURN(success, DecodeString("name", o, &str_value)); if (!property_object.IsObject()) {
if (success) { return ErrorStatus("Property entry is malformed.");
property_table->SetName(str_value);
}
// Decode property table properties (columns).
{
constexpr char kName[] = "properties";
if (!object.Has(kName)) {
return ErrorStatus("Property table is malformed.");
} }
const tinygltf::Value &value = object.Get(kName); property->SetName(key);
if (!value.IsObject()) { const auto o = property_object.Get<tinygltf::Value::Object>();
return ErrorStatus(
"Property table properties property is malformed."); // The "values" property is required.
DRACO_ASSIGN_OR_RETURN(success, DecodePropertyTableData(
"values", o, &property->GetData()));
if (!success) {
return ErrorStatus("Property values property is malformed.");
} }
// Loop over property table properties. // All other properties are not required.
for (const auto &key : value.Keys()) { DRACO_ASSIGN_OR_RETURN(success,
// Create a property table property and populate it below. DecodeString("stringOffsetType", o, &str_value));
std::unique_ptr<PropertyTable::Property> property( if (success) {
new PropertyTable::Property()); property->GetStringOffsets().type = str_value;
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 "values" property is required.
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));
if (success) {
property->GetStringOffsets().type = 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));
DRACO_ASSIGN_OR_RETURN(
success,
DecodePropertyTableData("stringOffsets", o,
&property->GetStringOffsets().data));
// Add property to the property table.
property_table->AddProperty(std::move(property));
} }
} 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));
DRACO_ASSIGN_OR_RETURN(
success,
DecodePropertyTableData("stringOffsets", o,
&property->GetStringOffsets().data));
// Add property table to structural metadata. // Add property to the property table.
geometry->GetStructuralMetadata().AddPropertyTable( property_table->AddProperty(std::move(property));
std::move(property_table)); }
} }
// Add property table to structural metadata.
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(); return OkStatus();
} }
@ -1829,6 +2004,9 @@ Status GltfDecoder::DecodePrimitiveForScene(
pb.Start(number_of_points); 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; std::set<int32_t> normalized_attributes;
for (const auto &attribute : primitive.attributes) { for (const auto &attribute : primitive.attributes) {
if (attribute.second >= gltf_model_.accessors.size()) { if (attribute.second >= gltf_model_.accessors.size()) {
@ -1869,15 +2047,16 @@ Status GltfDecoder::DecodePrimitiveForScene(
DRACO_ASSIGN_OR_RETURN( DRACO_ASSIGN_OR_RETURN(
std::unique_ptr<Mesh> mesh, std::unique_ptr<Mesh> mesh,
BuildMeshFromBuilder(primitive.mode == TINYGLTF_MODE_TRIANGLES, &mb, BuildMeshFromBuilder(primitive.mode == TINYGLTF_MODE_TRIANGLES, &mb, &pb,
&pb)); deduplicate_vertices_));
// Set all normalized flags for appropriate attributes. // Set all normalized flags for appropriate attributes.
for (const int32_t att_id : normalized_attributes) { for (const int32_t att_id : normalized_attributes) {
mesh->attribute(att_id)->set_normalized(true); 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(), primitive, &scene_->GetMaterialLibrary().MutableTextureLibrary(),
mesh.get())); mesh.get()));
@ -1973,6 +2152,33 @@ Status GltfDecoder::DecodeMeshFeatures(const tinygltf::Primitive &primitive,
return OkStatus(); 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( Status GltfDecoder::DecodeMeshFeatures(
const tinygltf::Value::Object &extension, TextureLibrary *texture_library, const tinygltf::Value::Object &extension, TextureLibrary *texture_library,
std::vector<std::unique_ptr<MeshFeatures>> *mesh_features) { std::vector<std::unique_ptr<MeshFeatures>> *mesh_features) {
@ -2055,7 +2261,11 @@ Status GltfDecoder::DecodeMeshFeatures(
if (!value.IsInt()) { if (!value.IsInt()) {
return ErrorStatus("Attribute property is malformed."); 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."); 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. // via a temporary |material| object.
Material material(texture_library); Material material(texture_library);
const auto &container_object = object.Get<tinygltf::Value::Object>(); const auto &container_object = object.Get<tinygltf::Value::Object>();
@ -2113,6 +2323,32 @@ Status GltfDecoder::DecodeMeshFeatures(
return OkStatus(); 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> template <typename BuilderT>
StatusOr<int> GltfDecoder::AddAttribute(const std::string &attribute_name, StatusOr<int> GltfDecoder::AddAttribute(const std::string &attribute_name,
int component_type, int type, int component_type, int type,
@ -2150,9 +2386,54 @@ StatusOr<int> GltfDecoder::AddAttribute(GeometryAttribute::Type attribute_type,
if (att_id < 0) { if (att_id < 0) {
return Status(Status::DRACO_ERROR, "Could not add attribute."); 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; 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( StatusOr<bool> GltfDecoder::CheckKhrTextureTransform(
const tinygltf::ExtensionMap &extension, TextureTransform *transform) { const tinygltf::ExtensionMap &extension, TextureTransform *transform) {
bool transform_set = false; bool transform_set = false;
@ -2870,12 +3151,13 @@ size_t GltfDecoder::PrimitiveSignature::Hash::operator()(
} }
StatusOr<std::unique_ptr<Mesh>> GltfDecoder::BuildMeshFromBuilder( 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; std::unique_ptr<Mesh> mesh;
if (use_mesh_builder) { if (use_mesh_builder) {
mesh = mb->Finalize(); mesh = mb->Finalize();
} else { } else {
std::unique_ptr<PointCloud> pc = pb->Finalize(true); std::unique_ptr<PointCloud> pc = pb->Finalize(deduplicate_vertices);
if (pc) { if (pc) {
mesh.reset(new Mesh()); mesh.reset(new Mesh());
PointCloud *mesh_pc = mesh.get(); PointCloud *mesh_pc = mesh.get();
@ -2888,6 +3170,37 @@ StatusOr<std::unique_ptr<Mesh>> GltfDecoder::BuildMeshFromBuilder(
return mesh; 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 } // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED #endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -71,6 +71,18 @@ class GltfDecoder {
gltf_scene_graph_mode_ = mode; 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: private:
// Loads |file_name| into |gltf_model_|. Fills |input_files| with paths to all // Loads |file_name| into |gltf_model_|. Fills |input_files| with paths to all
// input files when non-null. // input files when non-null.
@ -192,6 +204,18 @@ class GltfDecoder {
const std::string &attribute_name, const std::string &attribute_name,
BuilderT *builder); 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. // 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 // 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 // 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, const std::vector<T> &data, bool reverse_winding,
PointCloudBuilder *builder); 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|. // Sets values in |data| into the mesh builder |mb| for |att_id|.
// |reverse_winding| if set will change the orientation of the data. // |reverse_winding| if set will change the orientation of the data.
template <typename T> template <typename T>
@ -306,28 +340,55 @@ class GltfDecoder {
const tinygltf::Value::Object &extension, const tinygltf::Value::Object &extension,
std::vector<MeshGroup::MaterialsVariantsMapping> *mappings); std::vector<MeshGroup::MaterialsVariantsMapping> *mappings);
// Decodes glTF mesh feature ID sets from all glTF primitives and adds them to // Decode extensions on all primitives of all scenes, such as mesh features
// |mesh|. // and structural metadata extensions, and add their contents to |mesh|.
Status AddMeshFeaturesToDracoMesh(Mesh *mesh); Status AddPrimitiveExtensionsToDracoMesh(Mesh *mesh);
Status AddPrimitiveExtensionsToDracoMesh(int node_index, Mesh *mesh);
// Decodes glTF mesh feature ID sets from glTF primitive in glTF node at Status AddPrimitiveExtensionsToDracoMesh(const tinygltf::Primitive &primitive,
// |node_index| and adds them to |mesh|. TextureLibrary *texture_library,
Status AddMeshFeaturesToDracoMesh(int node_index, Mesh *mesh); Mesh *mesh);
// Decodes glTF structural metadata from glTF model and adds it to |geometry|. // Decodes glTF structural metadata from glTF model and adds it to |geometry|.
template <typename GeometryT> template <typename GeometryT>
Status AddStructuralMetadataToGeometry(GeometryT *geometry); 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|. // Decodes glTF mesh feature ID sets from |primitive| and adds them to |mesh|.
Status DecodeMeshFeatures(const tinygltf::Primitive &primitive, Status DecodeMeshFeatures(const tinygltf::Primitive &primitive,
TextureLibrary *texture_library, Mesh *mesh); 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 // Decodes glTF mesh feature ID sets from |extension| and adds them to the
// |mesh_features| vector. // |mesh_features| vector.
Status DecodeMeshFeatures( Status DecodeMeshFeatures(
const tinygltf::Value::Object &extension, TextureLibrary *texture_library, const tinygltf::Value::Object &extension, TextureLibrary *texture_library,
std::vector<std::unique_ptr<MeshFeatures>> *mesh_features); 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 // Adds an attribute of type |attribute_name| to |builder|. Returns the
// attribute id. // attribute id.
template <typename BuilderT> template <typename BuilderT>
@ -427,6 +488,11 @@ class GltfDecoder {
// Adds the skins to the scene. // Adds the skins to the scene.
Status AddSkinsToScene(); 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 // All material and non-material textures (e.g., from EXT_mesh_features) are
// initially loaded into a texture library inside the the material library. // initially loaded into a texture library inside the the material library.
// These methods move |non_material_textures| from material texture 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 // point cloud builder |pb|. Mesh builder is used if |use_mesh_builder| is set
// to true. // to true.
static StatusOr<std::unique_ptr<Mesh>> BuildMeshFromBuilder( static StatusOr<std::unique_ptr<Mesh>> BuildMeshFromBuilder(
bool use_mesh_builder, TriangleSoupMeshBuilder *mb, bool use_mesh_builder, TriangleSoupMeshBuilder *mb, PointCloudBuilder *pb,
PointCloudBuilder *pb); bool deduplicate_vertices);
// Map of glTF Mesh to Draco scene mesh group. // Map of glTF Mesh to Draco scene mesh group.
std::map<int, MeshGroupIndex> gltf_mesh_to_scene_mesh_group_; std::map<int, MeshGroupIndex> gltf_mesh_to_scene_mesh_group_;
@ -457,6 +523,10 @@ class GltfDecoder {
TriangleSoupMeshBuilder mb_; TriangleSoupMeshBuilder mb_;
PointCloudBuilder pb_; 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. // Next face index used when adding attribute data to the Draco mesh.
int next_face_id_; int next_face_id_;
@ -504,6 +574,9 @@ class GltfDecoder {
// Selected mode of the decoded scene graph. // Selected mode of the decoded scene graph.
GltfSceneGraphMode gltf_scene_graph_mode_ = GltfSceneGraphMode::TREE; GltfSceneGraphMode gltf_scene_graph_mode_ = GltfSceneGraphMode::TREE;
// Whether vertices should be deduplicated after loading.
bool deduplicate_vertices_ = true;
// Functionality for deduping primitives on decode. // Functionality for deduping primitives on decode.
struct PrimitiveSignature { struct PrimitiveSignature {
const tinygltf::Primitive &primitive; const tinygltf::Primitive &primitive;

View File

@ -14,9 +14,12 @@
// //
#include "draco/io/gltf_decoder.h" #include "draco/io/gltf_decoder.h"
#include <array>
#include <cmath> #include <cmath>
#include <iostream>
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <set>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -738,17 +741,14 @@ TEST(GltfDecoderTest, TextureTransformTest) {
"KhronosSampleModels/TextureTransformTest/glTF/TextureTransformTest.gltf"; "KhronosSampleModels/TextureTransformTest/glTF/TextureTransformTest.gltf";
const std::unique_ptr<Mesh> mesh(DecodeGltfFile(filename)); const std::unique_ptr<Mesh> mesh(DecodeGltfFile(filename));
EXPECT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 9); EXPECT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 9);
for (int i = 0; i < 6; ++i) { std::set<int> expected_default_transforms = {4, 5, 6};
EXPECT_FALSE(TextureTransform::IsDefault(mesh->GetMaterialLibrary() for (int i = 0; i < 9; ++i) {
.GetMaterial(i) const bool expected_default = (expected_default_transforms.count(i) != 0);
->GetTextureMapByIndex(0) EXPECT_EQ(TextureTransform::IsDefault(mesh->GetMaterialLibrary()
->texture_transform())); .GetMaterial(i)
} ->GetTextureMapByIndex(0)
for (int i = 6; i < 9; ++i) { ->texture_transform()),
EXPECT_TRUE(TextureTransform::IsDefault(mesh->GetMaterialLibrary() expected_default);
.GetMaterial(i)
->GetTextureMapByIndex(0)
->texture_transform()));
} }
const std::unique_ptr<Scene> scene(DecodeGltfFileToScene(filename)); const std::unique_ptr<Scene> scene(DecodeGltfFileToScene(filename));
@ -1139,7 +1139,7 @@ TEST(GltfDecoderTest, CorrectVolumeThicknessFactor) {
ASSERT_NE(scene, nullptr); ASSERT_NE(scene, nullptr);
auto instances = draco::SceneUtils::ComputeAllInstances(*scene); auto instances = draco::SceneUtils::ComputeAllInstances(*scene);
ASSERT_EQ(instances.size(), 2); ASSERT_EQ(instances.size(), 2);
ASSERT_EQ(instances[MeshInstanceIndex(0)].transform.col(0).norm(), ASSERT_EQ(instances[MeshInstanceIndex(1)].transform.col(0).norm(),
kDragonScale); kDragonScale);
ASSERT_EQ(scene->GetMaterialLibrary().GetMaterial(1)->GetThicknessFactor(), ASSERT_EQ(scene->GetMaterialLibrary().GetMaterial(1)->GetThicknessFactor(),
kDragonVolumeThickness); kDragonVolumeThickness);
@ -1256,47 +1256,59 @@ TEST(GltfDecoderTest, MaterialsVariants) {
TEST(GltfDecoderTest, DecodeMeshWithMeshFeaturesWithStructuralMetadata) { TEST(GltfDecoderTest, DecodeMeshWithMeshFeaturesWithStructuralMetadata) {
// Checks decoding of a simple glTF with mesh features and structural metadata // Checks decoding of a simple glTF with mesh features and structural metadata
// property table as draco::Mesh. // property table as draco::Mesh.
constexpr bool kDracoCompressionEnabled = false;
const auto path = GetTestFileFullPath("BoxMeta/glTF/BoxMeta.gltf"); 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::GltfDecoder decoder;
DRACO_ASSIGN_OR_ASSERT(auto mesh, decoder.DecodeFromFile(path)); DRACO_ASSIGN_OR_ASSERT(auto mesh, decoder.DecodeFromFile(path));
ASSERT_NE(mesh, nullptr); ASSERT_NE(mesh, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh, kDracoCompressionEnabled); GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh, use_case);
GltfTestHelper::CheckBoxMetaStructuralMetadata(*mesh); GltfTestHelper::CheckBoxMetaStructuralMetadata(*mesh, use_case);
} }
TEST(GltfDecoderTest, DecodeMeshWithMeshFeaturesWithDracoCompression) { TEST(GltfDecoderTest, DecodeMeshWithMeshFeaturesWithDracoCompression) {
// Checks decoding of a simple glTF with mesh features compressed with Draco // Checks decoding of a simple glTF with mesh features compressed with Draco
// as draco::Mesh. // as draco::Mesh.
constexpr bool kDracoCompressionEnabled = true;
const auto path = GetTestFileFullPath("BoxMetaDraco/glTF/BoxMetaDraco.gltf"); 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::GltfDecoder decoder;
DRACO_ASSIGN_OR_ASSERT(auto mesh, decoder.DecodeFromFile(path)); DRACO_ASSIGN_OR_ASSERT(auto mesh, decoder.DecodeFromFile(path));
ASSERT_NE(mesh, nullptr); ASSERT_NE(mesh, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh, kDracoCompressionEnabled); GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh, use_case);
} }
TEST(GltfDecoderTest, DecodeSceneWithMeshFeaturesWithStructuralMetadata) { TEST(GltfDecoderTest, DecodeSceneWithMeshFeaturesWithStructuralMetadata) {
// Checks decoding of a simple glTF with mesh features and structural metadata // Checks decoding of a simple glTF with mesh features and structural metadata
// property table as draco::Scene. // property table as draco::Scene.
constexpr bool kHasDracoCompression = false;
const auto path = GetTestFileFullPath("BoxMeta/glTF/BoxMeta.gltf"); 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::GltfDecoder decoder;
DRACO_ASSIGN_OR_ASSERT(auto scene, decoder.DecodeFromFileToScene(path)); DRACO_ASSIGN_OR_ASSERT(auto scene, decoder.DecodeFromFileToScene(path));
ASSERT_NE(scene, nullptr); ASSERT_NE(scene, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*scene, kHasDracoCompression); GltfTestHelper::CheckBoxMetaMeshFeatures(*scene, use_case);
GltfTestHelper::CheckBoxMetaStructuralMetadata(*scene); GltfTestHelper::CheckBoxMetaStructuralMetadata(*scene, use_case);
} }
TEST(GltfDecoderTest, DecodeSceneWithMeshFeaturesWithDracoCompression) { TEST(GltfDecoderTest, DecodeSceneWithMeshFeaturesWithDracoCompression) {
// Checks decoding of a simple glTF with mesh features compressed with Draco // Checks decoding of a simple glTF with mesh features compressed with Draco
// as draco::Scene. // as draco::Scene.
constexpr bool kHasDracoCompression = true;
const auto path = GetTestFileFullPath("BoxMetaDraco/glTF/BoxMetaDraco.gltf"); 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::GltfDecoder decoder;
DRACO_ASSIGN_OR_ASSERT(auto scene, decoder.DecodeFromFileToScene(path)); DRACO_ASSIGN_OR_ASSERT(auto scene, decoder.DecodeFromFileToScene(path));
ASSERT_NE(scene, nullptr); ASSERT_NE(scene, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*scene, kHasDracoCompression); GltfTestHelper::CheckBoxMetaMeshFeatures(*scene, use_case);
} }
TEST(GltfDecoderTest, DecodePointCloudToMesh) { TEST(GltfDecoderTest, DecodePointCloudToMesh) {
@ -1316,6 +1328,10 @@ TEST(GltfDecoderTest, DecodePointCloudToMesh) {
ASSERT_EQ(mesh->NumNamedAttributes(draco::GeometryAttribute::TANGENT), 1); ASSERT_EQ(mesh->NumNamedAttributes(draco::GeometryAttribute::TANGENT), 1);
ASSERT_EQ(mesh->NumNamedAttributes(draco::GeometryAttribute::MATERIAL), 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. // Check the point cloud has two materials.
ASSERT_EQ(mesh->GetNamedAttribute(draco::GeometryAttribute::MATERIAL)->size(), ASSERT_EQ(mesh->GetNamedAttribute(draco::GeometryAttribute::MATERIAL)->size(),
2); 2);
@ -1398,5 +1414,52 @@ TEST(GltfDecoderTest, TestLoadUnsupportedTexCoordAttributes) {
2); 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 } // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED #endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -45,6 +45,7 @@
#include "draco/mesh/mesh_features.h" #include "draco/mesh/mesh_features.h"
#include "draco/mesh/mesh_splitter.h" #include "draco/mesh/mesh_splitter.h"
#include "draco/mesh/mesh_utils.h" #include "draco/mesh/mesh_utils.h"
#include "draco/metadata/property_attribute.h"
#include "draco/scene/instance_array.h" #include "draco/scene/instance_array.h"
#include "draco/scene/scene_indices.h" #include "draco/scene/scene_indices.h"
#include "draco/scene/scene_utils.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 // Returns a boolean indicating whether |mesh| attribute at |att_index| is a
// entry value if it begins with "_FEATURE_ID_", or an empty string otherwise. // feature ID vertex attribute referred to by any of the feature ID sets stored
std::string GetFeatureIdAttributeName(const PointAttribute &att, // in the |mesh|.
const Mesh &mesh) { bool IsFeatureIdAttribute(int att_index, const Mesh &mesh) {
const auto *const metadata = for (MeshFeaturesIndex i(0); i < mesh.NumMeshFeatures(); ++i) {
mesh.GetAttributeMetadataByAttributeId(att.unique_id()); if (mesh.GetMeshFeatures(i).GetAttributeIndex() == att_index) {
if (metadata) { return true;
std::string attribute_name; }
if (metadata->GetEntryString("attribute_name", &attribute_name)) { }
constexpr char kPrefix[] = "_FEATURE_ID_"; return false;
if (attribute_name.rfind(kPrefix) == 0) { }
return attribute_name;
// 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. // Struct to hold glTF Scene data.
@ -123,7 +148,7 @@ struct GltfNode {
root_node(false) {} root_node(false) {}
std::string name; std::string name;
std::vector<int> childern_indices; std::vector<int> children_indices;
int mesh_index; int mesh_index;
int skin_index; int skin_index;
int light_index; int light_index;
@ -220,8 +245,13 @@ struct GltfPrimitive {
int material; int material;
std::vector<MeshGroup::MaterialsVariantsMapping> material_variants_mappings; std::vector<MeshGroup::MaterialsVariantsMapping> material_variants_mappings;
std::vector<const MeshFeatures *> mesh_features; std::vector<const MeshFeatures *> mesh_features;
std::vector<int> property_attributes;
std::map<std::string, int> attributes; std::map<std::string, int> attributes;
GltfDracoCompressedMesh compressed_mesh_info; 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 { struct GltfMesh {
@ -249,6 +279,8 @@ class GltfAsset {
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 generator() const { return generator_; }
std::string version() const { return version_; } std::string version() const { return version_; }
std::string buffer_name() const { return buffer_name_; } std::string buffer_name() const { return buffer_name_; }
@ -351,7 +383,8 @@ class GltfAsset {
int AddDracoJoints(const Mesh &mesh, int num_encoded_points); int AddDracoJoints(const Mesh &mesh, int num_encoded_points);
int AddDracoWeights(const Mesh &mesh, int num_encoded_points); int AddDracoWeights(const Mesh &mesh, int num_encoded_points);
std::vector<std::pair<std::string, int>> AddDracoGenerics( 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 // 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 // to the asset. Returns true if |mesh| does not contain any materials or all
@ -515,9 +548,26 @@ class GltfAsset {
gltf_json_.EndArray(); gltf_json_.EndArray();
} }
// Add a mesh Draco attribute |att| that is comprised of floats to the glTF // Add a mesh Draco attribute |att| to the glTF data. Returns the index
// data. Returns the index accessor added to the glTF data. Returns -1 on // accessor added to the glTF data. Returns -1 on error.
// 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> template <class att_data_t>
int AddAttribute(const PointAttribute &att, int num_points, int AddAttribute(const PointAttribute &att, int num_points,
int num_encoded_points, bool compress) { int num_encoded_points, bool compress) {
@ -554,6 +604,10 @@ class GltfAsset {
int num_encoded_points, const std::string &type, int num_encoded_points, const std::string &type,
bool compress); bool compress);
void SetCopyrightFromScene(const Scene &scene);
void SetCopyrightFromMesh(const Mesh &mesh);
std::string copyright_;
std::string generator_; std::string generator_;
std::string version_; std::string version_;
std::vector<GltfScene> scenes_; std::vector<GltfScene> scenes_;
@ -609,8 +663,7 @@ class GltfAsset {
std::vector<std::unique_ptr<Light>> lights_; std::vector<std::unique_ptr<Light>> lights_;
std::vector<std::string> materials_variants_names_; std::vector<std::string> materials_variants_names_;
std::vector<EncoderInstanceArray> instance_arrays_; std::vector<EncoderInstanceArray> instance_arrays_;
PropertyTable::Schema property_table_schema_; const StructuralMetadata *structural_metadata_;
std::vector<const PropertyTable *> property_tables_;
// Indicates whether Draco compression is used for any of the asset meshes. // Indicates whether Draco compression is used for any of the asset meshes.
bool draco_compression_used_; bool draco_compression_used_;
@ -618,6 +671,9 @@ class GltfAsset {
// Indicates whether mesh features are used. // Indicates whether mesh features are used.
bool mesh_features_used_; bool mesh_features_used_;
// Indicates whether structural metadata is used.
bool structural_metadata_used_;
// Counter for naming mesh feature textures. // Counter for naming mesh feature textures.
int mesh_features_texture_index_; int mesh_features_texture_index_;
@ -667,8 +723,10 @@ GltfAsset::GltfAsset()
version_("2.0"), version_("2.0"),
scene_index_(-1), scene_index_(-1),
buffer_name_("buffer0.bin"), buffer_name_("buffer0.bin"),
structural_metadata_(nullptr),
draco_compression_used_(false), draco_compression_used_(false),
mesh_features_used_(false), mesh_features_used_(false),
structural_metadata_used_(false),
mesh_features_texture_index_(0), mesh_features_texture_index_(0),
add_images_to_buffer_(false), add_images_to_buffer_(false),
output_type_(GltfEncoder::COMPACT) {} output_type_(GltfEncoder::COMPACT) {}
@ -686,6 +744,9 @@ bool GltfAsset::AddDracoMesh(const Mesh &mesh) {
meshes_.push_back(gltf_mesh); meshes_.push_back(gltf_mesh);
AddStructuralMetadata(mesh); AddStructuralMetadata(mesh);
if (copyright_.empty()) {
SetCopyrightFromMesh(mesh);
}
const int32_t material_att_id = const int32_t material_att_id =
mesh.GetNamedAttributeId(GeometryAttribute::MATERIAL); mesh.GetNamedAttributeId(GeometryAttribute::MATERIAL);
@ -713,6 +774,10 @@ bool GltfAsset::AddDracoMesh(const Mesh &mesh) {
// Copy over mesh features for a given material index. // Copy over mesh features for a given material index.
Mesh::CopyMeshFeaturesForMaterial(mesh, split_meshes[i].get(), mat_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 // 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 // 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 // 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. // Create Draco encoder.
EncoderBuffer buffer; EncoderBuffer buffer;
ExpertEncoder encoder(*mesh_copy); std::unique_ptr<ExpertEncoder> encoder;
encoder.SetTrackEncodedProperties(true); 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). // Convert compression level to speed (that 0 = slowest, 10 = fastest).
const int speed = 10 - compression_options.compression_level; const int speed = 10 - compression_options.compression_level;
encoder.SetSpeedOptions(speed, speed); encoder->SetSpeedOptions(speed, speed);
// Configure attribute quantization. // Configure attribute quantization.
for (int i = 0; i < mesh_copy->num_attributes(); ++i) { 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; num_quantization_bits = compression_options.quantization_bits_weight;
break; break;
case GeometryAttribute::GENERIC: case GeometryAttribute::GENERIC:
if (GetFeatureIdAttributeName(*att, *mesh_copy).empty()) { if (!IsFeatureIdAttribute(i, *mesh_copy)) {
num_quantization_bits = num_quantization_bits =
compression_options.quantization_bits_generic; compression_options.quantization_bits_generic;
} else { } else {
// Quantization is explicitly disabled for feature ID attributes. // Quantization is explicitly disabled for feature ID attributes.
encoder.SetAttributeQuantization(i, -1); encoder->SetAttributeQuantization(i, -1);
} }
break; break;
default: default:
break; break;
} }
if (num_quantization_bits > 0) { 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 // |compression_options| may have been modified and we need to update them
// before we start the encoding. // before we start the encoding.
mesh_copy->SetCompressionOptions(compression_options); mesh_copy->SetCompressionOptions(compression_options);
DRACO_RETURN_IF_ERROR(encoder.EncodeToBuffer(&buffer)); DRACO_RETURN_IF_ERROR(encoder->EncodeToBuffer(&buffer));
*num_encoded_points = encoder.num_encoded_points(); *num_encoded_points = encoder->num_encoded_points();
*num_encoded_faces = encoder.num_encoded_faces(); 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(); const size_t buffer_start_offset = buffer_.size();
if (!buffer_.Encode(buffer.data(), buffer.size())) { if (!buffer_.Encode(buffer.data(), buffer.size())) {
return Status(Status::DRACO_ERROR, "Could not copy Draco compressed data."); 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 joints_accessor_index = AddDracoJoints(mesh, num_encoded_points);
const int weights_accessor_index = AddDracoWeights(mesh, num_encoded_points); const int weights_accessor_index = AddDracoWeights(mesh, num_encoded_points);
const std::vector<std::pair<std::string, int>> generics_accessors = 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) { if (num_encoded_faces == 0) {
primitive.mode = 0; // POINTS mode. primitive.mode = 0; // POINTS mode.
@ -1067,6 +1144,10 @@ bool GltfAsset::AddDracoMesh(
for (MeshFeaturesIndex i(0); i < mesh.NumMeshFeatures(); ++i) { for (MeshFeaturesIndex i(0); i < mesh.NumMeshFeatures(); ++i) {
primitive.mesh_features.push_back(&mesh.GetMeshFeatures(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.indices = indices_index;
primitive.attributes.insert( primitive.attributes.insert(
std::pair<std::string, int>("POSITION", position_index)); std::pair<std::string, int>("POSITION", position_index));
@ -1312,62 +1393,68 @@ int GltfAsset::AddDracoWeights(const Mesh &mesh, int num_encoded_points) {
mesh.IsCompressionEnabled()); mesh.IsCompressionEnabled());
} }
// Adds generic attributes that have metadata describing the attribute name. // Adds generic attributes that have metadata describing the attribute name,
// This allows for export of application-specific attributes and feature ID // attributes referred to by one of the mesh feature ID sets or in the |mesh|,
// attributes defined in glTF extension EXT_mesh_features. Returns a vector of // and attributes referred to by one of the property attributes in the |mesh|.
// attribute-name, accessor pairs for each valid attribute. The length of the // This allows for export of application-specific attributes, feature ID
// vector is equal to the number of generic attributes. Vector entries // attributes defined in glTF extension EXT_mesh_features, and property
// corresponding to unsupported attributes (e.g., with no metadata) contain // attributes defined in glTF extension EXT_structural_metadata. Returns a
// empty attribute names. // 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( std::vector<std::pair<std::string, int>> GltfAsset::AddDracoGenerics(
const Mesh &mesh, int num_encoded_points) { const Mesh &mesh, int num_encoded_points,
const int num_attributes = std::unordered_map<int, int> *feature_id_name_indices) {
const int num_generic_attributes =
mesh.NumNamedAttributes(GeometryAttribute::GENERIC); mesh.NumNamedAttributes(GeometryAttribute::GENERIC);
std::vector<std::pair<std::string, int>> attrs(num_attributes); std::vector<std::pair<std::string, int>> attrs;
for (int i = 0; i < num_attributes; ++i) { int feature_id_count = 0;
const PointAttribute *const att = for (int i = 0; i < num_generic_attributes; ++i) {
mesh.GetNamedAttribute(GeometryAttribute::GENERIC, i); const int att_index =
auto const *metadata = mesh.GetNamedAttributeId(GeometryAttribute::GENERIC, i);
mesh.GetAttributeMetadataByAttributeId(att->unique_id()); 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) {
std::string attr_name;
if (metadata->GetEntryString(GltfEncoder::kDracoMetadataGltfAttributeName, if (metadata->GetEntryString(GltfEncoder::kDracoMetadataGltfAttributeName,
&attr_name)) { &attr_name)) {
if (att->data_type() == DT_FLOAT32) { if (att->data_type() == DT_FLOAT32) {
int accessor = accessor =
AddAttribute<float>(*att, mesh.num_points(), num_encoded_points, AddAttribute<float>(*att, mesh.num_points(), num_encoded_points,
mesh.IsCompressionEnabled()); 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,
mesh.IsCompressionEnabled());
break;
case DT_UINT16:
accessor = AddAttribute<uint16_t>(*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};
} }
} }
} else {
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());
// 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());
attr_name = att->name();
}
}
if (accessor != -1 && !attr_name.empty()) {
attrs.emplace_back(attr_name, accessor);
} }
} }
return attrs; return attrs;
@ -1432,13 +1519,7 @@ StatusOr<int> GltfAsset::AddImage(const std::string &image_stem,
image.texture = texture; image.texture = texture;
image.owned_texture = std::move(owned_texture); image.owned_texture = std::move(owned_texture);
image.num_components = num_components; image.num_components = num_components;
image.mime_type = TextureUtils::GetTargetMimeType(*texture);
// 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;
}
// For KTX2 with Basis compression, state that its extension is required. // For KTX2 with Basis compression, state that its extension is required.
if (extension == "ktx2") { if (extension == "ktx2") {
@ -1511,6 +1592,8 @@ Status GltfAsset::AddScene(const Scene &scene) {
if (!AddMaterials(scene)) { if (!AddMaterials(scene)) {
return Status(Status::DRACO_ERROR, "Error adding materials to the 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 // Initialize base mesh transforms that may be needed when the base meshes are
// compressed with Draco. // compressed with Draco.
base_mesh_transforms_ = SceneUtils::FindLargestBaseMeshTransforms(scene); 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(AddLights(scene));
DRACO_RETURN_IF_ERROR(AddMaterialsVariantsNames(scene)); DRACO_RETURN_IF_ERROR(AddMaterialsVariantsNames(scene));
DRACO_RETURN_IF_ERROR(AddInstanceArrays(scene)); DRACO_RETURN_IF_ERROR(AddInstanceArrays(scene));
AddStructuralMetadata(scene); if (copyright_.empty()) {
SetCopyrightFromScene(scene);
}
return OkStatus(); return OkStatus();
} }
@ -1542,7 +1627,7 @@ Status GltfAsset::AddSceneNode(const Scene &scene,
node.trs_matrix.Copy(scene_node->GetTrsMatrix()); node.trs_matrix.Copy(scene_node->GetTrsMatrix());
for (int i = 0; i < scene_node->NumChildren(); ++i) { 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(); 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) { for (MeshFeaturesIndex j(0); j < mesh.NumMeshFeatures(); ++j) {
primitive.mesh_features.push_back(&mesh.GetMeshFeatures(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); meshes_.back().primitives.push_back(primitive);
} }
} }
@ -1884,14 +1975,7 @@ Status GltfAsset::AddInstanceArrays(const Scene &scene) {
template <typename GeometryT> template <typename GeometryT>
void GltfAsset::AddStructuralMetadata(const GeometryT &geometry) { void GltfAsset::AddStructuralMetadata(const GeometryT &geometry) {
const StructuralMetadata &structural_metadata = structural_metadata_ = &geometry.GetStructuralMetadata();
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));
}
}
} }
StatusOr<int> GltfAsset::AddData(const std::vector<float> &data, 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_.BeginObject("asset");
gltf_json_.OutputValue("version", version_); gltf_json_.OutputValue("version", version_);
gltf_json_.OutputValue("generator", generator_); gltf_json_.OutputValue("generator", generator_);
if (!copyright_.empty()) {
gltf_json_.OutputValue("copyright", copyright_);
}
gltf_json_.EndObject(); gltf_json_.EndObject();
const std::string asset_str = gltf_json_.MoveData(); const std::string asset_str = gltf_json_.MoveData();
@ -2044,10 +2131,10 @@ bool GltfAsset::EncodeNodesProperty(EncoderBuffer *buf_out) {
gltf_json_.EndObject(); gltf_json_.EndObject();
} }
if (!nodes_[i].childern_indices.empty()) { if (!nodes_[i].children_indices.empty()) {
gltf_json_.BeginArray("children"); gltf_json_.BeginArray("children");
for (int j = 0; j < nodes_[i].childern_indices.size(); ++j) { for (int j = 0; j < nodes_[i].children_indices.size(); ++j) {
gltf_json_.OutputValue(nodes_[i].childern_indices[j]); gltf_json_.OutputValue(nodes_[i].children_indices[j]);
} }
gltf_json_.EndArray(); gltf_json_.EndArray();
} }
@ -2170,9 +2257,10 @@ Status GltfAsset::EncodePrimitiveExtensionsProperty(
primitive.compressed_mesh_info.buffer_view_index >= 0; primitive.compressed_mesh_info.buffer_view_index >= 0;
const bool has_materials_variants = const bool has_materials_variants =
!primitive.material_variants_mappings.empty(); !primitive.material_variants_mappings.empty();
const bool has_structural_metadata = !primitive.property_attributes.empty();
const bool has_mesh_features = !primitive.mesh_features.empty(); const bool has_mesh_features = !primitive.mesh_features.empty();
if (!has_draco_mesh_compression && !has_materials_variants && if (!has_draco_mesh_compression && !has_materials_variants &&
!has_mesh_features) { !has_mesh_features && !has_structural_metadata) {
return OkStatus(); return OkStatus();
} }
@ -2216,7 +2304,10 @@ Status GltfAsset::EncodePrimitiveExtensionsProperty(
} }
gltf_json_.OutputValue("featureCount", features->GetFeatureCount()); gltf_json_.OutputValue("featureCount", features->GetFeatureCount());
if (features->GetAttributeIndex() != -1) { 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) { if (features->GetPropertyTableIndex() != -1) {
gltf_json_.OutputValue("propertyTable", gltf_json_.OutputValue("propertyTable",
@ -2250,6 +2341,16 @@ Status GltfAsset::EncodePrimitiveExtensionsProperty(
gltf_json_.EndArray(); // featureIds array. gltf_json_.EndArray(); // featureIds array.
gltf_json_.EndObject(); // EXT_mesh_features entry. 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. gltf_json_.EndObject(); // extensions entry.
return OkStatus(); return OkStatus();
} }
@ -2963,7 +3064,8 @@ Status GltfAsset::EncodeSkinsProperty(EncoderBuffer *buf_out) {
Status GltfAsset::EncodeTopLevelExtensionsProperty(EncoderBuffer *buf_out) { Status GltfAsset::EncodeTopLevelExtensionsProperty(EncoderBuffer *buf_out) {
// Return if there are no top-level asset extensions to encode. // Return if there are no top-level asset extensions to encode.
if (lights_.empty() && materials_variants_names_.empty() && if (lights_.empty() && materials_variants_names_.empty() &&
property_tables_.empty()) { structural_metadata_->NumPropertyTables() == 0 &&
structural_metadata_->NumPropertyAttributes() == 0) {
return OkStatus(); return OkStatus();
} }
@ -3048,38 +3150,39 @@ Status GltfAsset::EncodeMaterialsVariantsNamesProperty(EncoderBuffer *buf_out) {
} }
Status GltfAsset::EncodeStructuralMetadataProperty(EncoderBuffer *buf_out) { Status GltfAsset::EncodeStructuralMetadataProperty(EncoderBuffer *buf_out) {
if (property_table_schema_.Empty()) { if (structural_metadata_->GetSchema().Empty()) {
return OkStatus(); return OkStatus();
} }
structural_metadata_used_ = true;
gltf_json_.BeginObject("EXT_structural_metadata"); gltf_json_.BeginObject("EXT_structural_metadata");
// Encodes property table schema. // Encodes structural metadata schema.
struct SchemaWriter { struct SchemaWriter {
static void Write(const PropertyTable::Schema::Object &object, typedef StructuralMetadataSchema::Object Object;
JsonWriter *json_writer) { static void Write(const Object &object, JsonWriter *json_writer) {
switch (object.GetType()) { switch (object.GetType()) {
case PropertyTable::Schema::Object::OBJECT: case Object::OBJECT:
json_writer->BeginObject(object.GetName()); json_writer->BeginObject(object.GetName());
for (const PropertyTable::Schema::Object &obj : object.GetObjects()) { for (const Object &obj : object.GetObjects()) {
Write(obj, json_writer); Write(obj, json_writer);
} }
json_writer->EndObject(); json_writer->EndObject();
break; break;
case PropertyTable::Schema::Object::ARRAY: case Object::ARRAY:
json_writer->BeginArray(object.GetName()); json_writer->BeginArray(object.GetName());
for (const PropertyTable::Schema::Object &obj : object.GetArray()) { for (const Object &obj : object.GetArray()) {
Write(obj, json_writer); Write(obj, json_writer);
} }
json_writer->EndArray(); json_writer->EndArray();
break; break;
case PropertyTable::Schema::Object::STRING: case Object::STRING:
json_writer->OutputValue(object.GetName(), object.GetString()); json_writer->OutputValue(object.GetName(), object.GetString());
break; break;
case PropertyTable::Schema::Object::INTEGER: case Object::INTEGER:
json_writer->OutputValue(object.GetName(), object.GetInteger()); json_writer->OutputValue(object.GetName(), object.GetInteger());
break; break;
case PropertyTable::Schema::Object::BOOLEAN: case Object::BOOLEAN:
json_writer->OutputValue(object.GetName(), object.GetBoolean()); json_writer->OutputValue(object.GetName(), object.GetBoolean());
break; break;
} }
@ -3087,11 +3190,13 @@ Status GltfAsset::EncodeStructuralMetadataProperty(EncoderBuffer *buf_out) {
}; };
// Encode property table schema. // Encode property table schema.
SchemaWriter::Write(property_table_schema_.json, &gltf_json_); SchemaWriter::Write(structural_metadata_->GetSchema().json, &gltf_json_);
// Encode all property tables. // Encode all property tables.
gltf_json_.BeginArray("propertyTables"); 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(); gltf_json_.BeginObject();
if (!table->GetName().empty()) { if (!table->GetName().empty()) {
gltf_json_.OutputValue("name", table->GetName()); gltf_json_.OutputValue("name", table->GetName());
@ -3138,7 +3243,33 @@ Status GltfAsset::EncodeStructuralMetadataProperty(EncoderBuffer *buf_out) {
gltf_json_.EndObject(); // properties entry. gltf_json_.EndObject(); // properties entry.
gltf_json_.EndObject(); gltf_json_.EndObject();
} }
gltf_json_.EndArray(); // propertyTables entry. 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. gltf_json_.EndObject(); // EXT_structural_metadata entry.
return OkStatus(); return OkStatus();
} }
@ -3246,7 +3377,7 @@ Status GltfAsset::EncodeExtensionsProperties(EncoderBuffer *buf_out) {
if (mesh_features_used_) { if (mesh_features_used_) {
extensions_used_.insert("EXT_mesh_features"); extensions_used_.insert("EXT_mesh_features");
} }
if (!property_table_schema_.Empty()) { if (structural_metadata_used_) {
extensions_used_.insert("EXT_structural_metadata"); 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); 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[] = const char GltfEncoder::kDracoMetadataGltfAttributeName[] =
"//GLTF/ApplicationSpecificAttributeName"; "//GLTF/ApplicationSpecificAttributeName";
@ -3436,6 +3581,7 @@ Status GltfEncoder::EncodeFile(const T &geometry, const std::string &filename,
} }
GltfAsset gltf_asset; GltfAsset gltf_asset;
gltf_asset.set_copyright(copyright_);
gltf_asset.set_output_type(output_type_); gltf_asset.set_output_type(output_type_);
if (extension == "gltf") { if (extension == "gltf") {
@ -3465,6 +3611,7 @@ Status GltfEncoder::EncodeToBuffer(const T &geometry,
gltf_asset.set_output_type(output_type_); gltf_asset.set_output_type(output_type_);
gltf_asset.buffer_name(""); gltf_asset.buffer_name("");
gltf_asset.set_add_images_to_buffer(true); gltf_asset.set_add_images_to_buffer(true);
gltf_asset.set_copyright(copyright_);
// Encode the geometry into a buffer. // Encode the geometry into a buffer.
EncoderBuffer buffer; EncoderBuffer buffer;

View File

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

View File

@ -15,10 +15,15 @@
#include "draco/io/gltf_encoder.h" #include "draco/io/gltf_encoder.h"
#ifdef DRACO_TRANSCODER_SUPPORTED #ifdef DRACO_TRANSCODER_SUPPORTED
#include <array>
#include <iostream>
#include <map>
#include <memory>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <unordered_set> #include <unordered_set>
#include <utility> #include <utility>
#include <vector>
#include "draco/core/draco_test_base.h" #include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.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"); const std::string tmp_name = draco::GetTestTempFileFullPath("tmp.gltf");
DRACO_ASSERT_OK(encoder.EncodeFile<Scene>(*original, tmp_name)); DRACO_ASSERT_OK(encoder.EncodeFile<Scene>(*original, tmp_name));
// Read model from the temporay file. // Read model from the temporary file.
GltfDecoder decoder; GltfDecoder decoder;
DRACO_ASSIGN_OR_ASSERT(auto encoded, decoder.DecodeFromFileToScene(tmp_name)); DRACO_ASSIGN_OR_ASSERT(auto encoded, decoder.DecodeFromFileToScene(tmp_name));
ASSERT_NE(encoded, nullptr); ASSERT_NE(encoded, nullptr);
@ -1483,6 +1488,44 @@ TEST_F(GltfEncoderTest, EncodeToBuffer) {
ASSERT_EQ(std::memcmp(file_data.data(), buffer.data(), buffer.size()), 0); 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. // Tests that a scene with lights can be encoded into a file.
TEST_F(GltfEncoderTest, EncodeLights) { TEST_F(GltfEncoderTest, EncodeLights) {
const std::string file_name = "sphere_lights.gltf"; const std::string file_name = "sphere_lights.gltf";
@ -1565,9 +1608,9 @@ TEST_F(GltfEncoderTest, EncodeMaterialsVariants) {
// structural metadata property table. // structural metadata property table.
TEST_F(GltfEncoderTest, EncodeSceneWithMeshFeaturesWithStructuralMetadata) { TEST_F(GltfEncoderTest, EncodeSceneWithMeshFeaturesWithStructuralMetadata) {
const std::string file_name = "BoxMeta/glTF/BoxMeta.gltf"; const std::string file_name = "BoxMeta/glTF/BoxMeta.gltf";
constexpr bool kHasMeshFeatures = true; GltfTestHelper::UseCase use_case;
constexpr bool kHasStructuralMetadata = true; use_case.has_mesh_features = true;
constexpr bool kHasDracoCompression = false; use_case.has_structural_metadata = true;
// Read test file from file. // Read test file from file.
const std::unique_ptr<Scene> scene(DecodeTestGltfFileToScene(file_name)); const std::unique_ptr<Scene> scene(DecodeTestGltfFileToScene(file_name));
@ -1577,18 +1620,17 @@ TEST_F(GltfEncoderTest, EncodeSceneWithMeshFeaturesWithStructuralMetadata) {
std::unique_ptr<Scene> scene_from_gltf; std::unique_ptr<Scene> scene_from_gltf;
SceneToDecodedGltfScene(*scene, &scene_from_gltf); SceneToDecodedGltfScene(*scene, &scene_from_gltf);
ASSERT_NE(scene_from_gltf, nullptr); ASSERT_NE(scene_from_gltf, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*scene_from_gltf, GltfTestHelper::CheckBoxMetaMeshFeatures(*scene_from_gltf, use_case);
kHasDracoCompression); GltfTestHelper::CheckBoxMetaStructuralMetadata(*scene_from_gltf, use_case);
GltfTestHelper::CheckBoxMetaStructuralMetadata(*scene_from_gltf);
} }
// Tests encoding of draco::Scene with Draco compression to glTF with various // Tests encoding of draco::Scene with Draco compression to glTF with various
// mesh feature ID sets. // mesh feature ID sets.
TEST_F(GltfEncoderTest, EncodeSceneWithMeshFeaturesWithDracoCompression) { TEST_F(GltfEncoderTest, EncodeSceneWithMeshFeaturesWithDracoCompression) {
const std::string file_name = "BoxMetaDraco/glTF/BoxMetaDraco.gltf"; const std::string file_name = "BoxMetaDraco/glTF/BoxMetaDraco.gltf";
constexpr bool kHasMeshFeatures = true; GltfTestHelper::UseCase use_case;
constexpr bool kHasStructuralMetadata = false; use_case.has_draco_compression = true;
constexpr bool kHasDracoCompression = true; use_case.has_mesh_features = true;
// Read test file from file. // Read test file from file.
const std::unique_ptr<Scene> scene(DecodeTestGltfFileToScene(file_name)); const std::unique_ptr<Scene> scene(DecodeTestGltfFileToScene(file_name));
@ -1598,15 +1640,16 @@ TEST_F(GltfEncoderTest, EncodeSceneWithMeshFeaturesWithDracoCompression) {
std::unique_ptr<Scene> scene_from_gltf; std::unique_ptr<Scene> scene_from_gltf;
SceneToDecodedGltfScene(*scene, &scene_from_gltf); SceneToDecodedGltfScene(*scene, &scene_from_gltf);
ASSERT_NE(scene_from_gltf, nullptr); ASSERT_NE(scene_from_gltf, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*scene_from_gltf, GltfTestHelper::CheckBoxMetaMeshFeatures(*scene_from_gltf, use_case);
kHasDracoCompression);
} }
// Tests encoding of draco::Mesh to glTF with various mesh feature ID sets and // Tests encoding of draco::Mesh to glTF with various mesh feature ID sets and
// structural metadata property table. // structural metadata property table.
TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithStructuralMetadata) { TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithStructuralMetadata) {
const std::string file_name = "BoxMeta/glTF/BoxMeta.gltf"; 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. // Read test file from file.
const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name)); const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name));
@ -1616,16 +1659,17 @@ TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithStructuralMetadata) {
std::unique_ptr<Mesh> mesh_from_gltf; std::unique_ptr<Mesh> mesh_from_gltf;
MeshToDecodedGltfMesh(*mesh, &mesh_from_gltf); MeshToDecodedGltfMesh(*mesh, &mesh_from_gltf);
ASSERT_NE(mesh_from_gltf, nullptr); ASSERT_NE(mesh_from_gltf, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh_from_gltf, GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh_from_gltf, use_case);
kHasDracoCompression); GltfTestHelper::CheckBoxMetaStructuralMetadata(*mesh_from_gltf, use_case);
GltfTestHelper::CheckBoxMetaStructuralMetadata(*mesh_from_gltf);
} }
// Tests encoding of draco::Mesh with Draco compression to glTF with various // Tests encoding of draco::Mesh with Draco compression to glTF with various
// mesh feature ID sets. // mesh feature ID sets.
TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithDracoCompression) { TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithDracoCompression) {
constexpr bool kHasDracoCompression = true;
const std::string file_name = "BoxMetaDraco/glTF/BoxMetaDraco.gltf"; 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. // Read test file from file.
const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name)); const std::unique_ptr<Mesh> mesh(ReadMeshFromTestFile(file_name));
@ -1635,8 +1679,37 @@ TEST_F(GltfEncoderTest, EncodeMeshWithMeshFeaturesWithDracoCompression) {
std::unique_ptr<Mesh> mesh_from_gltf; std::unique_ptr<Mesh> mesh_from_gltf;
MeshToDecodedGltfMesh(*mesh, &mesh_from_gltf); MeshToDecodedGltfMesh(*mesh, &mesh_from_gltf);
ASSERT_NE(mesh_from_gltf, nullptr); ASSERT_NE(mesh_from_gltf, nullptr);
GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh_from_gltf, GltfTestHelper::CheckBoxMetaMeshFeatures(*mesh_from_gltf, use_case);
kHasDracoCompression); }
// 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 // 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); 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. // Tests encoding of draco::Mesh containing a point cloud and two materials.
TEST_F(GltfEncoderTest, EncodePointCloudWithMaterials) { TEST_F(GltfEncoderTest, EncodePointCloudWithMaterials) {
const std::string file_name = const std::string file_name =

View File

@ -20,8 +20,10 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "draco/attributes/geometry_attribute.h"
#include "draco/core/draco_test_base.h" #include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h" #include "draco/core/draco_test_utils.h"
#include "draco/metadata/property_attribute.h"
#include "draco/metadata/property_table.h" #include "draco/metadata/property_table.h"
#include "draco/texture/texture_library.h" #include "draco/texture/texture_library.h"
@ -59,9 +61,6 @@ void GltfTestHelper::AddBoxMetaMeshFeatures(Scene *scene) {
pa->SetAttributeValue(avi, &val); pa->SetAttributeValue(avi, &val);
} }
const int att_id = mesh.AddPerFaceAttribute(std::move(pa)); 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. // Add feature ID set to the mesh.
std::unique_ptr<MeshFeatures> features(new MeshFeatures()); std::unique_ptr<MeshFeatures> features(new MeshFeatures());
@ -69,7 +68,7 @@ void GltfTestHelper::AddBoxMetaMeshFeatures(Scene *scene) {
features->SetFeatureCount(num_faces); features->SetFeatureCount(num_faces);
features->SetNullFeatureId(100); features->SetNullFeatureId(100);
features->SetPropertyTableIndex(0); features->SetPropertyTableIndex(0);
features->SetAttributeIndex(0); features->SetAttributeIndex(att_id);
mesh.AddMeshFeatures(std::move(features)); mesh.AddMeshFeatures(std::move(features));
} }
@ -84,9 +83,6 @@ void GltfTestHelper::AddBoxMetaMeshFeatures(Scene *scene) {
pa->SetAttributeValue(avi, &val); pa->SetAttributeValue(avi, &val);
} }
const int att_id = mesh.AddPerVertexAttribute(std::move(pa)); 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. // Add feature ID set to the mesh.
std::unique_ptr<MeshFeatures> features(new MeshFeatures()); std::unique_ptr<MeshFeatures> features(new MeshFeatures());
@ -94,7 +90,7 @@ void GltfTestHelper::AddBoxMetaMeshFeatures(Scene *scene) {
features->SetFeatureCount(num_vertices); features->SetFeatureCount(num_vertices);
features->SetNullFeatureId(101); features->SetNullFeatureId(101);
features->SetPropertyTableIndex(1); features->SetPropertyTableIndex(1);
features->SetAttributeIndex(1); features->SetAttributeIndex(att_id);
mesh.AddMeshFeatures(std::move(features)); mesh.AddMeshFeatures(std::move(features));
} }
@ -113,14 +109,11 @@ void GltfTestHelper::AddBoxMetaMeshFeatures(Scene *scene) {
} }
const int att_id = const int att_id =
mesh.AddAttributeWithConnectivity(std::move(pa), corner_to_value); 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. // Add feature ID set to the mesh.
std::unique_ptr<MeshFeatures> features(new MeshFeatures()); std::unique_ptr<MeshFeatures> features(new MeshFeatures());
features->SetFeatureCount(num_corners); features->SetFeatureCount(num_corners);
features->SetAttributeIndex(2); features->SetAttributeIndex(att_id);
mesh.AddMeshFeatures(std::move(features)); mesh.AddMeshFeatures(std::move(features));
} }
@ -163,7 +156,7 @@ void GltfTestHelper::AddBoxMetaMeshFeatures(Scene *scene) {
} }
void GltfTestHelper::AddBoxMetaStructuralMetadata(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": { // "schema": {
// "id": "galaxy", // "id": "galaxy",
// "classes": { // "classes": {
@ -181,11 +174,30 @@ void GltfTestHelper::AddBoxMetaStructuralMetadata(Scene *scene) {
// "type": "STRING" // "type": "STRING"
// } // }
// "sequence": { // "sequence": {
// "componentType": "FLOAT32",
// "description": "The number sequence.", // "description": "The number sequence.",
// "required": false, // "required": false,
// "type": "SCALAR" // "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": { // "enums": {
@ -201,35 +213,76 @@ void GltfTestHelper::AddBoxMetaStructuralMetadata(Scene *scene) {
// ] // ]
// } // }
// } // }
// } // },
typedef PropertyTable::Schema::Object Object; // "propertyAttributes": [{
PropertyTable::Schema schema; // "name": "The movement.",
// "class": "movement",
// "properties": {
// "direction": {
// "attribute": "_DIRECTION",
// },
// "magnitude": {
// "attribute": "_MAGNITUDE",
// }
// }
// }]
typedef StructuralMetadataSchema::Object Object;
StructuralMetadataSchema schema;
Object &json = schema.json; Object &json = schema.json;
json.SetObjects().emplace_back("id", "galaxy"); json.SetObjects().emplace_back("id", "galaxy");
json.SetObjects().emplace_back("classes"); json.SetObjects().emplace_back("classes");
json.SetObjects().back().SetObjects().emplace_back("planet");
Object &planet = json.SetObjects().back().SetObjects().back();
planet.SetObjects().emplace_back("properties");
Object &properties = planet.SetObjects().back();
properties.SetObjects().emplace_back("color"); // Add class "planet" to schema.
Object &color = properties.SetObjects().back(); {
color.SetObjects().emplace_back("componentType", "UINT8"); json.SetObjects().back().SetObjects().emplace_back("planet");
color.SetObjects().emplace_back("description", "The RGB color."); Object &planet = json.SetObjects().back().SetObjects().back();
color.SetObjects().emplace_back("required", true); planet.SetObjects().emplace_back("properties");
color.SetObjects().emplace_back("type", "VEC3"); Object &properties = planet.SetObjects().back();
properties.SetObjects().emplace_back("name"); properties.SetObjects().emplace_back("color");
Object &name = properties.SetObjects().back(); Object &color = properties.SetObjects().back();
name.SetObjects().emplace_back("description", "The name."); color.SetObjects().emplace_back("componentType", "UINT8");
name.SetObjects().emplace_back("required", true); color.SetObjects().emplace_back("description", "The RGB color.");
name.SetObjects().emplace_back("type", "STRING"); color.SetObjects().emplace_back("required", true);
color.SetObjects().emplace_back("type", "VEC3");
properties.SetObjects().emplace_back("sequence"); properties.SetObjects().emplace_back("name");
Object &sequence = properties.SetObjects().back(); Object &name = properties.SetObjects().back();
sequence.SetObjects().emplace_back("description", "The number sequence."); name.SetObjects().emplace_back("description", "The name.");
sequence.SetObjects().emplace_back("required", false); name.SetObjects().emplace_back("required", true);
sequence.SetObjects().emplace_back("type", "SCALAR"); name.SetObjects().emplace_back("type", "STRING");
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().emplace_back("enums");
json.SetObjects().back().SetObjects().emplace_back("classifications"); 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("name", "Ordnance");
values.SetArray().back().SetObjects().emplace_back("value", 4); values.SetArray().back().SetObjects().emplace_back("value", 4);
// Add property table schema to the scene. // Add structural metadata schema to the scene.
scene->GetStructuralMetadata().SetPropertyTableSchema(schema); scene->GetStructuralMetadata().SetSchema(schema);
// Add structural metadata property table. // Add structural metadata property table.
std::unique_ptr<PropertyTable> table(new PropertyTable()); std::unique_ptr<PropertyTable> table(new PropertyTable());
@ -334,9 +387,9 @@ void GltfTestHelper::AddBoxMetaStructuralMetadata(Scene *scene) {
212, 0, 0, 0, // Mustafar 212, 0, 0, 0, // Mustafar
232, 0, 0, 0, // Bespin 232, 0, 0, 0, // Bespin
250, 0, 0, 0, // Yavin 250, 0, 0, 0, // Yavin
12, 1, 0, 0, // Geonosis 11, 1, 0, 0, // Geonosis
32, 1, 0, 0, // UNLABELED 31, 1, 0, 0, // UNLABELED
41, 1, 0, 0}; 40, 1, 0, 0};
table->AddProperty(std::move(property)); table->AddProperty(std::move(property));
} }
@ -391,36 +444,95 @@ void GltfTestHelper::AddBoxMetaStructuralMetadata(Scene *scene) {
// Add property table to the scene. // Add property table to the scene.
scene->GetStructuralMetadata().AddPropertyTable(std::move(table)); 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 <> template <>
void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &geometry, void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &geometry,
bool has_draco_compression) { const UseCase &use_case) {
CheckBoxMetaMeshFeatures(geometry, geometry.GetNonMaterialTextureLibrary(), CheckBoxMetaMeshFeatures(geometry, geometry.GetNonMaterialTextureLibrary(),
has_draco_compression); use_case);
} }
template <> template <>
void GltfTestHelper::CheckBoxMetaMeshFeatures(const Scene &geometry, void GltfTestHelper::CheckBoxMetaMeshFeatures(const Scene &geometry,
bool has_draco_compression) { const UseCase &use_case) {
ASSERT_EQ(geometry.NumMeshes(), 1); ASSERT_EQ(geometry.NumMeshes(), 1);
CheckBoxMetaMeshFeatures(geometry.GetMesh(MeshIndex(0)), CheckBoxMetaMeshFeatures(geometry.GetMesh(MeshIndex(0)),
geometry.GetNonMaterialTextureLibrary(), geometry.GetNonMaterialTextureLibrary(), use_case);
has_draco_compression);
} }
void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh, void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh,
const TextureLibrary &texture_lib, const TextureLibrary &texture_lib,
bool has_draco_compression) { const UseCase &use_case) {
// Check texture library. // Check texture library.
ASSERT_EQ(texture_lib.NumTextures(), 2); ASSERT_EQ(texture_lib.NumTextures(), 2);
// Check basic mesh properties. // Check basic mesh properties.
ASSERT_EQ(mesh.NumMeshFeatures(), 5); ASSERT_EQ(mesh.NumMeshFeatures(), 5);
ASSERT_EQ(mesh.num_faces(), 12); 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.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); ASSERT_EQ(mesh.NumNamedAttributes(GeometryAttribute::TEX_COORD), 2);
// Get mesh element counts. // Get mesh element counts.
@ -437,15 +549,15 @@ void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh,
ASSERT_EQ(features.GetFeatureCount(), num_faces); ASSERT_EQ(features.GetFeatureCount(), num_faces);
ASSERT_EQ(features.GetNullFeatureId(), 100); ASSERT_EQ(features.GetNullFeatureId(), 100);
ASSERT_EQ(features.GetPropertyTableIndex(), 0); 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_TRUE(features.GetTextureChannels().empty());
ASSERT_EQ(features.GetTextureMap().texture(), nullptr); ASSERT_EQ(features.GetTextureMap().texture(), nullptr);
ASSERT_EQ(features.GetTextureMap().tex_coord_index(), -1); ASSERT_EQ(features.GetTextureMap().tex_coord_index(), -1);
// Check per-face Uint8 attribute named _FEATURE_ID_0. // Check per-face Uint8 attribute named _FEATURE_ID_0.
const int att_id = const int att_id = features.GetAttributeIndex();
mesh.GetAttributeIdByMetadataEntry("attribute_name", "_FEATURE_ID_0"); const auto att = mesh.attribute(att_id);
auto att = mesh.GetAttributeByUniqueId(att_id);
ASSERT_NE(att, nullptr); ASSERT_NE(att, nullptr);
ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC); ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC);
ASSERT_EQ(att->data_type(), DataType::DT_UINT8); 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. // Check that the values are all the numbers from 0 to 12.
const std::vector<uint8_t> expected_values = 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>{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}; : std::vector<uint8_t>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
for (int i = 0; i < num_faces; i++) { 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.GetFeatureCount(), num_vertices);
ASSERT_EQ(features.GetNullFeatureId(), 101); ASSERT_EQ(features.GetNullFeatureId(), 101);
ASSERT_EQ(features.GetPropertyTableIndex(), 1); 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_TRUE(features.GetTextureChannels().empty());
ASSERT_EQ(features.GetTextureMap().texture(), nullptr); ASSERT_EQ(features.GetTextureMap().texture(), nullptr);
ASSERT_EQ(features.GetTextureMap().tex_coord_index(), -1); ASSERT_EQ(features.GetTextureMap().tex_coord_index(), -1);
// Check per-vertex Uint16 attribute named _FEATURE_ID_1. // Check per-vertex Uint16 attribute named _FEATURE_ID_1.
const int att_id = const int att_id = features.GetAttributeIndex();
mesh.GetAttributeIdByMetadataEntry("attribute_name", "_FEATURE_ID_1"); const auto att = mesh.attribute(att_id);
auto att = mesh.GetAttributeByUniqueId(att_id);
ASSERT_NE(att, nullptr); ASSERT_NE(att, nullptr);
ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC); ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC);
ASSERT_EQ(att->data_type(), DataType::DT_UINT16); ASSERT_EQ(att->data_type(), DataType::DT_UINT16);
@ -500,8 +612,9 @@ void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh,
// Check that the values are all the numbers from 0 to 7. // Check that the values are all the numbers from 0 to 7.
const std::vector<uint16_t> expected_values = 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>{0, 1, 2, 3, 4, 5, 6, 7}; ? 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++) { for (int i = 0; i < num_vertices; i++) {
uint16_t val; uint16_t val;
att->GetValue(AttributeValueIndex(i), &val); att->GetValue(AttributeValueIndex(i), &val);
@ -528,15 +641,15 @@ void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh,
ASSERT_EQ(features.GetFeatureCount(), num_corners); ASSERT_EQ(features.GetFeatureCount(), num_corners);
ASSERT_EQ(features.GetNullFeatureId(), -1); ASSERT_EQ(features.GetNullFeatureId(), -1);
ASSERT_EQ(features.GetPropertyTableIndex(), -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_TRUE(features.GetTextureChannels().empty());
ASSERT_EQ(features.GetTextureMap().texture(), nullptr); ASSERT_EQ(features.GetTextureMap().texture(), nullptr);
ASSERT_EQ(features.GetTextureMap().tex_coord_index(), -1); ASSERT_EQ(features.GetTextureMap().tex_coord_index(), -1);
// Check per-corner Float attribute named _FEATURE_ID_2. // Check per-corner Float attribute named _FEATURE_ID_2.
const int att_id = const int att_id = features.GetAttributeIndex();
mesh.GetAttributeIdByMetadataEntry("attribute_name", "_FEATURE_ID_2"); const auto att = mesh.attribute(att_id);
auto att = mesh.GetAttributeByUniqueId(att_id);
ASSERT_NE(att, nullptr); ASSERT_NE(att, nullptr);
ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC); ASSERT_EQ(att->attribute_type(), GeometryAttribute::GENERIC);
ASSERT_EQ(att->data_type(), DataType::DT_FLOAT32); 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. // Check that the values are from 0 to 35.
const std::vector<float> expected_values = 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, ? 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, 7, 8, 6, 15, 16, 17, 14, 12, 13, 5, 3, 4,
19, 20, 18, 27, 28, 29, 26, 24, 25, 1, 2, 0} 19, 20, 18, 27, 28, 29, 26, 24, 25, 1, 2, 0}
@ -598,55 +711,104 @@ void GltfTestHelper::CheckBoxMetaMeshFeatures(const Mesh &mesh,
} }
void GltfTestHelper::CheckBoxMetaStructuralMetadata( void GltfTestHelper::CheckBoxMetaStructuralMetadata(
const StructuralMetadata &structural_metadata) { const Mesh &mesh, const StructuralMetadata &structural_metadata,
// Check property table schema. const UseCase &use_case) {
// Check structural metadata schema.
{ {
const PropertyTable::Schema &schema = const StructuralMetadataSchema &schema = structural_metadata.GetSchema();
structural_metadata.GetPropertyTableSchema();
ASSERT_FALSE(schema.Empty()); 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().size(), 3);
ASSERT_EQ(json.GetObjects()[0].GetName(), "classes"); ASSERT_EQ(json.GetObjects()[0].GetName(), "classes");
ASSERT_EQ(json.GetObjects()[0].GetObjects().size(), 1); ASSERT_EQ(json.GetObjects()[0].GetObjects().size(), 2);
ASSERT_EQ(json.GetObjects()[0].GetObjects()[0].GetName(), "planet");
ASSERT_EQ(json.GetObjects()[0].GetObjects()[0].GetObjects().size(), 1);
const auto &properties = // Check class "movement".
json.GetObjects()[0].GetObjects()[0].GetObjects()[0]; {
ASSERT_EQ(properties.GetName(), "properties"); const auto item = json.GetObjects()[0].GetObjects()[0];
ASSERT_EQ(properties.GetObjects().size(), 3); ASSERT_EQ(item.GetName(), "movement");
ASSERT_EQ(item.GetObjects().size(), 3);
const auto &color = properties.GetObjects()[0]; const auto &description = item.GetObjects()[0];
ASSERT_EQ(color.GetName(), "color"); ASSERT_EQ(description.GetName(), "description");
ASSERT_EQ(color.GetObjects().size(), 4); ASSERT_EQ(description.GetString(), "Vertex movement.");
ASSERT_EQ(color.GetObjects()[0].GetName(), "componentType");
ASSERT_EQ(color.GetObjects()[1].GetName(), "description");
ASSERT_EQ(color.GetObjects()[2].GetName(), "required");
ASSERT_EQ(color.GetObjects()[3].GetName(), "type");
ASSERT_EQ(color.GetObjects()[0].GetString(), "UINT8");
ASSERT_EQ(color.GetObjects()[1].GetString(), "The RGB color.");
ASSERT_TRUE(color.GetObjects()[2].GetBoolean());
ASSERT_EQ(color.GetObjects()[3].GetString(), "VEC3");
const auto &name = properties.GetObjects()[1]; const auto &name = item.GetObjects()[1];
ASSERT_EQ(name.GetName(), "name"); ASSERT_EQ(name.GetName(), "name");
ASSERT_EQ(name.GetObjects().size(), 3); ASSERT_EQ(name.GetString(), "The movement.");
ASSERT_EQ(name.GetObjects()[0].GetName(), "description");
ASSERT_EQ(name.GetObjects()[1].GetName(), "required");
ASSERT_EQ(name.GetObjects()[2].GetName(), "type");
ASSERT_EQ(name.GetObjects()[0].GetString(), "The name.");
ASSERT_TRUE(name.GetObjects()[1].GetBoolean());
ASSERT_EQ(name.GetObjects()[2].GetString(), "STRING");
const auto &sequence = properties.GetObjects()[2]; const auto &properties = item.GetObjects()[2];
ASSERT_EQ(sequence.GetName(), "sequence"); ASSERT_EQ(properties.GetName(), "properties");
ASSERT_EQ(sequence.GetObjects().size(), 3); ASSERT_EQ(properties.GetObjects().size(), 2);
ASSERT_EQ(sequence.GetObjects()[0].GetName(), "description");
ASSERT_EQ(sequence.GetObjects()[1].GetName(), "required"); const auto &direction = properties.GetObjects()[0];
ASSERT_EQ(sequence.GetObjects()[2].GetName(), "type"); ASSERT_EQ(direction.GetName(), "direction");
ASSERT_EQ(sequence.GetObjects()[0].GetString(), "The number sequence."); ASSERT_EQ(direction.GetObjects().size(), 4);
ASSERT_FALSE(sequence.GetObjects()[1].GetBoolean()); ASSERT_EQ(direction.GetObjects()[0].GetName(), "componentType");
ASSERT_EQ(sequence.GetObjects()[2].GetString(), "SCALAR"); 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);
const auto &color = properties.GetObjects()[0];
ASSERT_EQ(color.GetName(), "color");
ASSERT_EQ(color.GetObjects().size(), 4);
ASSERT_EQ(color.GetObjects()[0].GetName(), "componentType");
ASSERT_EQ(color.GetObjects()[1].GetName(), "description");
ASSERT_EQ(color.GetObjects()[2].GetName(), "required");
ASSERT_EQ(color.GetObjects()[3].GetName(), "type");
ASSERT_EQ(color.GetObjects()[0].GetString(), "UINT8");
ASSERT_EQ(color.GetObjects()[1].GetString(), "The RGB color.");
ASSERT_TRUE(color.GetObjects()[2].GetBoolean());
ASSERT_EQ(color.GetObjects()[3].GetString(), "VEC3");
const auto &name = properties.GetObjects()[1];
ASSERT_EQ(name.GetName(), "name");
ASSERT_EQ(name.GetObjects().size(), 3);
ASSERT_EQ(name.GetObjects()[0].GetName(), "description");
ASSERT_EQ(name.GetObjects()[1].GetName(), "required");
ASSERT_EQ(name.GetObjects()[2].GetName(), "type");
ASSERT_EQ(name.GetObjects()[0].GetString(), "The name.");
ASSERT_TRUE(name.GetObjects()[1].GetBoolean());
ASSERT_EQ(name.GetObjects()[2].GetString(), "STRING");
const auto &sequence = properties.GetObjects()[2];
ASSERT_EQ(sequence.GetName(), "sequence");
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"); ASSERT_EQ(json.GetObjects()[1].GetName(), "enums");
const auto &classifications = json.GetObjects()[1].GetObjects()[0]; const auto &classifications = json.GetObjects()[1].GetObjects()[0];
@ -736,11 +898,11 @@ void GltfTestHelper::CheckBoxMetaStructuralMetadata(
ASSERT_EQ(offsets[1], 0); ASSERT_EQ(offsets[1], 0);
ASSERT_EQ(offsets[2], 0); ASSERT_EQ(offsets[2], 0);
ASSERT_EQ(offsets[3], 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[61], 1);
ASSERT_EQ(offsets[62], 0); ASSERT_EQ(offsets[62], 0);
ASSERT_EQ(offsets[63], 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[65], 1);
ASSERT_EQ(offsets[66], 0); ASSERT_EQ(offsets[66], 0);
ASSERT_EQ(offsets[67], 0); ASSERT_EQ(offsets[67], 0);
@ -748,8 +910,8 @@ void GltfTestHelper::CheckBoxMetaStructuralMetadata(
struct Name { struct Name {
static std::string Extract(const std::vector<uint8_t> &data, static std::string Extract(const std::vector<uint8_t> &data,
const std::vector<uint8_t> &offsets, int row) { const std::vector<uint8_t> &offsets, int row) {
const int b = offsets[4 * (row + 0)] + 255 * offsets[4 * (row + 0) + 1]; const int b = offsets[4 * (row + 0)] + 256 * offsets[4 * (row + 0) + 1];
const int e = offsets[4 * (row + 1)] + 255 * offsets[4 * (row + 1) + 1]; const int e = offsets[4 * (row + 1)] + 256 * offsets[4 * (row + 1) + 1];
return std::string(data.begin() + b, data.begin() + e); 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. // 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, 0), "named_class:Tatooine");
ASSERT_EQ(Name::Extract(data, offsets, 6), "named_class:Corellia"); 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_EQ(Name::Extract(data, offsets, 15), "UNLABELED");
ASSERT_TRUE(property.GetArrayOffsets().type.empty()); ASSERT_TRUE(property.GetArrayOffsets().type.empty());
@ -816,6 +981,101 @@ void GltfTestHelper::CheckBoxMetaStructuralMetadata(
ASSERT_TRUE(property.GetStringOffsets().data.data.empty()); ASSERT_TRUE(property.GetStringOffsets().data.data.empty());
ASSERT_EQ(property.GetStringOffsets().data.target, 0); 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 #endif // DRACO_TRANSCODER_SUPPORTED

View File

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

View File

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

View File

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

View File

@ -216,6 +216,13 @@ TEST_F(GltfUtilsTest, TestArrays) {
json_writer.EndArray(); json_writer.EndArray();
json_writer.EndArray(); json_writer.EndArray();
CompareGolden(&json_writer, "\"array1\": [\n \"array2\": [\n ]\n]"); 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) { TEST_F(GltfUtilsTest, TestGltfValues) {
@ -359,6 +366,13 @@ TEST_F(GltfUtilsTest, TestArraysCompact) {
json_writer.EndArray(); json_writer.EndArray();
json_writer.EndArray(); json_writer.EndArray();
CompareGolden(&json_writer, "\"array1\":[\"array2\":[]]"); 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 } // namespace draco

View File

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

View File

@ -541,7 +541,7 @@ bool ObjDecoder::ParseMaterial(Status * /* status */) {
parser::SkipWhitespace(&line_buffer); parser::SkipWhitespace(&line_buffer);
std::string mat_name; std::string mat_name;
parser::ParseLine(&line_buffer, &mat_name); parser::ParseLine(&line_buffer, &mat_name);
if (mat_name.length() == 0) { if (mat_name.empty()) {
return false; return false;
} }
auto it = material_name_to_id_.find(mat_name); 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)) { if (!parser::ParseString(&line_buffer, &obj_name)) {
return false; return false;
} }
if (obj_name.length() == 0) { if (obj_name.empty()) {
return true; // Ignore empty name entries. return true; // Ignore empty name entries.
} }
auto it = obj_name_to_id_.find(obj_name); 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) { Status PlyDecoder::DecodeFaceData(const PlyElement *face_element) {
// We accept point clouds now. // We accept point clouds now.
if (face_element == nullptr) { if (face_element == nullptr) {
return Status(Status::INVALID_PARAMETER, "face_element is null"); return OkStatus();
} }
const PlyProperty *vertex_indices = const PlyProperty *vertex_indices =
face_element->GetPropertyByName("vertex_indices"); face_element->GetPropertyByName("vertex_indices");

View File

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

View File

@ -16,7 +16,11 @@
#ifdef DRACO_TRANSCODER_SUPPORTED #ifdef DRACO_TRANSCODER_SUPPORTED
#include <algorithm>
#include <cstring>
#include "draco/io/file_utils.h" #include "draco/io/file_utils.h"
#include "draco/texture/texture_utils.h"
namespace draco { namespace draco {
@ -25,12 +29,74 @@ namespace {
StatusOr<std::unique_ptr<Texture>> CreateDracoTextureInternal( StatusOr<std::unique_ptr<Texture>> CreateDracoTextureInternal(
const std::vector<uint8_t> &image_data, SourceImage *out_source_image) { const std::vector<uint8_t> &image_data, SourceImage *out_source_image) {
std::unique_ptr<Texture> draco_texture(new Texture()); 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->MutableEncodedData() = image_data;
out_source_image->set_mime_type(TextureUtils::GetMimeType(format));
return std::move(draco_texture); return std::move(draco_texture);
} }
} // namespace } // 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( StatusOr<std::unique_ptr<Texture>> ReadTextureFromFile(
const std::string &file_name) { const std::string &file_name) {
std::vector<uint8_t> image_data; std::vector<uint8_t> image_data;
@ -42,23 +108,31 @@ StatusOr<std::unique_ptr<Texture>> ReadTextureFromFile(
DRACO_ASSIGN_OR_RETURN(auto texture, DRACO_ASSIGN_OR_RETURN(auto texture,
CreateDracoTextureInternal(image_data, &source_image)); CreateDracoTextureInternal(image_data, &source_image));
source_image.set_filename(file_name); source_image.set_filename(file_name);
const std::string extension = LowercaseFileExtension(file_name); if (source_image.mime_type().empty()) {
const std::string mime_type = // Try to set mime type from extension if we were not able to detect it
"image/" + (extension == "jpg" ? "jpeg" : extension); // automatically.
source_image.set_mime_type(mime_type); 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); texture->set_source_image(source_image);
return texture; return texture;
} }
StatusOr<std::unique_ptr<Texture>> ReadTextureFromBuffer( StatusOr<std::unique_ptr<Texture>> ReadTextureFromBuffer(
const uint8_t *buffer, size_t buffer_size, const std::string &mime_type) { const uint8_t *buffer, size_t buffer_size, const std::string &mime_type) {
SourceImage source_image; return ReadTextureFromBuffer(buffer, buffer_size);
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;
} }
Status WriteTextureToFile(const std::string &file_name, Status WriteTextureToFile(const std::string &file_name,

View File

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

View File

@ -15,6 +15,7 @@
#include "draco/io/texture_io.h" #include "draco/io/texture_io.h"
#ifdef DRACO_TRANSCODER_SUPPORTED #ifdef DRACO_TRANSCODER_SUPPORTED
#include <cstdint>
#include <memory> #include <memory>
#include <string> #include <string>
#include <unordered_map> #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 } // namespace
#endif // DRACO_TRANSCODER_SUPPORTED #endif // DRACO_TRANSCODER_SUPPORTED

View File

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

View File

@ -179,6 +179,19 @@ bool PointCloudBuilder::SetMetadataForAttribute(PointCloud *pc,
return true; 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() {} MeshBuilder::MeshBuilder() {}
bool MeshBuilder::AddFacesToMesh(Mesh *mesh, long num_faces, const int *faces) { 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, bool SetMetadataForAttribute(draco::PointCloud *pc, long attribute_id,
const draco::Metadata *metadata); const draco::Metadata *metadata);
bool AddMetadata(draco::PointCloud *pc, const draco::Metadata *metadata); bool AddMetadata(draco::PointCloud *pc, const draco::Metadata *metadata);
bool SetNormalizedFlagForAttribute(draco::PointCloud *pc, long attribute_id,
bool normalized);
private: private:
template <typename DataTypeT> template <typename DataTypeT>

View File

@ -26,11 +26,7 @@ namespace draco {
template <bool B, class T, class F> template <bool B, class T, class F>
using conditional_t = typename std::conditional<B, T, F>::type; using conditional_t = typename std::conditional<B, T, F>::type;
#ifdef DRACO_TRANSCODER_SUPPORTED
Mesh::Mesh() : compression_enabled_(false) {}
#else
Mesh::Mesh() {} Mesh::Mesh() {}
#endif
#ifdef DRACO_TRANSCODER_SUPPORTED #ifdef DRACO_TRANSCODER_SUPPORTED
void Mesh::Copy(const Mesh &src) { void Mesh::Copy(const Mesh &src) {
@ -39,8 +35,6 @@ void Mesh::Copy(const Mesh &src) {
faces_ = src.faces_; faces_ = src.faces_;
attribute_data_ = src.attribute_data_; attribute_data_ = src.attribute_data_;
material_library_.Copy(src.material_library_); material_library_.Copy(src.material_library_);
compression_enabled_ = src.compression_enabled_;
compression_options_ = src.compression_options_;
// Copy mesh feature ID sets. // Copy mesh feature ID sets.
mesh_features_.clear(); mesh_features_.clear();
@ -67,6 +61,8 @@ void Mesh::Copy(const Mesh &src) {
// Copy structural metadata. // Copy structural metadata.
structural_metadata_.Copy(src.structural_metadata_); structural_metadata_.Copy(src.structural_metadata_);
property_attributes_ = src.property_attributes_;
property_attributes_material_mask_ = src.property_attributes_material_mask_;
} }
namespace { 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) { if (num_used_materials == num_materials) {
return; // All materials are used, don't do anything. 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( 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) { int32_t Mesh::AddPerFaceAttribute(std::unique_ptr<PointAttribute> att) {
IndexTypeVector<CornerIndex, AttributeValueIndex> corner_map(num_faces() * 3); IndexTypeVector<CornerIndex, AttributeValueIndex> corner_map(num_faces() * 3);
for (CornerIndex ci(0); ci < num_faces() * 3; ++ci) { for (CornerIndex ci(0); ci < num_faces() * 3; ++ci) {

View File

@ -24,7 +24,6 @@
#include "draco/core/status.h" #include "draco/core/status.h"
#include "draco/draco_features.h" #include "draco/draco_features.h"
#ifdef DRACO_TRANSCODER_SUPPORTED #ifdef DRACO_TRANSCODER_SUPPORTED
#include "draco/compression/draco_compression_options.h"
#include "draco/material/material_library.h" #include "draco/material/material_library.h"
#include "draco/mesh/mesh_features.h" #include "draco/mesh/mesh_features.h"
#include "draco/mesh/mesh_indices.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())) { if (att_id >= 0 && att_id < static_cast<int>(attribute_data_.size())) {
attribute_data_.erase(attribute_data_.begin() + att_id); attribute_data_.erase(attribute_data_.begin() + att_id);
} }
#ifdef DRACO_TRANSCODER_SUPPORTED
UpdateMeshFeaturesAfterDeletedAttribute(att_id);
#endif
} }
#ifdef DRACO_TRANSCODER_SUPPORTED #ifdef DRACO_TRANSCODER_SUPPORTED
@ -170,22 +172,6 @@ class Mesh : public PointCloud {
void RemoveUnusedMaterials(); void RemoveUnusedMaterials();
void RemoveUnusedMaterials(bool remove_unused_material_indices); 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. // Library that contains non-material textures.
const TextureLibrary &GetNonMaterialTextureLibrary() const { const TextureLibrary &GetNonMaterialTextureLibrary() const {
return non_material_texture_library_; return non_material_texture_library_;
@ -208,12 +194,20 @@ class Mesh : public PointCloud {
MeshFeatures &GetMeshFeatures(MeshFeaturesIndex index) { MeshFeatures &GetMeshFeatures(MeshFeaturesIndex index) {
return *mesh_features_[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) { void RemoveMeshFeatures(MeshFeaturesIndex index) {
mesh_features_.erase(mesh_features_.begin() + index.value()); mesh_features_.erase(mesh_features_.begin() + index.value());
mesh_features_material_mask_.erase(mesh_features_material_mask_.begin() + mesh_features_material_mask_.erase(mesh_features_material_mask_.begin() +
index.value()); 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 // Restricts given mesh features to faces mapped to a material with
// |material_index|. Note that single mesh features can be restricted to // |material_index|. Note that single mesh features can be restricted to
// multiple materials. // multiple materials.
@ -249,6 +243,49 @@ class Mesh : public PointCloud {
return structural_metadata_; return structural_metadata_;
} }
StructuralMetadata &GetStructuralMetadata() { 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 #endif // DRACO_TRANSCODER_SUPPORTED
protected: protected:
@ -266,6 +303,11 @@ class Mesh : public PointCloud {
IndexTypeVector<FaceIndex, Face> &faces() { return faces_; } IndexTypeVector<FaceIndex, Face> &faces() { return faces_; }
private: 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. // Mesh specific per-attribute data.
std::vector<AttributeData> attribute_data_; std::vector<AttributeData> attribute_data_;
@ -280,11 +322,6 @@ class Mesh : public PointCloud {
// Materials applied to to this mesh. // Materials applied to to this mesh.
MaterialLibrary material_library_; 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. // Sets of feature IDs as defined by EXT_mesh_features glTF extension.
IndexTypeVector<MeshFeaturesIndex, std::unique_ptr<MeshFeatures>> IndexTypeVector<MeshFeaturesIndex, std::unique_ptr<MeshFeatures>>
mesh_features_; mesh_features_;
@ -296,6 +333,15 @@ class Mesh : public PointCloud {
IndexTypeVector<MeshFeaturesIndex, std::vector<int>> IndexTypeVector<MeshFeaturesIndex, std::vector<int>>
mesh_features_material_mask_; 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., // Texture library for storing non-material textures used by this mesh, e.g.,
// textures containing mesh feature IDs of EXT_mesh_features glTF extension. // 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. // 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) { void MeshCleanup::RemoveDuplicateFaces(Mesh *mesh) {
const PointAttribute *const pos_att = std::unordered_set<Mesh::Face, HashArray<Mesh::Face>> is_face_used;
mesh->GetNamedAttribute(GeometryAttribute::POSITION);
typedef std::array<AttributeValueIndex::ValueType, 3> PosTriplet;
PosTriplet pos_indices;
std::unordered_set<PosTriplet, HashArray<PosTriplet>> is_face_used;
uint32_t num_duplicate_faces = 0; uint32_t num_duplicate_faces = 0;
for (FaceIndex fi(0); fi < mesh->num_faces(); ++fi) { for (FaceIndex fi(0); fi < mesh->num_faces(); ++fi) {
const auto f = mesh->face(fi); auto face = mesh->face(fi);
for (int c = 0; c < 3; ++c) {
pos_indices[c] = pos_att->mapped_index(f[c]).value(); // Shift the face indices until the smallest index is the first one.
} while (face[0] > face[1] || face[0] > face[2]) {
// 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]) {
// Shift to the left. // Shift to the left.
std::swap(pos_indices[0], pos_indices[1]); std::swap(face[0], face[1]);
std::swap(pos_indices[1], pos_indices[2]); std::swap(face[1], face[2]);
} }
// Check if have encountered the same position triplet on a different face. // Check if have encountered the same face before.
if (is_face_used.find(pos_indices) != is_face_used.end()) { if (is_face_used.find(face) != is_face_used.end()) {
// Duplicate face. Ignore it. // Duplicate face. Ignore it.
num_duplicate_faces++; num_duplicate_faces++;
} else { } else {
// Insert new face to the set. // Insert new face to the set.
is_face_used.insert(pos_indices); is_face_used.insert(face);
if (num_duplicate_faces > 0) { if (num_duplicate_faces > 0) {
// Copy the face to its new location. // 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) { TEST_F(MeshCleanupTest, TestDuplicateFaces) {
// This test verifies that the mesh cleanup tool removes duplicate faces.
TriangleSoupMeshBuilder mb; TriangleSoupMeshBuilder mb;
mb.Start(5); mb.Start(5);
const int pos_att_id = const int pos_att_id =
mb.AddAttribute(GeometryAttribute::POSITION, 3, DT_FLOAT32); 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 // clang-format off
mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(0), mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(0),
Vector3f(0.f, 0.f, 0.f).data(), Vector3f(0.f, 0.f, 0.f).data(),
Vector3f(1.f, 0.f, 0.f).data(), Vector3f(1.f, 0.f, 0.f).data(),
Vector3f(0.f, 1.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), mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(1),
Vector3f(0.f, 0.f, 0.f).data(), Vector3f(0.f, 0.f, 0.f).data(),
Vector3f(1.f, 0.f, 0.f).data(), Vector3f(1.f, 0.f, 0.f).data(),
Vector3f(0.f, 1.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), mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(2),
Vector3f(0.f, 0.f, 0.f).data(), Vector3f(0.f, 0.f, 0.f).data(),
Vector3f(1.f, 0.f, 0.f).data(), Vector3f(1.f, 0.f, 0.f).data(),
Vector3f(0.f, 1.f, 1.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), mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(3),
Vector3f(1.f, 0.f, 0.f).data(), Vector3f(1.f, 0.f, 0.f).data(),
Vector3f(0.f, 1.f, 0.f).data(), Vector3f(0.f, 1.f, 0.f).data(),
Vector3f(0.f, 0.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), mb.SetAttributeValuesForFace(pos_att_id, FaceIndex(4),
Vector3f(0.f, 0.f, 0.f).data(), Vector3f(0.f, 0.f, 0.f).data(),
Vector3f(1.f, 0.f, 0.f).data(), Vector3f(1.f, 0.f, 0.f).data(),
Vector3f(0.f, 1.f, 1.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 // clang-format on
std::unique_ptr<Mesh> mesh = mb.Finalize(); std::unique_ptr<Mesh> mesh = mb.Finalize();
@ -180,7 +204,7 @@ TEST_F(MeshCleanupTest, TestDuplicateFaces) {
ASSERT_EQ(mesh->num_faces(), 5); ASSERT_EQ(mesh->num_faces(), 5);
const MeshCleanupOptions cleanup_options; const MeshCleanupOptions cleanup_options;
DRACO_ASSERT_OK(MeshCleanup::Cleanup(mesh.get(), 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 } // namespace draco

View File

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

View File

@ -18,6 +18,10 @@
#ifndef DRACO_MESH_MESH_MISC_FUNCTIONS_H_ #ifndef DRACO_MESH_MESH_MISC_FUNCTIONS_H_
#define 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/corner_table.h"
#include "draco/mesh/mesh.h" #include "draco/mesh/mesh.h"
@ -77,6 +81,10 @@ InterpolatedVectorT ComputeInterpolatedAttributeValueOnMeshFace(
for (int c = 0; c < 3; ++c) { for (int c = 0; c < 3; ++c) {
attribute.GetMappedValue(face[c], &(val[c][0])); 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. // Return an interpolated value.
InterpolatedVectorT res; InterpolatedVectorT res;
for (int d = 0; d < InterpolatedVectorT::dimension; ++d) { for (int d = 0; d < InterpolatedVectorT::dimension; ++d) {

View File

@ -18,10 +18,13 @@
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#include <vector>
#include "draco/attributes/geometry_attribute.h"
#include "draco/mesh/mesh_utils.h" #include "draco/mesh/mesh_utils.h"
#include "draco/mesh/triangle_soup_mesh_builder.h" #include "draco/mesh/triangle_soup_mesh_builder.h"
#include "draco/point_cloud/point_cloud_builder.h" #include "draco/point_cloud/point_cloud_builder.h"
#include "draco/texture/texture_map.h"
namespace draco { namespace draco {
@ -46,8 +49,8 @@ class MeshSplitterInternal {
const PointAttribute *split_attribute, const PointAttribute *split_attribute,
WorkData *work_data) const; WorkData *work_data) const;
// Builds the meshes from the data accumulated in the builders. // Builds the meshes from the data accumulated in the builders.
StatusOr<MeshSplitter::MeshVector> BuildMeshes(const Mesh &mesh, StatusOr<MeshSplitter::MeshVector> BuildMeshes(
WorkData *work_data) const; const Mesh &mesh, WorkData *work_data, bool deduplicate_vertices) const;
}; };
namespace { namespace {
@ -65,7 +68,9 @@ void AddElementToBuilder(
MeshSplitter::MeshSplitter() MeshSplitter::MeshSplitter()
: preserve_materials_(false), : preserve_materials_(false),
remove_unused_material_indices_(true), 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( StatusOr<MeshSplitter::MeshVector> MeshSplitter::SplitMesh(
const Mesh &mesh, uint32_t split_attribute_id) { const Mesh &mesh, uint32_t split_attribute_id) {
@ -86,7 +91,7 @@ StatusOr<MeshSplitter::MeshVector> MeshSplitter::SplitMeshInternal(
mesh.attribute(split_attribute_id); mesh.attribute(split_attribute_id);
// Preserve the split attribute only if it is the material attribute and the // 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. // discarded.
// TODO(ostava): We may revisit this later and add an option to always // TODO(ostava): We may revisit this later and add an option to always
// preserve the split attribute. // preserve the split attribute.
@ -126,8 +131,9 @@ StatusOr<MeshSplitter::MeshVector> MeshSplitter::SplitMeshInternal(
splitter_internal.AddElementsToBuilder(mesh, split_attribute, &work_data); splitter_internal.AddElementsToBuilder(mesh, split_attribute, &work_data);
DRACO_ASSIGN_OR_RETURN(MeshVector out_meshes, DRACO_ASSIGN_OR_RETURN(
splitter_internal.BuildMeshes(mesh, &work_data)); MeshVector out_meshes,
splitter_internal.BuildMeshes(mesh, &work_data, deduplicate_vertices_));
return FinalizeMeshes(mesh, work_data, std::move(out_meshes)); 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); const GeometryAttribute *const src_att = mesh.attribute(ai);
work_data->att_id_map[ai] = work_data->builders[b_index].AddAttribute( work_data->att_id_map[ai] = work_data->builders[b_index].AddAttribute(
src_att->attribute_type(), src_att->num_components(), 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 <> template <>
StatusOr<MeshSplitter::MeshVector> StatusOr<MeshSplitter::MeshVector>
MeshSplitterInternal<TriangleSoupMeshBuilder>::BuildMeshes( 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(); const int num_out_meshes = work_data->builders.size();
MeshSplitter::MeshVector out_meshes(num_out_meshes); MeshSplitter::MeshVector out_meshes(num_out_meshes);
for (int mi = 0; mi < num_out_meshes; ++mi) { for (int mi = 0; mi < num_out_meshes; ++mi) {
@ -270,7 +278,7 @@ MeshSplitterInternal<TriangleSoupMeshBuilder>::BuildMeshes(
template <> template <>
StatusOr<MeshSplitter::MeshVector> StatusOr<MeshSplitter::MeshVector>
MeshSplitterInternal<PointCloudBuilder>::BuildMeshes( 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(); const int num_out_meshes = work_data->builders.size();
MeshSplitter::MeshVector out_meshes(num_out_meshes); MeshSplitter::MeshVector out_meshes(num_out_meshes);
for (int mi = 0; mi < num_out_meshes; ++mi) { 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 // For point clouds, we first build a point cloud and copy it over into
// a draco::Mesh. // 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) { if (pc == nullptr) {
continue; continue;
} }
@ -311,7 +320,66 @@ StatusOr<MeshSplitter::MeshVector> MeshSplitter::FinalizeMeshes(
} }
out_meshes[mi]->SetName(mesh.GetName()); out_meshes[mi]->SetName(mesh.GetName());
if (preserve_materials_) { if (preserve_materials_) {
out_meshes[mi]->GetMaterialLibrary().Copy(mesh.GetMaterialLibrary()); 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. // Copy metadata of the original mesh to the output meshes.
@ -404,6 +472,62 @@ StatusOr<MeshSplitter::MeshVector> MeshSplitter::FinalizeMeshes(
MeshUtils::RemoveUnusedMeshFeatures(out_meshes[mi].get())); 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 // Remove unused materials after we remove mesh features because some of
// the mesh features may have referenced old material indices. // the mesh features may have referenced old material indices.
if (preserve_materials_) { if (preserve_materials_) {
@ -442,8 +566,9 @@ StatusOr<MeshSplitter::MeshVector> MeshSplitter::SplitMeshToComponents(
AddElementToBuilder(mi, fi, target_fi, mesh, &work_data); AddElementToBuilder(mi, fi, target_fi, mesh, &work_data);
} }
} }
DRACO_ASSIGN_OR_RETURN(auto out_meshes, DRACO_ASSIGN_OR_RETURN(
splitter_internal.BuildMeshes(mesh, &work_data)); auto out_meshes,
splitter_internal.BuildMeshes(mesh, &work_data, deduplicate_vertices_));
return FinalizeMeshes(mesh, work_data, std::move(out_meshes)); return FinalizeMeshes(mesh, work_data, std::move(out_meshes));
} }

View File

@ -63,6 +63,25 @@ class MeshSplitter {
// Default = false. // Default = false.
void SetPreserveMeshFeatures(bool flag) { preserve_mesh_features_ = flag; } 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 // Splits the input |mesh| according to attribute values stored in the
// specified attribute. If the |mesh| contains faces, the attribute values // 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 // 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 preserve_materials_;
bool remove_unused_material_indices_; bool remove_unused_material_indices_;
bool preserve_mesh_features_; bool preserve_mesh_features_;
bool preserve_structural_metadata_;
bool deduplicate_vertices_;
template <typename BuilderT> template <typename BuilderT>
friend class MeshSplitterInternal; friend class MeshSplitterInternal;

View File

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

View File

@ -16,6 +16,7 @@
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <vector>
#include "draco/core/draco_test_base.h" #include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h" #include "draco/core/draco_test_utils.h"
@ -539,6 +540,57 @@ TEST(MeshTest, MeshCopyWithMeshFeatures) {
library_copy.GetTexture(1)); 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. // Tests copying of a mesh with structural metadata.
TEST(MeshTest, TestCopyWithStructuralMetadata) { TEST(MeshTest, TestCopyWithStructuralMetadata) {
const std::unique_ptr<draco::Mesh> mesh = const std::unique_ptr<draco::Mesh> mesh =
@ -546,22 +598,32 @@ TEST(MeshTest, TestCopyWithStructuralMetadata) {
ASSERT_NE(mesh, nullptr); ASSERT_NE(mesh, nullptr);
// Add structural metadata to the mesh. // Add structural metadata to the mesh.
draco::PropertyTable::Schema schema; draco::StructuralMetadataSchema schema;
schema.json.SetString("Data"); schema.json.SetString("Data");
mesh->GetStructuralMetadata().SetPropertyTableSchema(schema); mesh->GetStructuralMetadata().SetSchema(schema);
mesh->AddPropertyAttributesIndex(0);
mesh->AddPropertyAttributesIndex(1);
// Copy the mesh. // Copy the mesh.
draco::Mesh copy; draco::Mesh copy;
copy.Copy(*mesh); copy.Copy(*mesh);
// Check that the structural metadata has been copied. // Check that the structural metadata has been copied.
ASSERT_EQ( ASSERT_EQ(copy.GetStructuralMetadata().GetSchema().json.GetString(), "Data");
copy.GetStructuralMetadata().GetPropertyTableSchema().json.GetString(), ASSERT_EQ(copy.NumPropertyAttributesIndices(), 2);
"Data"); 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. // Tests removing of unused materials for a mesh with mesh features and property
TEST(MeshTest, RemoveUnusedMaterialsWithMeshFeatures) { // attributes indices.
TEST(MeshTest,
RemoveUnusedMaterialsWithMeshFeaturesAndPropertyAttributesIndices) {
const std::unique_ptr<draco::Mesh> mesh = const std::unique_ptr<draco::Mesh> mesh =
draco::ReadMeshFromTestFile("BoxesMeta/glTF/BoxesMeta.gltf"); draco::ReadMeshFromTestFile("BoxesMeta/glTF/BoxesMeta.gltf");
ASSERT_NE(mesh, nullptr); ASSERT_NE(mesh, nullptr);
@ -580,6 +642,12 @@ TEST(MeshTest, RemoveUnusedMaterialsWithMeshFeatures) {
ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(4), 0), ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(4), 0),
1); 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. // Remove material 0.
draco::PointAttribute *mat_att = mesh->attribute( draco::PointAttribute *mat_att = mesh->attribute(
mesh->GetNamedAttributeId(draco::GeometryAttribute::MATERIAL)); mesh->GetNamedAttributeId(draco::GeometryAttribute::MATERIAL));
@ -588,17 +656,21 @@ TEST(MeshTest, RemoveUnusedMaterialsWithMeshFeatures) {
mat_att->SetAttributeValue(draco::AttributeValueIndex(0), &new_mat_index); mat_att->SetAttributeValue(draco::AttributeValueIndex(0), &new_mat_index);
// This should not do anything because we still have the material 0 referenced // 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(); mesh->RemoveUnusedMaterials();
ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 2); ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 2);
ASSERT_EQ(mesh->NumMeshFeatures(), 5); 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::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. // All remaining mesh features should be still mapped to material 1.
ASSERT_EQ(mesh->NumMeshFeatures(), 3);
ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(0), 0), ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(0), 0),
1); 1);
ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(1), 0), ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(1), 0),
@ -606,18 +678,50 @@ TEST(MeshTest, RemoveUnusedMaterialsWithMeshFeatures) {
ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(2), 0), ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(2), 0),
1); 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). // Now remove the unused materials (0).
mesh->RemoveUnusedMaterials(); mesh->RemoveUnusedMaterials();
// Only one material should be remaining and all the mesh features should now // Only one material should be remaining.
// be mapped to material 0.
ASSERT_EQ(mesh->GetMaterialLibrary().NumMaterials(), 1); 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), ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(0), 0),
0); 0);
ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(1), 0), ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(1), 0),
0); 0);
ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(2), 0), ASSERT_EQ(mesh->GetMeshFeaturesMaterialMask(draco::MeshFeaturesIndex(2), 0),
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 #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) { // Returns indices of all used materials on the |mesh|.
// Unused mesh features are features that are not used by any face / vertex std::unordered_set<int> FindUsedMaterials(const Mesh &mesh) {
// 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 PointAttribute *const mat_att = const PointAttribute *const mat_att =
mesh->GetNamedAttribute(GeometryAttribute::MATERIAL); mesh.GetNamedAttribute(GeometryAttribute::MATERIAL);
// Find which materials are used.
std::unordered_set<int> used_materials; std::unordered_set<int> used_materials;
if (mat_att == nullptr) { if (mat_att == nullptr) {
// Only material with index 0 is assumed to be used. // Only material with index 0 is assumed to be used.
@ -187,7 +182,16 @@ Status MeshUtils::RemoveUnusedMeshFeatures(Mesh *mesh) {
used_materials.insert(mat_index); 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; std::vector<MeshFeaturesIndex> unused_mesh_features;
for (MeshFeaturesIndex mfi(0); mfi < mesh->NumMeshFeatures(); ++mfi) { for (MeshFeaturesIndex mfi(0); mfi < mesh->NumMeshFeatures(); ++mfi) {
bool is_used = false; bool is_used = false;
@ -247,6 +251,44 @@ Status MeshUtils::RemoveUnusedMeshFeatures(Mesh *mesh) {
return OkStatus(); 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, bool MeshUtils::FlipTextureUvValues(bool flip_u, bool flip_v,
PointAttribute *att) { PointAttribute *att) {
if (att->attribute_type() != GeometryAttribute::TEX_COORD) { if (att->attribute_type() != GeometryAttribute::TEX_COORD) {

View File

@ -42,6 +42,9 @@ class MeshUtils {
// error is returned. // error is returned.
static Status RemoveUnusedMeshFeatures(Mesh *mesh); static Status RemoveUnusedMeshFeatures(Mesh *mesh);
// Removes unused property attributes indices from |mesh|.
static Status RemoveUnusedPropertyAttributesIndices(Mesh *mesh);
// Flips the UV values of |att|. // Flips the UV values of |att|.
static bool FlipTextureUvValues(bool flip_u, bool flip_v, static bool FlipTextureUvValues(bool flip_u, bool flip_v,
PointAttribute *att); 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 } // namespace
#endif // DRACO_TRANSCODER_SUPPORTED #endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -14,6 +14,8 @@
// //
#include "draco/mesh/triangle_soup_mesh_builder.h" #include "draco/mesh/triangle_soup_mesh_builder.h"
#include <string>
namespace draco { namespace draco {
void TriangleSoupMeshBuilder::Start(int num_faces) { void TriangleSoupMeshBuilder::Start(int num_faces) {
@ -96,4 +98,16 @@ std::unique_ptr<Mesh> TriangleSoupMeshBuilder::Finalize() {
return std::move(mesh_); 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 } // namespace draco

View File

@ -80,6 +80,14 @@ class TriangleSoupMeshBuilder {
mesh_->AddMetadata(std::move(metadata)); 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. // Add metadata for an attribute.
void AddAttributeMetadata(int32_t att_id, void AddAttributeMetadata(int32_t att_id,
std::unique_ptr<AttributeMetadata> metadata) { 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, 0.f, 1.f).data(),
Vector3f(0.f, 1.f, 0.f).data()); Vector3f(0.f, 1.f, 0.f).data());
// clang-format on // clang-format on
#ifdef DRACO_TRANSCODER_SUPPORTED
mb.SetAttributeName(pos_att_id, "Bob");
#endif
std::unique_ptr<Mesh> mesh = mb.Finalize(); std::unique_ptr<Mesh> mesh = mb.Finalize();
ASSERT_NE(mesh, nullptr) << "Failed to build the cube mesh."; ASSERT_NE(mesh, nullptr) << "Failed to build the cube mesh.";
#ifdef DRACO_TRANSCODER_SUPPORTED #ifdef DRACO_TRANSCODER_SUPPORTED
EXPECT_EQ(mesh->GetName(), "Cube"); EXPECT_EQ(mesh->GetName(), "Cube");
EXPECT_EQ(mesh->attribute(pos_att_id)->name(), "Bob");
#endif #endif
EXPECT_EQ(mesh->num_points(), 8) << "Unexpected number of vertices."; EXPECT_EQ(mesh->num_points(), 8) << "Unexpected number of vertices.";
EXPECT_EQ(mesh->num_faces(), 12) << "Unexpected number of faces."; EXPECT_EQ(mesh->num_faces(), 12) << "Unexpected number of faces.";
@ -207,6 +211,22 @@ TEST_F(TriangleSoupMeshBuilderTest, TestPerFaceAttribs) {
<< "Unexpected attribute element type."; << "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 #ifdef DRACO_TRANSCODER_SUPPORTED
TEST_F(TriangleSoupMeshBuilderTest, NormalizedColor) { TEST_F(TriangleSoupMeshBuilderTest, NormalizedColor) {
// This tests, verifies that the mesh builder constructs a valid model with // 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,111 +23,56 @@
namespace draco { namespace draco {
bool PropertyTable::Schema::Object::operator==(const Object& other) const { bool PropertyTable::Property::Data::operator==(const Data &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; return data == other.data && target == other.target;
} }
bool PropertyTable::Property::Offsets::operator==(const Offsets& other) const { bool PropertyTable::Property::Offsets::operator==(const Offsets &other) const {
return data == other.data && type == other.type; return data == other.data && type == other.type;
} }
bool PropertyTable::Property::operator==(const Property& other) const { bool PropertyTable::Property::operator==(const Property &other) const {
return name_ == other.name_ && data_ == other.data_ && return name_ == other.name_ && data_ == other.data_ &&
array_offsets_ == other.array_offsets_ && array_offsets_ == other.array_offsets_ &&
string_offsets_ == other.string_offsets_; string_offsets_ == other.string_offsets_;
} }
void PropertyTable::Property::Copy(const Property& src) { void PropertyTable::Property::Copy(const Property &src) {
name_ = src.name_; name_ = src.name_;
data_ = src.data_; data_ = src.data_;
array_offsets_ = src.array_offsets_; array_offsets_ = src.array_offsets_;
string_offsets_ = src.string_offsets_; string_offsets_ = src.string_offsets_;
} }
void PropertyTable::Property::SetName(const std::string& name) { name_ = name; } void PropertyTable::Property::SetName(const std::string &name) { name_ = name; }
const std::string& PropertyTable::Property::GetName() const { return name_; } const std::string &PropertyTable::Property::GetName() const { return name_; }
PropertyTable::Property::Data& PropertyTable::Property::GetData() { PropertyTable::Property::Data &PropertyTable::Property::GetData() {
return data_; return data_;
} }
const PropertyTable::Property::Data& PropertyTable::Property::GetData() const { const PropertyTable::Property::Data &PropertyTable::Property::GetData() const {
return data_; return data_;
} }
const PropertyTable::Property::Offsets& const PropertyTable::Property::Offsets &
PropertyTable::Property::GetArrayOffsets() const { PropertyTable::Property::GetArrayOffsets() const {
return array_offsets_; return array_offsets_;
} }
PropertyTable::Property::Offsets& PropertyTable::Property::GetArrayOffsets() { PropertyTable::Property::Offsets &PropertyTable::Property::GetArrayOffsets() {
return array_offsets_; return array_offsets_;
} }
const PropertyTable::Property::Offsets& const PropertyTable::Property::Offsets &
PropertyTable::Property::GetStringOffsets() const { PropertyTable::Property::GetStringOffsets() const {
return string_offsets_; return string_offsets_;
} }
PropertyTable::Property::Offsets& PropertyTable::Property::GetStringOffsets() { PropertyTable::Property::Offsets &PropertyTable::Property::GetStringOffsets() {
return string_offsets_; return string_offsets_;
} }
PropertyTable::PropertyTable() : count_(0) {} PropertyTable::PropertyTable() : count_(0) {}
bool PropertyTable::operator==(const PropertyTable& other) const { bool PropertyTable::operator==(const PropertyTable &other) const {
if (name_ != other.name_ || class_ != other.class_ || if (name_ != other.name_ || class_ != other.class_ ||
count_ != other.count_ || count_ != other.count_ ||
properties_.size() != other.properties_.size()) { properties_.size() != other.properties_.size()) {
@ -141,7 +86,7 @@ bool PropertyTable::operator==(const PropertyTable& other) const {
return true; return true;
} }
void PropertyTable::Copy(const PropertyTable& src) { void PropertyTable::Copy(const PropertyTable &src) {
name_ = src.name_; name_ = src.name_;
class_ = src.class_; class_ = src.class_;
count_ = src.count_; count_ = src.count_;
@ -154,11 +99,11 @@ void PropertyTable::Copy(const PropertyTable& src) {
} }
} }
void PropertyTable::SetName(const std::string& value) { name_ = value; } void PropertyTable::SetName(const std::string &value) { name_ = value; }
const std::string& PropertyTable::GetName() const { return name_; } const std::string &PropertyTable::GetName() const { return name_; }
void PropertyTable::SetClass(const std::string& value) { class_ = value; } void PropertyTable::SetClass(const std::string &value) { class_ = value; }
const std::string& PropertyTable::GetClass() const { return class_; } const std::string &PropertyTable::GetClass() const { return class_; }
void PropertyTable::SetCount(int count) { count_ = count; } void PropertyTable::SetCount(int count) { count_ = count; }
int PropertyTable::GetCount() const { return count_; } int PropertyTable::GetCount() const { return count_; }
@ -168,10 +113,10 @@ int PropertyTable::AddProperty(std::unique_ptr<Property> property) {
return properties_.size() - 1; return properties_.size() - 1;
} }
int PropertyTable::NumProperties() const { return properties_.size(); } int PropertyTable::NumProperties() const { return properties_.size(); }
const PropertyTable::Property& PropertyTable::GetProperty(int index) const { const PropertyTable::Property &PropertyTable::GetProperty(int index) const {
return *properties_[index]; return *properties_[index];
} }
PropertyTable::Property& PropertyTable::GetProperty(int index) { PropertyTable::Property &PropertyTable::GetProperty(int index) {
return *properties_[index]; return *properties_[index];
} }
void PropertyTable::RemoveProperty(int index) { void PropertyTable::RemoveProperty(int index) {

View File

@ -19,116 +19,29 @@
#ifdef DRACO_TRANSCODER_SUPPORTED #ifdef DRACO_TRANSCODER_SUPPORTED
#include <cstdint>
#include <cstring>
#include <limits>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include "draco/core/status_or.h"
namespace draco { namespace draco {
// Describes a property table as defined in the EXT_structural_metadata glTF // Describes a property table (properties are table columns) as defined in the
// extension, including property table schema and table properties (columns). // EXT_structural_metadata glTF extension.
class PropertyTable { class PropertyTable {
public: 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. // Describes a property (column) of a property table.
class Property { class Property {
public: public:
// Describes glTF buffer view data. // Describes glTF buffer view data.
struct Data { struct Data {
// Methods for comparing two data objects. // Methods for comparing two data objects.
bool operator==(const Data& other) const; bool operator==(const Data &other) const;
bool operator!=(const Data& other) const { return !(*this == other); } bool operator!=(const Data &other) const { return !(*this == other); }
// Buffer view data. // Buffer view data.
std::vector<uint8_t> data; std::vector<uint8_t> data;
@ -143,36 +56,102 @@ class PropertyTable {
// arrays. // arrays.
struct Offsets { struct Offsets {
// Methods for comparing two offsets. // Methods for comparing two offsets.
bool operator==(const Offsets& other) const; bool operator==(const Offsets &other) const;
bool operator!=(const Offsets& other) const { return !(*this == other); } bool operator!=(const Offsets &other) const { return !(*this == other); }
// Data containing the offset entries. // Data containing the offset entries.
Data data; Data data;
// Data type of the offset entries. // Data type of the offset entries.
std::string type; 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. // Creates an empty property.
Property(); Property() = default;
// Methods for comparing two properties. // Methods for comparing two properties.
bool operator==(const Property& other) const; bool operator==(const Property &other) const;
bool operator!=(const Property& other) const { return !(*this == other); } bool operator!=(const Property &other) const { return !(*this == other); }
// Copies all data from |src| property. // Copies all data from |src| property.
void Copy(const Property& src); void Copy(const Property &src);
// Name of this property. // Name of this property.
void SetName(const std::string& name); void SetName(const std::string &name);
const std::string& GetName() const; const std::string &GetName() const;
// Property data stores one table column worth of data. For example, when // Property data stores one table column worth of data. For example, when
// the data of type UINT8 is [11, 22] then the property values are 11 and 22 // the data of type UINT8 is [11, 22] then the property values are 11 and 22
// for the first and second table rows. See EXT_structural_metadata glTF // for the first and second table rows. See EXT_structural_metadata glTF
// extension documentation for more details. // extension documentation for more details.
Data& GetData(); Data &GetData();
const Data& GetData() const; const Data &GetData() const;
// Array offsets are used when property data contains a variable-length // Array offsets are used when property data contains a variable-length
// number arrays. For example, when the data is [0, 1, 2, 3, 4] and the // number arrays. For example, when the data is [0, 1, 2, 3, 4] and the
@ -180,16 +159,16 @@ class PropertyTable {
// arrays are [0, 1] and [2, 3, 4] for the first and second table rows, // arrays are [0, 1] and [2, 3, 4] for the first and second table rows,
// respectively. See EXT_structural_metadata glTF extension documentation // respectively. See EXT_structural_metadata glTF extension documentation
// for more details. // for more details.
const Offsets& GetArrayOffsets() const; const Offsets &GetArrayOffsets() const;
Offsets& GetArrayOffsets(); Offsets &GetArrayOffsets();
// String offsets are used when property data contains strings. For example, // String offsets are used when property data contains strings. For example,
// when the data is "SeaLand" and the array offsets are [0, 3, 7] for a // when the data is "SeaLand" and the array offsets are [0, 3, 7] for a
// two-row table, then the property strings are "Sea" and "Land" for the // two-row table, then the property strings are "Sea" and "Land" for the
// first and second table rows, respectively. See EXT_structural_metadata // first and second table rows, respectively. See EXT_structural_metadata
// glTF extension documentation for more details. // glTF extension documentation for more details.
const Offsets& GetStringOffsets() const; const Offsets &GetStringOffsets() const;
Offsets& GetStringOffsets(); Offsets &GetStringOffsets();
private: private:
std::string name_; std::string name_;
@ -203,21 +182,21 @@ class PropertyTable {
PropertyTable(); PropertyTable();
// Methods for comparing two property tables. // Methods for comparing two property tables.
bool operator==(const PropertyTable& other) const; bool operator==(const PropertyTable &other) const;
bool operator!=(const PropertyTable& other) const { bool operator!=(const PropertyTable &other) const {
return !(*this == other); return !(*this == other);
} }
// Copies all data from |src| property table. // Copies all data from |src| property table.
void Copy(const PropertyTable& src); void Copy(const PropertyTable &src);
// Name of this property table. // Name of this property table.
void SetName(const std::string& value); void SetName(const std::string &value);
const std::string& GetName() const; const std::string &GetName() const;
// Class of this property table. // Class of this property table.
void SetClass(const std::string& value); void SetClass(const std::string &value);
const std::string& GetClass() const; const std::string &GetClass() const;
// Number of rows in this property table. // Number of rows in this property table.
void SetCount(int count); void SetCount(int count);
@ -226,8 +205,8 @@ class PropertyTable {
// Table properties (columns). // Table properties (columns).
int AddProperty(std::unique_ptr<Property> property); int AddProperty(std::unique_ptr<Property> property);
int NumProperties() const; int NumProperties() const;
const Property& GetProperty(int index) const; const Property &GetProperty(int index) const;
Property& GetProperty(int index); Property &GetProperty(int index);
void RemoveProperty(int index); void RemoveProperty(int index);
private: private:

View File

@ -14,11 +14,12 @@
// //
#include "draco/metadata/property_table.h" #include "draco/metadata/property_table.h"
#include <cstdint>
#include <memory> #include <memory>
#include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h" #include "draco/core/draco_test_utils.h"
namespace { namespace {
@ -60,266 +61,6 @@ TEST(PropertyTableTest, TestPropertyTableDefaults) {
ASSERT_EQ(table.NumProperties(), 0); 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(PropertyTableTest, TestPropertySettersAndGetters) {
// Test setter and getter methods of the property table property. // Test setter and getter methods of the property table property.
draco::PropertyTable::Property 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 #endif // DRACO_TRANSCODER_SUPPORTED
} // namespace } // namespace

View File

@ -21,30 +21,54 @@
namespace draco { 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 { bool StructuralMetadata::operator==(const StructuralMetadata &other) const {
return property_table_schema_ == other.property_table_schema_ && return schema_ == other.schema_ &&
property_tables_ == other.property_tables_; VectorsAreEqual(property_tables_, other.property_tables_) &&
VectorsAreEqual(property_attributes_, other.property_attributes_);
} }
void StructuralMetadata::Copy(const StructuralMetadata &src) { 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()); property_tables_.resize(src.property_tables_.size());
for (int i = 0; i < property_tables_.size(); ++i) { for (int i = 0; i < property_tables_.size(); ++i) {
property_tables_[i] = std::unique_ptr<PropertyTable>(new PropertyTable()); property_tables_[i] = std::unique_ptr<PropertyTable>(new PropertyTable());
property_tables_[i]->Copy(*src.property_tables_[i]); 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( void StructuralMetadata::SetSchema(const StructuralMetadataSchema &schema) {
const PropertyTable::Schema &schema) { schema_ = schema;
property_table_schema_ = schema;
} }
const PropertyTable::Schema &StructuralMetadata::GetPropertyTableSchema() const StructuralMetadataSchema &StructuralMetadata::GetSchema() const {
const { return schema_;
return property_table_schema_;
} }
int StructuralMetadata::AddPropertyTable( int StructuralMetadata::AddPropertyTable(
@ -69,6 +93,29 @@ void StructuralMetadata::RemovePropertyTable(int index) {
property_tables_.erase(property_tables_.begin() + 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 } // namespace draco
#endif // DRACO_TRANSCODER_SUPPORTED #endif // DRACO_TRANSCODER_SUPPORTED

View File

@ -20,17 +20,18 @@
#ifdef DRACO_TRANSCODER_SUPPORTED #ifdef DRACO_TRANSCODER_SUPPORTED
#include <memory> #include <memory>
#include <string>
#include <vector> #include <vector>
#include "draco/metadata/property_attribute.h"
#include "draco/metadata/property_table.h" #include "draco/metadata/property_table.h"
#include "draco/metadata/structural_metadata_schema.h"
namespace draco { namespace draco {
// Holds data associated with EXT_structural_metadata glTF extension. // Holds data associated with EXT_structural_metadata glTF extension.
class StructuralMetadata { class StructuralMetadata {
public: public:
StructuralMetadata(); StructuralMetadata() = default;
// Methods for comparing two structural metadata objects. // Methods for comparing two structural metadata objects.
bool operator==(const StructuralMetadata &other) const; bool operator==(const StructuralMetadata &other) const;
@ -41,9 +42,9 @@ class StructuralMetadata {
// Copies |src| structural metadata into this object. // Copies |src| structural metadata into this object.
void Copy(const StructuralMetadata &src); void Copy(const StructuralMetadata &src);
// Property table schema. // Schema of the structural metadata.
void SetPropertyTableSchema(const PropertyTable::Schema &schema); void SetSchema(const StructuralMetadataSchema &schema);
const PropertyTable::Schema &GetPropertyTableSchema() const; const StructuralMetadataSchema &GetSchema() const;
// Property tables. // Property tables.
int AddPropertyTable(std::unique_ptr<PropertyTable> property_table); int AddPropertyTable(std::unique_ptr<PropertyTable> property_table);
@ -52,10 +53,23 @@ class StructuralMetadata {
PropertyTable &GetPropertyTable(int index); PropertyTable &GetPropertyTable(int index);
void RemovePropertyTable(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: private:
// Property table schema and property tables. // Schema of the structural metadata.
PropertyTable::Schema property_table_schema_; StructuralMetadataSchema schema_;
// Property tables.
std::vector<std::unique_ptr<PropertyTable>> property_tables_; std::vector<std::unique_ptr<PropertyTable>> property_tables_;
// Property attributes.
std::vector<std::unique_ptr<PropertyAttribute>> property_attributes_;
}; };
} // namespace draco } // 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 <memory>
#include <utility> #include <utility>
#include <vector>
#include "draco/core/draco_test_base.h" #include "draco/core/draco_test_base.h"
#include "draco/core/draco_test_utils.h" #include "draco/core/draco_test_utils.h"
@ -30,9 +29,9 @@ TEST(StructuralMetadataTest, TestCopy) {
draco::StructuralMetadata structural_metadata; draco::StructuralMetadata structural_metadata;
// Add property table schema to structural metadata. // Add property table schema to structural metadata.
draco::PropertyTable::Schema schema; draco::StructuralMetadataSchema schema;
schema.json.SetString("Culture"); schema.json.SetString("Culture");
structural_metadata.SetPropertyTableSchema(schema); structural_metadata.SetSchema(schema);
// Add property table to structural metadata. // Add property table to structural metadata.
std::unique_ptr<draco::PropertyTable> table(new draco::PropertyTable()); std::unique_ptr<draco::PropertyTable> table(new draco::PropertyTable());
@ -58,7 +57,7 @@ TEST(StructuralMetadataTest, TestCopy) {
copy.Copy(structural_metadata); copy.Copy(structural_metadata);
// Check that the structural metadata property table schema has been copied. // 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. // Check that the structural metadata property table has been copied.
ASSERT_EQ(copy.NumPropertyTables(), 1); ASSERT_EQ(copy.NumPropertyTables(), 1);
@ -114,6 +113,7 @@ TEST(StructuralMetadataTest, TestPropertyTables) {
TEST(StructuralMetadataTest, TestCompare) { TEST(StructuralMetadataTest, TestCompare) {
// Test comparison of two structural metadata objects. // Test comparison of two structural metadata objects.
typedef draco::PropertyTable PropertyTable; typedef draco::PropertyTable PropertyTable;
typedef draco::PropertyAttribute PropertyAttribute;
{ {
// Compare the same structural metadata object. // Compare the same structural metadata object.
draco::StructuralMetadata a; draco::StructuralMetadata a;
@ -131,17 +131,17 @@ TEST(StructuralMetadataTest, TestCompare) {
// Compare two structural metadata objects with different schemas. // Compare two structural metadata objects with different schemas.
draco::StructuralMetadata a; draco::StructuralMetadata a;
draco::StructuralMetadata b; draco::StructuralMetadata b;
PropertyTable::Schema s1; draco::StructuralMetadataSchema s1;
PropertyTable::Schema s2; draco::StructuralMetadataSchema s2;
s1.json.SetString("one"); s1.json.SetString("one");
s2.json.SetString("two"); s2.json.SetString("two");
a.SetPropertyTableSchema(s1); a.SetSchema(s1);
b.SetPropertyTableSchema(s2); b.SetSchema(s2);
ASSERT_FALSE(a == b); ASSERT_FALSE(a == b);
ASSERT_TRUE(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 a;
draco::StructuralMetadata b; draco::StructuralMetadata b;
a.AddPropertyTable(std::unique_ptr<PropertyTable>(new PropertyTable())); a.AddPropertyTable(std::unique_ptr<PropertyTable>(new PropertyTable()));
@ -151,7 +151,20 @@ TEST(StructuralMetadataTest, TestCompare) {
ASSERT_TRUE(a != b); 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 a;
draco::StructuralMetadata b; draco::StructuralMetadata b;
auto p1 = std::unique_ptr<PropertyTable>(new PropertyTable()); auto p1 = std::unique_ptr<PropertyTable>(new PropertyTable());
@ -163,6 +176,32 @@ TEST(StructuralMetadataTest, TestCompare) {
ASSERT_FALSE(a == b); ASSERT_FALSE(a == b);
ASSERT_TRUE(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 #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] = std::unique_ptr<PointAttribute>(new PointAttribute());
attributes_[i]->CopyFrom(*src.attributes_[i]); attributes_[i]->CopyFrom(*src.attributes_[i]);
} }
compression_enabled_ = src.compression_enabled_;
compression_options_ = src.compression_options_;
CopyMetadata(src); CopyMetadata(src);
} }
@ -106,6 +108,20 @@ const PointAttribute *PointCloud::GetAttributeByUniqueId(
return attributes_[att_id].get(); 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 { int32_t PointCloud::GetAttributeIdByUniqueId(uint32_t unique_id) const {
for (size_t att_id = 0; att_id < attributes_.size(); ++att_id) { for (size_t att_id = 0; att_id < attributes_.size(); ++att_id) {
if (attributes_[att_id]->unique_id() == unique_id) { if (attributes_[att_id]->unique_id() == unique_id) {

View File

@ -21,6 +21,10 @@
#include "draco/draco_features.h" #include "draco/draco_features.h"
#include "draco/metadata/geometry_metadata.h" #include "draco/metadata/geometry_metadata.h"
#ifdef DRACO_TRANSCODER_SUPPORTED
#include "draco/compression/draco_compression_options.h"
#endif
namespace draco { namespace draco {
// PointCloud is a collection of n-dimensional points that are described by a // 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; const PointAttribute *GetAttributeByUniqueId(uint32_t id) const;
int32_t GetAttributeIdByUniqueId(uint32_t unique_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 { int32_t num_attributes() const {
return static_cast<int32_t>(attributes_.size()); return static_cast<int32_t>(attributes_.size());
} }
@ -189,6 +199,24 @@ class PointCloud {
// cloud. // cloud.
void set_num_points(PointIndex::ValueType num) { num_points_ = num; } 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: protected:
#ifdef DRACO_TRANSCODER_SUPPORTED #ifdef DRACO_TRANSCODER_SUPPORTED
// Copies metadata from the |src| point cloud. // Copies metadata from the |src| point cloud.
@ -217,6 +245,13 @@ class PointCloud {
// in corresponding PointAttribute instances in the |attributes_| array. // in corresponding PointAttribute instances in the |attributes_| array.
PointIndex::ValueType num_points_; 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; friend struct PointCloudHasher;
}; };

View File

@ -14,6 +14,7 @@
// //
#include "draco/point_cloud/point_cloud_builder.h" #include "draco/point_cloud/point_cloud_builder.h"
#include <string>
#include <utility> #include <utility>
namespace draco { namespace draco {
@ -27,8 +28,14 @@ void PointCloudBuilder::Start(PointIndex::ValueType num_points) {
int PointCloudBuilder::AddAttribute(GeometryAttribute::Type attribute_type, int PointCloudBuilder::AddAttribute(GeometryAttribute::Type attribute_type,
int8_t num_components, DataType data_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; 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); DataTypeLength(data_type) * num_components, 0);
return point_cloud_->AddAttribute(ga, true, point_cloud_->num_points()); 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_); 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 } // namespace draco

View File

@ -50,6 +50,8 @@ class PointCloudBuilder {
int AddAttribute(GeometryAttribute::Type attribute_type, int AddAttribute(GeometryAttribute::Type attribute_type,
int8_t num_components, DataType data_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. // Sets attribute value for a specific point.
// |attribute_value| must contain data in the format specified by the // |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, void SetAttributeValuesForAllPoints(int att_id, const void *attribute_values,
int stride); 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. // Finalizes the PointCloud or returns nullptr on error.
// If |deduplicate_points| is set to true, the following happens: // If |deduplicate_points| is set to true, the following happens:
// 1. Attribute values with duplicate entries are deduplicated. // 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, builder.SetAttributeValueForPoint(intensity_att_id, i,
intensity_data_.data() + i.value()); intensity_data_.data() + i.value());
} }
#ifdef DRACO_TRANSCODER_SUPPORTED
builder.SetAttributeName(pos_att_id, "Bob");
#endif
std::unique_ptr<PointCloud> res = builder.Finalize(false); std::unique_ptr<PointCloud> res = builder.Finalize(false);
ASSERT_TRUE(res != nullptr); ASSERT_TRUE(res != nullptr);
ASSERT_EQ(res->num_points(), 10); 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) { 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 } // namespace draco

View File

@ -77,6 +77,71 @@ TEST_F(PointCloudTest, PointCloudCopy) {
ASSERT_TRUE(att_metadata_copy->GetEntryInt("attribute_test", &att_test)); ASSERT_TRUE(att_metadata_copy->GetEntryInt("attribute_test", &att_test));
ASSERT_EQ(att_test, 3); 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 #endif
TEST_F(PointCloudTest, TestAttributeDeletion) { TEST_F(PointCloudTest, TestAttributeDeletion) {

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