Updated meshoptimizer.
This commit is contained in:
parent
70c9d2d50e
commit
f450d227c0
3
3rdparty/meshoptimizer/.travis.yml
vendored
3
3rdparty/meshoptimizer/.travis.yml
vendored
@ -6,6 +6,9 @@ matrix:
|
||||
compiler: gcc
|
||||
- os: linux
|
||||
compiler: clang
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
arch: arm64
|
||||
- os: osx
|
||||
compiler: clang
|
||||
- os: windows
|
||||
|
25
3rdparty/meshoptimizer/CMakeLists.txt
vendored
25
3rdparty/meshoptimizer/CMakeLists.txt
vendored
@ -57,24 +57,27 @@ if(BUILD_TOOLS)
|
||||
target_link_libraries(gltfpack meshoptimizer)
|
||||
list(APPEND TARGETS gltfpack)
|
||||
|
||||
set_target_properties(gltfpack PROPERTIES INSTALL_RPATH "$ORIGIN/../lib")
|
||||
string(CONCAT RPATH "$ORIGIN/../" ${CMAKE_INSTALL_LIBDIR})
|
||||
set_target_properties(gltfpack PROPERTIES INSTALL_RPATH ${RPATH})
|
||||
endif()
|
||||
|
||||
install(TARGETS ${TARGETS} EXPORT meshoptimizerTargets
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
INCLUDES DESTINATION include)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
install(FILES src/meshoptimizer.h DESTINATION include)
|
||||
install(EXPORT meshoptimizerTargets DESTINATION lib/cmake/meshoptimizer NAMESPACE meshoptimizer::)
|
||||
install(TARGETS ${TARGETS} EXPORT meshoptimizerTargets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
install(FILES src/meshoptimizer.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
install(EXPORT meshoptimizerTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/meshoptimizer NAMESPACE meshoptimizer::)
|
||||
|
||||
# TARGET_PDB_FILE is available since 3.1
|
||||
if(MSVC AND NOT (CMAKE_VERSION VERSION_LESS "3.1"))
|
||||
foreach(TARGET ${TARGETS})
|
||||
get_target_property(TARGET_TYPE ${TARGET} TYPE)
|
||||
if(NOT ${TARGET_TYPE} STREQUAL "STATIC_LIBRARY")
|
||||
install(FILES $<TARGET_PDB_FILE:${TARGET}> DESTINATION bin OPTIONAL)
|
||||
install(FILES $<TARGET_PDB_FILE:${TARGET}> DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL)
|
||||
endif()
|
||||
endforeach(TARGET)
|
||||
endif()
|
||||
@ -83,11 +86,11 @@ include(CMakePackageConfigHelpers)
|
||||
|
||||
configure_package_config_file(config.cmake.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfig.cmake
|
||||
INSTALL_DESTINATION lib/cmake/meshoptimizer NO_SET_AND_CHECK_MACRO)
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/meshoptimizer NO_SET_AND_CHECK_MACRO)
|
||||
|
||||
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfigVersion.cmake COMPATIBILITY ExactVersion)
|
||||
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfig.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfigVersion.cmake
|
||||
DESTINATION lib/cmake/meshoptimizer)
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/meshoptimizer)
|
||||
|
3
3rdparty/meshoptimizer/README.md
vendored
3
3rdparty/meshoptimizer/README.md
vendored
@ -290,10 +290,11 @@ gltfpack -i scene.gltf -o scene.glb
|
||||
|
||||
gltfpack substantially changes the glTF data by optimizing the meshes for vertex fetch and transform cache, quantizing the geometry to reduce the memory consumption and size, merging meshes to reduce the draw call count, quantizing and resampling animations to reduce animation size and simplify playback, and pruning the node tree by removing or collapsing redundant nodes. It will also simplify the meshes when requested to do so.
|
||||
|
||||
gltfpack can produce two types of output files:
|
||||
gltfpack can produce three types of output files:
|
||||
|
||||
- By default gltfpack outputs regular `.glb`/`.gltf` files that have been optimized for GPU consumption using various cache optimizers and quantization. These files can be loaded by standard GLTF loaders present in frameworks such as [three.js](https://threejs.org/) (r107+) and [Babylon.js](https://www.babylonjs.com/) (4.1+).
|
||||
- When using `-c` option, gltfpack outputs compressed `.glb`/`.gltf` files that use meshoptimizer codecs to reduce the download size further. Loading these files requires extending GLTF loaders with custom decompression support; `demo/GLTFLoader.js` contains a custom version of three.js loader that can be used to load them.
|
||||
- When using `-cf` option, gltfpack outputs compressed files and an extra `.fallback.bin` file with uncompressed data. These files can be loaded by standard glTF loaders; loaders with decompression support don't need to load the fallback.
|
||||
|
||||
> Note: files produced by gltfpack use `MESHOPT_quantized_geometry` and `MESHOPT_compression` pseudo-extensions; both of these have *not* been standardized yet but eventually will be. glTF validator doesn't recognize these extensions and produces a large number of validation errors because of this.
|
||||
|
||||
|
316
3rdparty/meshoptimizer/demo/GLTFLoader.js
vendored
316
3rdparty/meshoptimizer/demo/GLTFLoader.js
vendored
@ -10,29 +10,29 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
function GLTFLoader( manager ) {
|
||||
|
||||
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
|
||||
THREE.Loader.call( this, manager );
|
||||
|
||||
this.dracoLoader = null;
|
||||
this.ddsLoader = null;
|
||||
this.meshoptDecoder = null;
|
||||
|
||||
}
|
||||
|
||||
GLTFLoader.prototype = {
|
||||
GLTFLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype ), {
|
||||
|
||||
constructor: GLTFLoader,
|
||||
|
||||
crossOrigin: 'anonymous',
|
||||
|
||||
load: function ( url, onLoad, onProgress, onError ) {
|
||||
|
||||
var scope = this;
|
||||
|
||||
var resourcePath;
|
||||
|
||||
if ( this.resourcePath !== undefined ) {
|
||||
if ( this.resourcePath !== '' ) {
|
||||
|
||||
resourcePath = this.resourcePath;
|
||||
|
||||
} else if ( this.path !== undefined ) {
|
||||
} else if ( this.path !== '' ) {
|
||||
|
||||
resourcePath = this.path;
|
||||
|
||||
@ -69,6 +69,12 @@ THREE.GLTFLoader = ( function () {
|
||||
loader.setPath( this.path );
|
||||
loader.setResponseType( 'arraybuffer' );
|
||||
|
||||
if ( scope.crossOrigin === 'use-credentials' ) {
|
||||
|
||||
loader.setWithCredentials( true );
|
||||
|
||||
}
|
||||
|
||||
loader.load( url, function ( data ) {
|
||||
|
||||
try {
|
||||
@ -91,27 +97,6 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
},
|
||||
|
||||
setCrossOrigin: function ( value ) {
|
||||
|
||||
this.crossOrigin = value;
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
setPath: function ( value ) {
|
||||
|
||||
this.path = value;
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
setResourcePath: function ( value ) {
|
||||
|
||||
this.resourcePath = value;
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
setDRACOLoader: function ( dracoLoader ) {
|
||||
|
||||
this.dracoLoader = dracoLoader;
|
||||
@ -119,6 +104,13 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
},
|
||||
|
||||
setDDSLoader: function ( ddsLoader ) {
|
||||
|
||||
this.ddsLoader = ddsLoader;
|
||||
return this;
|
||||
|
||||
},
|
||||
|
||||
setMeshoptDecoder: function ( decoder ) {
|
||||
|
||||
this.meshoptDecoder = decoder;
|
||||
@ -185,11 +177,11 @@ THREE.GLTFLoader = ( function () {
|
||||
break;
|
||||
|
||||
case EXTENSIONS.KHR_MATERIALS_UNLIT:
|
||||
extensions[ extensionName ] = new GLTFMaterialsUnlitExtension( json );
|
||||
extensions[ extensionName ] = new GLTFMaterialsUnlitExtension();
|
||||
break;
|
||||
|
||||
case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:
|
||||
extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension( json );
|
||||
extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension();
|
||||
break;
|
||||
|
||||
case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION:
|
||||
@ -197,15 +189,15 @@ THREE.GLTFLoader = ( function () {
|
||||
break;
|
||||
|
||||
case EXTENSIONS.MSFT_TEXTURE_DDS:
|
||||
extensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] = new GLTFTextureDDSExtension();
|
||||
extensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] = new GLTFTextureDDSExtension( this.ddsLoader );
|
||||
break;
|
||||
|
||||
case EXTENSIONS.KHR_TEXTURE_TRANSFORM:
|
||||
extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] = new GLTFTextureTransformExtension( json );
|
||||
extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] = new GLTFTextureTransformExtension();
|
||||
break;
|
||||
|
||||
case EXTENSIONS.MESHOPT_COMPRESSION:
|
||||
extensions[ extensionName ] = new GLTFMeshoptCompressionExtension( json, this.meshoptDecoder );
|
||||
extensions[ extensionName ] = new GLTFMeshoptCompressionExtension( this.meshoptDecoder );
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -234,7 +226,7 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
} );
|
||||
|
||||
/* GLTFREGISTRY */
|
||||
|
||||
@ -290,27 +282,26 @@ THREE.GLTFLoader = ( function () {
|
||||
/**
|
||||
* DDS Texture Extension
|
||||
*
|
||||
* Specification:
|
||||
* https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds
|
||||
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds
|
||||
*
|
||||
*/
|
||||
function GLTFTextureDDSExtension() {
|
||||
function GLTFTextureDDSExtension( ddsLoader ) {
|
||||
|
||||
if ( ! THREE.DDSLoader ) {
|
||||
if ( ! ddsLoader ) {
|
||||
|
||||
throw new Error( 'THREE.GLTFLoader: Attempting to load .dds texture without importing THREE.DDSLoader' );
|
||||
|
||||
}
|
||||
|
||||
this.name = EXTENSIONS.MSFT_TEXTURE_DDS;
|
||||
this.ddsLoader = new THREE.DDSLoader();
|
||||
this.ddsLoader = ddsLoader;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Lights Extension
|
||||
* Punctual Lights Extension
|
||||
*
|
||||
* Specification: PENDING
|
||||
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
|
||||
*/
|
||||
function GLTFLightsExtension( json ) {
|
||||
|
||||
@ -377,9 +368,9 @@ THREE.GLTFLoader = ( function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* Unlit Materials Extension (pending)
|
||||
* Unlit Materials Extension
|
||||
*
|
||||
* PR: https://github.com/KhronosGroup/glTF/pull/1163
|
||||
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
|
||||
*/
|
||||
function GLTFMaterialsUnlitExtension() {
|
||||
|
||||
@ -426,8 +417,6 @@ THREE.GLTFLoader = ( function () {
|
||||
};
|
||||
|
||||
/* BINARY EXTENSION */
|
||||
|
||||
var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF';
|
||||
var BINARY_EXTENSION_HEADER_MAGIC = 'glTF';
|
||||
var BINARY_EXTENSION_HEADER_LENGTH = 12;
|
||||
var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 };
|
||||
@ -496,7 +485,7 @@ THREE.GLTFLoader = ( function () {
|
||||
/**
|
||||
* DRACO Mesh Compression Extension
|
||||
*
|
||||
* Specification: https://github.com/KhronosGroup/glTF/pull/874
|
||||
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression
|
||||
*/
|
||||
function GLTFDracoMeshCompressionExtension( json, dracoLoader ) {
|
||||
|
||||
@ -574,7 +563,7 @@ THREE.GLTFLoader = ( function () {
|
||||
/**
|
||||
* Texture Transform Extension
|
||||
*
|
||||
* Specification:
|
||||
* Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform
|
||||
*/
|
||||
function GLTFTextureTransformExtension() {
|
||||
|
||||
@ -864,7 +853,7 @@ THREE.GLTFLoader = ( function () {
|
||||
},
|
||||
|
||||
// Here's based on refreshUniformsCommon() and refreshUniformsStandard() in WebGLRenderer.
|
||||
refreshUniforms: function ( renderer, scene, camera, geometry, material, group ) {
|
||||
refreshUniforms: function ( renderer, scene, camera, geometry, material ) {
|
||||
|
||||
if ( material.isGLTFSpecularGlossinessMaterial !== true ) {
|
||||
|
||||
@ -1008,7 +997,7 @@ THREE.GLTFLoader = ( function () {
|
||||
/**
|
||||
* meshoptimizer Compression Extension
|
||||
*/
|
||||
function GLTFMeshoptCompressionExtension( json, meshoptDecoder ) {
|
||||
function GLTFMeshoptCompressionExtension( meshoptDecoder ) {
|
||||
|
||||
if ( ! meshoptDecoder ) {
|
||||
|
||||
@ -1017,21 +1006,18 @@ THREE.GLTFLoader = ( function () {
|
||||
}
|
||||
|
||||
this.name = EXTENSIONS.MESHOPT_COMPRESSION;
|
||||
this.json = json;
|
||||
this.meshoptDecoder = meshoptDecoder;
|
||||
|
||||
}
|
||||
|
||||
GLTFMeshoptCompressionExtension.prototype.decodeBufferView = function ( bufferViewDef, buffer ) {
|
||||
GLTFMeshoptCompressionExtension.prototype.decodeBufferView = function ( extensionDef, buffer ) {
|
||||
|
||||
var decoder = this.meshoptDecoder;
|
||||
|
||||
return decoder.ready.then( function () {
|
||||
|
||||
var extensionDef = bufferViewDef.extensions[ EXTENSIONS.MESHOPT_COMPRESSION ];
|
||||
|
||||
var byteOffset = bufferViewDef.byteOffset || 0;
|
||||
var byteLength = bufferViewDef.byteLength || 0;
|
||||
var byteOffset = extensionDef.byteOffset || 0;
|
||||
var byteLength = extensionDef.byteLength || 0;
|
||||
|
||||
var count = extensionDef.count;
|
||||
var stride = extensionDef.byteStride;
|
||||
@ -1058,7 +1044,7 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/*********************************/
|
||||
/********** INTERPOLATION ********/
|
||||
@ -1167,17 +1153,6 @@ THREE.GLTFLoader = ( function () {
|
||||
UNSIGNED_SHORT: 5123
|
||||
};
|
||||
|
||||
var WEBGL_TYPE = {
|
||||
5126: Number,
|
||||
//35674: THREE.Matrix2,
|
||||
35675: THREE.Matrix3,
|
||||
35676: THREE.Matrix4,
|
||||
35664: THREE.Vector2,
|
||||
35665: THREE.Vector3,
|
||||
35666: THREE.Vector4,
|
||||
35678: THREE.Texture
|
||||
};
|
||||
|
||||
var WEBGL_COMPONENT_TYPES = {
|
||||
5120: Int8Array,
|
||||
5121: Uint8Array,
|
||||
@ -1190,10 +1165,10 @@ THREE.GLTFLoader = ( function () {
|
||||
var WEBGL_FILTERS = {
|
||||
9728: THREE.NearestFilter,
|
||||
9729: THREE.LinearFilter,
|
||||
9984: THREE.NearestMipMapNearestFilter,
|
||||
9985: THREE.LinearMipMapNearestFilter,
|
||||
9986: THREE.NearestMipMapLinearFilter,
|
||||
9987: THREE.LinearMipMapLinearFilter
|
||||
9984: THREE.NearestMipmapNearestFilter,
|
||||
9985: THREE.LinearMipmapNearestFilter,
|
||||
9986: THREE.NearestMipmapLinearFilter,
|
||||
9987: THREE.LinearMipmapLinearFilter
|
||||
};
|
||||
|
||||
var WEBGL_WRAPPINGS = {
|
||||
@ -1202,48 +1177,6 @@ THREE.GLTFLoader = ( function () {
|
||||
10497: THREE.RepeatWrapping
|
||||
};
|
||||
|
||||
var WEBGL_SIDES = {
|
||||
1028: THREE.BackSide, // Culling front
|
||||
1029: THREE.FrontSide // Culling back
|
||||
//1032: THREE.NoSide // Culling front and back, what to do?
|
||||
};
|
||||
|
||||
var WEBGL_DEPTH_FUNCS = {
|
||||
512: THREE.NeverDepth,
|
||||
513: THREE.LessDepth,
|
||||
514: THREE.EqualDepth,
|
||||
515: THREE.LessEqualDepth,
|
||||
516: THREE.GreaterEqualDepth,
|
||||
517: THREE.NotEqualDepth,
|
||||
518: THREE.GreaterEqualDepth,
|
||||
519: THREE.AlwaysDepth
|
||||
};
|
||||
|
||||
var WEBGL_BLEND_EQUATIONS = {
|
||||
32774: THREE.AddEquation,
|
||||
32778: THREE.SubtractEquation,
|
||||
32779: THREE.ReverseSubtractEquation
|
||||
};
|
||||
|
||||
var WEBGL_BLEND_FUNCS = {
|
||||
0: THREE.ZeroFactor,
|
||||
1: THREE.OneFactor,
|
||||
768: THREE.SrcColorFactor,
|
||||
769: THREE.OneMinusSrcColorFactor,
|
||||
770: THREE.SrcAlphaFactor,
|
||||
771: THREE.OneMinusSrcAlphaFactor,
|
||||
772: THREE.DstAlphaFactor,
|
||||
773: THREE.OneMinusDstAlphaFactor,
|
||||
774: THREE.DstColorFactor,
|
||||
775: THREE.OneMinusDstColorFactor,
|
||||
776: THREE.SrcAlphaSaturateFactor
|
||||
// The followings are not supported by Three.js yet
|
||||
//32769: CONSTANT_COLOR,
|
||||
//32770: ONE_MINUS_CONSTANT_COLOR,
|
||||
//32771: CONSTANT_ALPHA,
|
||||
//32772: ONE_MINUS_CONSTANT_COLOR
|
||||
};
|
||||
|
||||
var WEBGL_TYPE_SIZES = {
|
||||
'SCALAR': 1,
|
||||
'VEC2': 2,
|
||||
@ -1279,15 +1212,6 @@ THREE.GLTFLoader = ( function () {
|
||||
STEP: THREE.InterpolateDiscrete
|
||||
};
|
||||
|
||||
var STATES_ENABLES = {
|
||||
2884: 'CULL_FACE',
|
||||
2929: 'DEPTH_TEST',
|
||||
3042: 'BLEND',
|
||||
3089: 'SCISSOR_TEST',
|
||||
32823: 'POLYGON_OFFSET_FILL',
|
||||
32926: 'SAMPLE_ALPHA_TO_COVERAGE'
|
||||
};
|
||||
|
||||
var ALPHA_MODES = {
|
||||
OPAQUE: 'OPAQUE',
|
||||
MASK: 'MASK',
|
||||
@ -1306,6 +1230,13 @@ THREE.GLTFLoader = ( function () {
|
||||
// Invalid URL
|
||||
if ( typeof url !== 'string' || url === '' ) return '';
|
||||
|
||||
// Host Relative URL
|
||||
if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) {
|
||||
|
||||
path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' );
|
||||
|
||||
}
|
||||
|
||||
// Absolute URL http://,https://,//
|
||||
if ( /^(https?:)?\/\//i.test( url ) ) return url;
|
||||
|
||||
@ -1580,19 +1511,6 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
function isObjectEqual( a, b ) {
|
||||
|
||||
if ( Object.keys( a ).length !== Object.keys( b ).length ) return false;
|
||||
|
||||
for ( var key in a ) {
|
||||
|
||||
if ( a[ key ] !== b[ key ] ) return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
function createPrimitiveKey( primitiveDef ) {
|
||||
@ -1677,6 +1595,12 @@ THREE.GLTFLoader = ( function () {
|
||||
this.fileLoader = new THREE.FileLoader( this.options.manager );
|
||||
this.fileLoader.setResponseType( 'arraybuffer' );
|
||||
|
||||
if ( this.options.crossOrigin === 'use-credentials' ) {
|
||||
|
||||
this.fileLoader.setWithCredentials( true );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GLTFParser.prototype.parse = function ( onLoad, onError ) {
|
||||
@ -1711,6 +1635,8 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
addUnknownExtensionsToUserData( extensions, result, json );
|
||||
|
||||
assignExtrasToUserData( result, json );
|
||||
|
||||
onLoad( result );
|
||||
|
||||
} ).catch( onError );
|
||||
@ -1933,26 +1859,25 @@ THREE.GLTFLoader = ( function () {
|
||||
if ( bufferViewDef.extensions && bufferViewDef.extensions[ EXTENSIONS.MESHOPT_COMPRESSION ] ) {
|
||||
|
||||
var extension = this.extensions[ EXTENSIONS.MESHOPT_COMPRESSION ];
|
||||
var extensionDef = bufferViewDef.extensions[ EXTENSIONS.MESHOPT_COMPRESSION ];
|
||||
|
||||
return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) {
|
||||
return this.getDependency( 'buffer', extensionDef.buffer ).then( function ( buffer ) {
|
||||
|
||||
return extension.decodeBufferView( bufferViewDef, buffer );
|
||||
|
||||
} );
|
||||
|
||||
} else {
|
||||
|
||||
return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) {
|
||||
|
||||
var byteLength = bufferViewDef.byteLength || 0;
|
||||
var byteOffset = bufferViewDef.byteOffset || 0;
|
||||
return buffer.slice( byteOffset, byteOffset + byteLength );
|
||||
return extension.decodeBufferView( extensionDef, buffer );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) {
|
||||
|
||||
var byteLength = bufferViewDef.byteLength || 0;
|
||||
var byteOffset = bufferViewDef.byteOffset || 0;
|
||||
return buffer.slice( byteOffset, byteOffset + byteLength );
|
||||
|
||||
} );
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors
|
||||
@ -2029,7 +1954,7 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
}
|
||||
|
||||
bufferAttribute = new THREE.InterleavedBufferAttribute( ib, itemSize, (byteOffset % byteStride) / elementBytes, normalized );
|
||||
bufferAttribute = new THREE.InterleavedBufferAttribute( ib, itemSize, ( byteOffset % byteStride ) / elementBytes, normalized );
|
||||
|
||||
} else {
|
||||
|
||||
@ -2062,7 +1987,7 @@ THREE.GLTFLoader = ( function () {
|
||||
if ( bufferView !== null ) {
|
||||
|
||||
// Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes.
|
||||
bufferAttribute.setArray( bufferAttribute.array.slice() );
|
||||
bufferAttribute = new THREE.BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized );
|
||||
|
||||
}
|
||||
|
||||
@ -2138,7 +2063,7 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
// Load Texture resource.
|
||||
|
||||
var loader = THREE.Loader.Handlers.get( sourceURI );
|
||||
var loader = options.manager.getHandler( sourceURI );
|
||||
|
||||
if ( ! loader ) {
|
||||
|
||||
@ -2179,7 +2104,7 @@ THREE.GLTFLoader = ( function () {
|
||||
var sampler = samplers[ textureDef.sampler ] || {};
|
||||
|
||||
texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter;
|
||||
texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.LinearMipMapLinearFilter;
|
||||
texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.LinearMipmapLinearFilter;
|
||||
texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping;
|
||||
texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping;
|
||||
|
||||
@ -2269,7 +2194,6 @@ THREE.GLTFLoader = ( function () {
|
||||
THREE.Material.prototype.copy.call( pointsMaterial, material );
|
||||
pointsMaterial.color.copy( material.color );
|
||||
pointsMaterial.map = material.map;
|
||||
pointsMaterial.lights = false; // PointsMaterial doesn't support lights yet
|
||||
pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px
|
||||
|
||||
this.cache.add( cacheKey, pointsMaterial );
|
||||
@ -2289,7 +2213,6 @@ THREE.GLTFLoader = ( function () {
|
||||
lineMaterial = new THREE.LineBasicMaterial();
|
||||
THREE.Material.prototype.copy.call( lineMaterial, material );
|
||||
lineMaterial.color.copy( material.color );
|
||||
lineMaterial.lights = false; // LineBasicMaterial doesn't support lights yet
|
||||
|
||||
this.cache.add( cacheKey, lineMaterial );
|
||||
|
||||
@ -2654,7 +2577,6 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
var parser = this;
|
||||
var json = this.json;
|
||||
var extensions = this.extensions;
|
||||
|
||||
var meshDef = json.meshes[ meshIndex ];
|
||||
var primitives = meshDef.primitives;
|
||||
@ -2698,7 +2620,7 @@ THREE.GLTFLoader = ( function () {
|
||||
? new THREE.SkinnedMesh( geometry, material )
|
||||
: new THREE.Mesh( geometry, material );
|
||||
|
||||
if ( mesh.isSkinnedMesh === true && !mesh.geometry.attributes.skinWeight.normalized ) {
|
||||
if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) {
|
||||
|
||||
// we normalize floating point skin weight array to fix malformed assets (see #15319)
|
||||
// it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs
|
||||
@ -2986,7 +2908,7 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
for ( var j = 0, jl = outputArray.length; j < jl; j ++ ) {
|
||||
|
||||
scaled[j] = outputArray[j] * scale;
|
||||
scaled[ j ] = outputArray[ j ] * scale;
|
||||
|
||||
}
|
||||
|
||||
@ -3053,14 +2975,11 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
return ( function () {
|
||||
|
||||
// .isBone isn't in glTF spec. See .markDefs
|
||||
if ( nodeDef.isBone === true ) {
|
||||
var pending = [];
|
||||
|
||||
return Promise.resolve( new THREE.Bone() );
|
||||
if ( nodeDef.mesh !== undefined ) {
|
||||
|
||||
} else if ( nodeDef.mesh !== undefined ) {
|
||||
|
||||
return parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) {
|
||||
pending.push( parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) {
|
||||
|
||||
var node;
|
||||
|
||||
@ -3106,25 +3025,58 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
return node;
|
||||
|
||||
} );
|
||||
|
||||
} else if ( nodeDef.camera !== undefined ) {
|
||||
|
||||
return parser.getDependency( 'camera', nodeDef.camera );
|
||||
|
||||
} else if ( nodeDef.extensions
|
||||
&& nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ]
|
||||
&& nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light !== undefined ) {
|
||||
|
||||
return parser.getDependency( 'light', nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light );
|
||||
|
||||
} else {
|
||||
|
||||
return Promise.resolve( new THREE.Object3D() );
|
||||
} ) );
|
||||
|
||||
}
|
||||
|
||||
}() ).then( function ( node ) {
|
||||
if ( nodeDef.camera !== undefined ) {
|
||||
|
||||
pending.push( parser.getDependency( 'camera', nodeDef.camera ) );
|
||||
|
||||
}
|
||||
|
||||
if ( nodeDef.extensions
|
||||
&& nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ]
|
||||
&& nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light !== undefined ) {
|
||||
|
||||
pending.push( parser.getDependency( 'light', nodeDef.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].light ) );
|
||||
|
||||
}
|
||||
|
||||
return Promise.all( pending );
|
||||
|
||||
}() ).then( function ( objects ) {
|
||||
|
||||
var node;
|
||||
|
||||
// .isBone isn't in glTF spec. See .markDefs
|
||||
if ( nodeDef.isBone === true ) {
|
||||
|
||||
node = new THREE.Bone();
|
||||
|
||||
} else if ( objects.length > 1 ) {
|
||||
|
||||
node = new THREE.Group();
|
||||
|
||||
} else if ( objects.length === 1 ) {
|
||||
|
||||
node = objects[ 0 ];
|
||||
|
||||
} else {
|
||||
|
||||
node = new THREE.Object3D();
|
||||
|
||||
}
|
||||
|
||||
if ( node !== objects[ 0 ] ) {
|
||||
|
||||
for ( var i = 0, il = objects.length; i < il; i ++ ) {
|
||||
|
||||
node.add( objects[ i ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( nodeDef.name !== undefined ) {
|
||||
|
||||
@ -3208,11 +3160,9 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
} ).then( function ( jointNodes ) {
|
||||
|
||||
var meshes = node.isGroup === true ? node.children : [ node ];
|
||||
node.traverse( function ( mesh ) {
|
||||
|
||||
for ( var i = 0, il = meshes.length; i < il; i ++ ) {
|
||||
|
||||
var mesh = meshes[ i ];
|
||||
if ( ! mesh.isMesh ) return;
|
||||
|
||||
var bones = [];
|
||||
var boneInverses = [];
|
||||
@ -3245,7 +3195,7 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
mesh.bind( new THREE.Skeleton( bones, boneInverses ), mesh.matrixWorld );
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
return node;
|
||||
|
||||
|
@ -1,50 +1,50 @@
|
||||
/* Babylon.js extension for MESHOPT_compression; requires Babylon.js 4.1 */
|
||||
var NAME = "MESHOPT_compression";
|
||||
var MESHOPT_compression = /** @class */ (function () {
|
||||
/** @hidden */
|
||||
function MESHOPT_compression(loader, decoder) {
|
||||
/** The name of this extension. */
|
||||
this.name = NAME;
|
||||
/** Defines whether this extension is enabled. */
|
||||
this.enabled = true;
|
||||
this._loader = loader;
|
||||
this._decoder = decoder;
|
||||
}
|
||||
/** @hidden */
|
||||
MESHOPT_compression.prototype.dispose = function () {
|
||||
delete this._loader;
|
||||
};
|
||||
/** @hidden */
|
||||
MESHOPT_compression.prototype.loadBufferViewAsync = function (context, bufferView) {
|
||||
if (bufferView.extensions && bufferView.extensions[NAME]) {
|
||||
if (bufferView._decoded) {
|
||||
return bufferView._decoded;
|
||||
}
|
||||
var view = this._loader.loadBufferViewAsync(context, bufferView);
|
||||
var decoder = this._decoder;
|
||||
bufferView._decoded = Promise.all([view, decoder.ready]).then(function (res) {
|
||||
var source = res[0];
|
||||
var extensionDef = bufferView.extensions[NAME];
|
||||
var count = extensionDef.count;
|
||||
var stride = extensionDef.byteStride;
|
||||
var result = new Uint8Array(new ArrayBuffer(count * stride));
|
||||
switch (extensionDef.mode) {
|
||||
case 0:
|
||||
decoder.decodeVertexBuffer(result, count, stride, source);
|
||||
break;
|
||||
case 1:
|
||||
decoder.decodeIndexBuffer(result, count, stride, source);
|
||||
break;
|
||||
default:
|
||||
throw new Error("GLTFLoader: Unrecognized meshopt compression mode.");
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
});
|
||||
return bufferView._decoded;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return MESHOPT_compression;
|
||||
}());
|
||||
/* BABYLON.GLTF2.GLTFLoader.RegisterExtension("MESHOPT_compression", (loader) => new MESHOPT_compression(loader, MeshoptDecoder)); */
|
||||
/* Babylon.js extension for MESHOPT_compression; requires Babylon.js 4.1 */
|
||||
var NAME = "MESHOPT_compression";
|
||||
var MESHOPT_compression = /** @class */ (function () {
|
||||
/** @hidden */
|
||||
function MESHOPT_compression(loader, decoder) {
|
||||
/** The name of this extension. */
|
||||
this.name = NAME;
|
||||
/** Defines whether this extension is enabled. */
|
||||
this.enabled = true;
|
||||
this._loader = loader;
|
||||
this._decoder = decoder;
|
||||
}
|
||||
/** @hidden */
|
||||
MESHOPT_compression.prototype.dispose = function () {
|
||||
delete this._loader;
|
||||
};
|
||||
/** @hidden */
|
||||
MESHOPT_compression.prototype.loadBufferViewAsync = function (context, bufferView) {
|
||||
if (bufferView.extensions && bufferView.extensions[NAME]) {
|
||||
var extensionDef = bufferView.extensions[NAME];
|
||||
if (extensionDef._decoded) {
|
||||
return extensionDef._decoded;
|
||||
}
|
||||
var view = this._loader.loadBufferViewAsync(context, extensionDef);
|
||||
var decoder = this._decoder;
|
||||
extensionDef._decoded = Promise.all([view, decoder.ready]).then(function (res) {
|
||||
var source = res[0];
|
||||
var count = extensionDef.count;
|
||||
var stride = extensionDef.byteStride;
|
||||
var result = new Uint8Array(new ArrayBuffer(count * stride));
|
||||
switch (extensionDef.mode) {
|
||||
case 0:
|
||||
decoder.decodeVertexBuffer(result, count, stride, source);
|
||||
break;
|
||||
case 1:
|
||||
decoder.decodeIndexBuffer(result, count, stride, source);
|
||||
break;
|
||||
default:
|
||||
throw new Error("GLTFLoader: Unrecognized meshopt compression mode.");
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
});
|
||||
return extensionDef._decoded;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return MESHOPT_compression;
|
||||
}());
|
||||
/* BABYLON.GLTF2.GLTFLoader.RegisterExtension("MESHOPT_compression", (loader) => new MESHOPT_compression(loader, MeshoptDecoder)); */
|
||||
|
138
3rdparty/meshoptimizer/demo/demo.html
vendored
138
3rdparty/meshoptimizer/demo/demo.html
vendored
@ -1,70 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>meshoptimizer - demo</title>
|
||||
|
||||
<script src="https://preview.babylonjs.com/babylon.js"></script>
|
||||
<script src="https://preview.babylonjs.com/loaders/babylon.glTF2FileLoader.js"></script>
|
||||
|
||||
<script src="babylon.MESHOPT_compression.js"></script>
|
||||
<script src="../js/meshopt_decoder.js"></script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#renderCanvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
touch-action: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="renderCanvas"></canvas>
|
||||
<script>
|
||||
var canvas = document.getElementById("renderCanvas");
|
||||
|
||||
BABYLON.GLTF2.GLTFLoader.RegisterExtension("MESHOPT_compression", (loader) => new MESHOPT_compression(loader, MeshoptDecoder));
|
||||
|
||||
var createScene = function () {
|
||||
var scene = new BABYLON.Scene(engine);
|
||||
|
||||
var light = new BABYLON.HemisphericLight();
|
||||
|
||||
var camera = new BABYLON.ArcRotateCamera("Camera", 0, 0.8, 10, BABYLON.Vector3.Zero(), scene);
|
||||
camera.attachControl(canvas, false);
|
||||
|
||||
BABYLON.SceneLoader.Append("", "pirate.glb", scene, function (newMeshes) {
|
||||
scene.activeCamera = null;
|
||||
scene.createDefaultCameraOrLight(true);
|
||||
scene.activeCamera.attachControl(canvas, false);
|
||||
scene.activeCamera.alpha = Math.PI / 2;
|
||||
});
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
var engine = new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true });
|
||||
var scene = createScene();
|
||||
|
||||
engine.runRenderLoop(function () {
|
||||
if (scene) {
|
||||
scene.render();
|
||||
}
|
||||
});
|
||||
|
||||
// Resize
|
||||
window.addEventListener("resize", function () {
|
||||
engine.resize();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
|
||||
<title>meshoptimizer - demo</title>
|
||||
|
||||
<script src="https://preview.babylonjs.com/babylon.js"></script>
|
||||
<script src="https://preview.babylonjs.com/loaders/babylon.glTF2FileLoader.js"></script>
|
||||
|
||||
<script src="babylon.MESHOPT_compression.js"></script>
|
||||
<script src="../js/meshopt_decoder.js"></script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#renderCanvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
touch-action: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="renderCanvas"></canvas>
|
||||
<script>
|
||||
var canvas = document.getElementById("renderCanvas");
|
||||
|
||||
BABYLON.GLTF2.GLTFLoader.RegisterExtension("MESHOPT_compression", (loader) => new MESHOPT_compression(loader, MeshoptDecoder));
|
||||
|
||||
var createScene = function () {
|
||||
var scene = new BABYLON.Scene(engine);
|
||||
|
||||
var camera = new BABYLON.ArcRotateCamera("Camera", 0, 0.8, 10, BABYLON.Vector3.Zero(), scene);
|
||||
camera.attachControl(canvas, false);
|
||||
|
||||
BABYLON.SceneLoader.Append("", "pirate.glb", scene, function (newMeshes) {
|
||||
scene.activeCamera = null;
|
||||
scene.createDefaultCameraOrLight(true);
|
||||
scene.activeCamera.attachControl(canvas, false);
|
||||
scene.activeCamera.alpha = Math.PI / 2;
|
||||
});
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
var engine = new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true });
|
||||
var scene = createScene();
|
||||
|
||||
engine.runRenderLoop(function () {
|
||||
if (scene) {
|
||||
scene.render();
|
||||
}
|
||||
});
|
||||
|
||||
// Resize
|
||||
window.addEventListener("resize", function () {
|
||||
engine.resize();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
6
3rdparty/meshoptimizer/demo/index.html
vendored
6
3rdparty/meshoptimizer/demo/index.html
vendored
@ -30,7 +30,7 @@
|
||||
<a href="https://github.com/zeux/meshoptimizer" target="_blank" rel="noopener">meshoptimizer</a>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js"></script>
|
||||
|
||||
<script src="../js/meshopt_decoder.js"></script>
|
||||
<script src="GLTFLoader.js"></script>
|
||||
@ -71,8 +71,8 @@
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
var loader = new THREE.GLTFLoader()
|
||||
loader.setMeshoptDecoder(MeshoptDecoder)
|
||||
var loader = new THREE.GLTFLoader();
|
||||
loader.setMeshoptDecoder(MeshoptDecoder);
|
||||
loader.load('pirate.glb', function (gltf) {
|
||||
var bbox = new THREE.Box3().setFromObject(gltf.scene);
|
||||
var scale = 2 / (bbox.max.y - bbox.min.y);
|
||||
|
BIN
3rdparty/meshoptimizer/demo/pirate.glb
vendored
BIN
3rdparty/meshoptimizer/demo/pirate.glb
vendored
Binary file not shown.
33
3rdparty/meshoptimizer/js/meshopt_decoder.js
vendored
33
3rdparty/meshoptimizer/js/meshopt_decoder.js
vendored
File diff suppressed because one or more lines are too long
2
3rdparty/meshoptimizer/src/meshoptimizer.h
vendored
2
3rdparty/meshoptimizer/src/meshoptimizer.h
vendored
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* meshoptimizer - version 0.11
|
||||
* meshoptimizer - version 0.12
|
||||
*
|
||||
* Copyright (C) 2016-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
|
||||
* Report bugs and download new versions at https://github.com/zeux/meshoptimizer
|
||||
|
78
3rdparty/meshoptimizer/tools/cgltf.h
vendored
78
3rdparty/meshoptimizer/tools/cgltf.h
vendored
@ -58,7 +58,16 @@
|
||||
* `cgltf_node_transform_world` calls `cgltf_node_transform_local` on every ancestor in order
|
||||
* to compute the root-to-node transformation.
|
||||
*
|
||||
* `cgltf_accessor_read_float` reads a certain element from an accessor and converts it to
|
||||
* `cgltf_accessor_unpack_floats` reads in the data from an accessor, applies sparse data (if any),
|
||||
* and converts them to floating point. Assumes that `cgltf_load_buffers` has already been called.
|
||||
* By passing null for the output pointer, users can find out how many floats are required in the
|
||||
* output buffer.
|
||||
*
|
||||
* `cgltf_accessor_num_components` is a tiny utility that tells you the dimensionality of
|
||||
* a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate
|
||||
* the necessary amount of memory.
|
||||
*
|
||||
* `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to
|
||||
* floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element
|
||||
* size is the number of floats in the output buffer, which should be in the range [1, 16]. Returns
|
||||
* false if the passed-in element_size is too small, or if the accessor is sparse.
|
||||
@ -67,7 +76,7 @@
|
||||
* and only works with single-component data types.
|
||||
*
|
||||
* `cgltf_result cgltf_copy_extras_json(const cgltf_data*, const cgltf_extras*,
|
||||
* char* dest, cgltf_size* dest_size)` allows to retrieve the "extras" data that
|
||||
* char* dest, cgltf_size* dest_size)` allows users to retrieve the "extras" data that
|
||||
* can be attached to many glTF objects (which can be arbitrary JSON data). The
|
||||
* `cgltf_extras` struct stores the offsets of the start and end of the extras JSON data
|
||||
* as it appears in the complete glTF JSON data. This function copies the extras data
|
||||
@ -267,7 +276,7 @@ typedef struct cgltf_attribute
|
||||
cgltf_accessor* data;
|
||||
} cgltf_attribute;
|
||||
|
||||
typedef struct cgltf_image
|
||||
typedef struct cgltf_image
|
||||
{
|
||||
char* name;
|
||||
char* uri;
|
||||
@ -302,7 +311,7 @@ typedef struct cgltf_texture_transform
|
||||
} cgltf_texture_transform;
|
||||
|
||||
typedef struct cgltf_texture_view
|
||||
{
|
||||
{
|
||||
cgltf_texture* texture;
|
||||
cgltf_int texcoord;
|
||||
cgltf_float scale; /* equivalent to strength for occlusion_texture */
|
||||
@ -573,8 +582,7 @@ cgltf_result cgltf_load_buffers(
|
||||
|
||||
cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data);
|
||||
|
||||
cgltf_result cgltf_validate(
|
||||
cgltf_data* data);
|
||||
cgltf_result cgltf_validate(cgltf_data* data);
|
||||
|
||||
void cgltf_free(cgltf_data* data);
|
||||
|
||||
@ -584,6 +592,10 @@ void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix)
|
||||
cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size);
|
||||
cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index);
|
||||
|
||||
cgltf_size cgltf_num_components(cgltf_type type);
|
||||
|
||||
cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count);
|
||||
|
||||
cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
@ -1563,7 +1575,6 @@ static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_ty
|
||||
return (cgltf_float)cgltf_component_read_index(in, component_type);
|
||||
}
|
||||
|
||||
static cgltf_size cgltf_num_components(cgltf_type type);
|
||||
static cgltf_size cgltf_component_size(cgltf_component_type component_type);
|
||||
|
||||
static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out, cgltf_size element_size)
|
||||
@ -1622,20 +1633,65 @@ static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type ty
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size)
|
||||
{
|
||||
if (accessor->is_sparse || accessor->buffer_view == NULL)
|
||||
if (accessor->is_sparse)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (accessor->buffer_view == NULL)
|
||||
{
|
||||
memset(out, 0, element_size * sizeof(cgltf_float));
|
||||
return 1;
|
||||
}
|
||||
cgltf_size offset = accessor->offset + accessor->buffer_view->offset;
|
||||
const uint8_t* element = (const uint8_t*) accessor->buffer_view->buffer->data;
|
||||
element += offset + accessor->stride * index;
|
||||
return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size);
|
||||
}
|
||||
|
||||
cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count)
|
||||
{
|
||||
cgltf_size floats_per_element = cgltf_num_components(accessor->type);
|
||||
cgltf_size available_floats = accessor->count * floats_per_element;
|
||||
if (out == NULL)
|
||||
{
|
||||
return available_floats;
|
||||
}
|
||||
|
||||
float_count = available_floats < float_count ? available_floats : float_count;
|
||||
cgltf_size element_count = float_count / floats_per_element;
|
||||
|
||||
// First pass: convert each element in the base accessor.
|
||||
cgltf_float* dest = out;
|
||||
cgltf_accessor dense = *accessor;
|
||||
dense.is_sparse = 0;
|
||||
for (cgltf_size index = 0; index < element_count; index++, dest += floats_per_element)
|
||||
{
|
||||
cgltf_accessor_read_float(&dense, index, dest, floats_per_element);
|
||||
}
|
||||
|
||||
// Second pass: write out each element in the sparse accessor.
|
||||
if (accessor->is_sparse)
|
||||
{
|
||||
const cgltf_accessor_sparse* sparse = &dense.sparse;
|
||||
const uint8_t* index_data = (const uint8_t*) sparse->indices_buffer_view->buffer->data;
|
||||
index_data += sparse->indices_byte_offset + sparse->indices_buffer_view->offset;
|
||||
cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type);
|
||||
const uint8_t* reader_head = (const uint8_t*) sparse->values_buffer_view->buffer->data;
|
||||
reader_head += sparse->values_byte_offset + sparse->values_buffer_view->offset;
|
||||
for (cgltf_size reader_index = 0; reader_index < sparse->count; reader_index++, index_data += index_stride)
|
||||
{
|
||||
size_t writer_index = cgltf_component_read_index(index_data, sparse->indices_component_type);
|
||||
float* writer_head = out + writer_index * floats_per_element;
|
||||
cgltf_element_read_float(reader_head, dense.type, dense.component_type, dense.normalized, writer_head, floats_per_element);
|
||||
reader_head += dense.stride;
|
||||
}
|
||||
}
|
||||
|
||||
return element_count * floats_per_element;
|
||||
}
|
||||
|
||||
cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index)
|
||||
{
|
||||
if (accessor->buffer_view)
|
||||
@ -3996,7 +4052,7 @@ static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* token
|
||||
return i;
|
||||
}
|
||||
|
||||
static cgltf_size cgltf_num_components(cgltf_type type) {
|
||||
cgltf_size cgltf_num_components(cgltf_type type) {
|
||||
switch (type)
|
||||
{
|
||||
case cgltf_type_vec2:
|
||||
|
389
3rdparty/meshoptimizer/tools/gltfpack.cpp
vendored
389
3rdparty/meshoptimizer/tools/gltfpack.cpp
vendored
@ -84,6 +84,8 @@ struct Settings
|
||||
bool simplify_aggressive;
|
||||
|
||||
bool compress;
|
||||
bool fallback;
|
||||
|
||||
int verbose;
|
||||
};
|
||||
|
||||
@ -180,6 +182,30 @@ cgltf_accessor* getAccessor(const cgltf_attribute* attributes, size_t attribute_
|
||||
return 0;
|
||||
}
|
||||
|
||||
void readAccessor(std::vector<float>& data, const cgltf_accessor* accessor)
|
||||
{
|
||||
assert(accessor->type == cgltf_type_scalar);
|
||||
|
||||
data.resize(accessor->count);
|
||||
cgltf_accessor_unpack_floats(accessor, &data[0], data.size());
|
||||
}
|
||||
|
||||
void readAccessor(std::vector<Attr>& data, const cgltf_accessor* accessor)
|
||||
{
|
||||
size_t components = cgltf_num_components(accessor->type);
|
||||
|
||||
std::vector<float> temp(accessor->count * components);
|
||||
cgltf_accessor_unpack_floats(accessor, &temp[0], temp.size());
|
||||
|
||||
data.resize(accessor->count);
|
||||
|
||||
for (size_t i = 0; i < accessor->count; ++i)
|
||||
{
|
||||
for (size_t k = 0; k < components && k < 4; ++k)
|
||||
data[i].f[k] = temp[i * components + k];
|
||||
}
|
||||
}
|
||||
|
||||
void transformPosition(float* ptr, const float* transform)
|
||||
{
|
||||
float x = ptr[0] * transform[0] + ptr[1] * transform[4] + ptr[2] * transform[8] + transform[12];
|
||||
@ -291,10 +317,7 @@ void parseMeshesGltf(cgltf_data* data, std::vector<Mesh>& meshes)
|
||||
}
|
||||
|
||||
Stream s = {attr.type, attr.index};
|
||||
s.data.resize(attr.data->count);
|
||||
|
||||
for (size_t i = 0; i < attr.data->count; ++i)
|
||||
cgltf_accessor_read_float(attr.data, i, s.data[i].f, 4);
|
||||
readAccessor(s.data, attr.data);
|
||||
|
||||
result.streams.push_back(s);
|
||||
}
|
||||
@ -314,10 +337,7 @@ void parseMeshesGltf(cgltf_data* data, std::vector<Mesh>& meshes)
|
||||
}
|
||||
|
||||
Stream s = {attr.type, attr.index, int(ti + 1)};
|
||||
s.data.resize(attr.data->count);
|
||||
|
||||
for (size_t i = 0; i < attr.data->count; ++i)
|
||||
cgltf_accessor_read_float(attr.data, i, s.data[i].f, 4);
|
||||
readAccessor(s.data, attr.data);
|
||||
|
||||
result.streams.push_back(s);
|
||||
}
|
||||
@ -821,10 +841,10 @@ void reindexMesh(Mesh& mesh)
|
||||
}
|
||||
}
|
||||
|
||||
const Stream* getPositionStream(const Mesh& mesh)
|
||||
Stream* getStream(Mesh& mesh, cgltf_attribute_type type)
|
||||
{
|
||||
for (size_t i = 0; i < mesh.streams.size(); ++i)
|
||||
if (mesh.streams[i].type == cgltf_attribute_type_position)
|
||||
if (mesh.streams[i].type == type)
|
||||
return &mesh.streams[i];
|
||||
|
||||
return 0;
|
||||
@ -835,7 +855,7 @@ void simplifyMesh(Mesh& mesh, float threshold, bool aggressive)
|
||||
if (threshold >= 1)
|
||||
return;
|
||||
|
||||
const Stream* positions = getPositionStream(mesh);
|
||||
const Stream* positions = getStream(mesh, cgltf_attribute_type_position);
|
||||
if (!positions)
|
||||
return;
|
||||
|
||||
@ -883,12 +903,59 @@ void optimizeMesh(Mesh& mesh)
|
||||
}
|
||||
}
|
||||
|
||||
struct BoneInfluence
|
||||
{
|
||||
float i;
|
||||
float w;
|
||||
|
||||
bool operator<(const BoneInfluence& other) const
|
||||
{
|
||||
return i < other.i;
|
||||
}
|
||||
};
|
||||
|
||||
void sortBoneInfluences(Mesh& mesh)
|
||||
{
|
||||
Stream* joints = getStream(mesh, cgltf_attribute_type_joints);
|
||||
Stream* weights = getStream(mesh, cgltf_attribute_type_weights);
|
||||
|
||||
if (!joints || !weights)
|
||||
return;
|
||||
|
||||
size_t vertex_count = mesh.streams[0].data.size();
|
||||
|
||||
for (size_t i = 0; i < vertex_count; ++i)
|
||||
{
|
||||
Attr& ja = joints->data[i];
|
||||
Attr& wa = weights->data[i];
|
||||
|
||||
BoneInfluence inf[4] = {};
|
||||
int count = 0;
|
||||
|
||||
for (int k = 0; k < 4; ++k)
|
||||
if (wa.f[k] > 0.f)
|
||||
{
|
||||
inf[count].i = ja.f[k];
|
||||
inf[count].w = wa.f[k];
|
||||
count++;
|
||||
}
|
||||
|
||||
std::sort(inf, inf + count);
|
||||
|
||||
for (int k = 0; k < 4; ++k)
|
||||
{
|
||||
ja.f[k] = inf[k].i;
|
||||
wa.f[k] = inf[k].w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void simplifyPointMesh(Mesh& mesh, float threshold)
|
||||
{
|
||||
if (threshold >= 1)
|
||||
return;
|
||||
|
||||
const Stream* positions = getPositionStream(mesh);
|
||||
const Stream* positions = getStream(mesh, cgltf_attribute_type_position);
|
||||
if (!positions)
|
||||
return;
|
||||
|
||||
@ -919,7 +986,7 @@ void simplifyPointMesh(Mesh& mesh, float threshold)
|
||||
|
||||
void sortPointMesh(Mesh& mesh)
|
||||
{
|
||||
const Stream* positions = getPositionStream(mesh);
|
||||
const Stream* positions = getStream(mesh, cgltf_attribute_type_position);
|
||||
if (!positions)
|
||||
return;
|
||||
|
||||
@ -1870,13 +1937,20 @@ size_t getBufferView(std::vector<BufferView>& views, BufferView::Kind kind, int
|
||||
return views.size() - 1;
|
||||
}
|
||||
|
||||
void writeBufferView(std::string& json, BufferView::Kind kind, size_t count, size_t stride, size_t bin_offset, size_t bin_size, int compression)
|
||||
void writeBufferView(std::string& json, BufferView::Kind kind, size_t count, size_t stride, size_t bin_offset, size_t bin_size, int compression, size_t compressed_offset, size_t compressed_size)
|
||||
{
|
||||
append(json, "{\"buffer\":0");
|
||||
append(json, ",\"byteLength\":");
|
||||
append(json, bin_size);
|
||||
assert(bin_size == count * stride);
|
||||
|
||||
// when compression is enabled, we store uncompressed data in buffer 1 and compressed data in buffer 0
|
||||
// when compression is disabled, we store uncompressed data in buffer 0
|
||||
size_t buffer = compression >= 0 ? 1 : 0;
|
||||
|
||||
append(json, "{\"buffer\":");
|
||||
append(json, buffer);
|
||||
append(json, ",\"byteOffset\":");
|
||||
append(json, bin_offset);
|
||||
append(json, ",\"byteLength\":");
|
||||
append(json, bin_size);
|
||||
if (kind == BufferView::Kind_Vertex)
|
||||
{
|
||||
append(json, ",\"byteStride\":");
|
||||
@ -1891,12 +1965,17 @@ void writeBufferView(std::string& json, BufferView::Kind kind, size_t count, siz
|
||||
{
|
||||
append(json, ",\"extensions\":{");
|
||||
append(json, "\"MESHOPT_compression\":{");
|
||||
append(json, "\"mode\":");
|
||||
append(json, "\"buffer\":0");
|
||||
append(json, ",\"byteOffset\":");
|
||||
append(json, size_t(compressed_offset));
|
||||
append(json, ",\"byteLength\":");
|
||||
append(json, size_t(compressed_size));
|
||||
append(json, ",\"byteStride\":");
|
||||
append(json, stride);
|
||||
append(json, ",\"mode\":");
|
||||
append(json, size_t(compression));
|
||||
append(json, ",\"count\":");
|
||||
append(json, count);
|
||||
append(json, ",\"byteStride\":");
|
||||
append(json, stride);
|
||||
append(json, "}}");
|
||||
}
|
||||
append(json, "}");
|
||||
@ -1961,7 +2040,7 @@ float getDelta(const Attr& l, const Attr& r, cgltf_animation_path_type type)
|
||||
}
|
||||
}
|
||||
|
||||
bool isTrackConstant(const cgltf_animation_sampler& sampler, cgltf_animation_path_type type, cgltf_node* target_node)
|
||||
bool isTrackConstant(const cgltf_animation_sampler& sampler, cgltf_animation_path_type type, cgltf_node* target_node, Attr* out_first = 0)
|
||||
{
|
||||
const float tolerance = 1e-3f;
|
||||
|
||||
@ -1972,15 +2051,16 @@ bool isTrackConstant(const cgltf_animation_sampler& sampler, cgltf_animation_pat
|
||||
|
||||
assert(sampler.input->count * value_stride * components == sampler.output->count);
|
||||
|
||||
std::vector<Attr> output;
|
||||
readAccessor(output, sampler.output);
|
||||
|
||||
for (size_t j = 0; j < components; ++j)
|
||||
{
|
||||
Attr first = {};
|
||||
cgltf_accessor_read_float(sampler.output, j * value_stride + value_offset, first.f, 4);
|
||||
Attr first = output[j * value_stride + value_offset];
|
||||
|
||||
for (size_t i = 1; i < sampler.input->count; ++i)
|
||||
{
|
||||
Attr attr = {};
|
||||
cgltf_accessor_read_float(sampler.output, (i * components + j) * value_stride + value_offset, attr.f, 4);
|
||||
const Attr& attr = output[(i * components + j) * value_stride + value_offset];
|
||||
|
||||
if (getDelta(first, attr, type) > tolerance)
|
||||
return false;
|
||||
@ -1992,8 +2072,7 @@ bool isTrackConstant(const cgltf_animation_sampler& sampler, cgltf_animation_pat
|
||||
{
|
||||
for (int k = 0; k < 2; ++k)
|
||||
{
|
||||
Attr t = {};
|
||||
cgltf_accessor_read_float(sampler.output, (i * components + j) * 3 + k * 2, t.f, 4);
|
||||
const Attr& t = output[(i * components + j) * 3 + k * 2];
|
||||
|
||||
float error = fabsf(t.f[0]) + fabsf(t.f[1]) + fabsf(t.f[2]) + fabsf(t.f[3]);
|
||||
|
||||
@ -2004,6 +2083,9 @@ bool isTrackConstant(const cgltf_animation_sampler& sampler, cgltf_animation_pat
|
||||
}
|
||||
}
|
||||
|
||||
if (out_first)
|
||||
*out_first = output[value_offset];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2093,6 +2175,11 @@ void resampleKeyframes(std::vector<Attr>& data, const cgltf_animation_sampler& s
|
||||
{
|
||||
size_t components = (type == cgltf_animation_path_type_weights) ? target_node->mesh->primitives[0].targets_count : 1;
|
||||
|
||||
std::vector<float> input;
|
||||
readAccessor(input, sampler.input);
|
||||
std::vector<Attr> output;
|
||||
readAccessor(output, sampler.output);
|
||||
|
||||
size_t cursor = 0;
|
||||
|
||||
for (int i = 0; i < frames; ++i)
|
||||
@ -2101,8 +2188,7 @@ void resampleKeyframes(std::vector<Attr>& data, const cgltf_animation_sampler& s
|
||||
|
||||
while (cursor + 1 < sampler.input->count)
|
||||
{
|
||||
float next_time = 0;
|
||||
cgltf_accessor_read_float(sampler.input, cursor + 1, &next_time, 1);
|
||||
float next_time = input[cursor + 1];
|
||||
|
||||
if (next_time > time)
|
||||
break;
|
||||
@ -2112,10 +2198,8 @@ void resampleKeyframes(std::vector<Attr>& data, const cgltf_animation_sampler& s
|
||||
|
||||
if (cursor + 1 < sampler.input->count)
|
||||
{
|
||||
float cursor_time = 0;
|
||||
float next_time = 0;
|
||||
cgltf_accessor_read_float(sampler.input, cursor + 0, &cursor_time, 1);
|
||||
cgltf_accessor_read_float(sampler.input, cursor + 1, &next_time, 1);
|
||||
float cursor_time = input[cursor + 0];
|
||||
float next_time = input[cursor + 1];
|
||||
|
||||
float range = next_time - cursor_time;
|
||||
float inv_range = (range == 0.f) ? 0.f : 1.f / (next_time - cursor_time);
|
||||
@ -2127,32 +2211,25 @@ void resampleKeyframes(std::vector<Attr>& data, const cgltf_animation_sampler& s
|
||||
{
|
||||
case cgltf_interpolation_type_linear:
|
||||
{
|
||||
Attr v0 = {};
|
||||
Attr v1 = {};
|
||||
cgltf_accessor_read_float(sampler.output, (cursor + 0) * components + j, v0.f, 4);
|
||||
cgltf_accessor_read_float(sampler.output, (cursor + 1) * components + j, v1.f, 4);
|
||||
const Attr& v0 = output[(cursor + 0) * components + j];
|
||||
const Attr& v1 = output[(cursor + 1) * components + j];
|
||||
data.push_back(interpolateLinear(v0, v1, t, type));
|
||||
}
|
||||
break;
|
||||
|
||||
case cgltf_interpolation_type_step:
|
||||
{
|
||||
Attr v = {};
|
||||
cgltf_accessor_read_float(sampler.output, cursor * components + j, v.f, 4);
|
||||
const Attr& v = output[cursor * components + j];
|
||||
data.push_back(v);
|
||||
}
|
||||
break;
|
||||
|
||||
case cgltf_interpolation_type_cubic_spline:
|
||||
{
|
||||
Attr v0 = {};
|
||||
Attr b0 = {};
|
||||
Attr a1 = {};
|
||||
Attr v1 = {};
|
||||
cgltf_accessor_read_float(sampler.output, (cursor * 3 + 1) * components + j, v0.f, 4);
|
||||
cgltf_accessor_read_float(sampler.output, (cursor * 3 + 2) * components + j, b0.f, 4);
|
||||
cgltf_accessor_read_float(sampler.output, (cursor * 3 + 3) * components + j, a1.f, 4);
|
||||
cgltf_accessor_read_float(sampler.output, (cursor * 3 + 4) * components + j, v1.f, 4);
|
||||
const Attr& v0 = output[(cursor * 3 + 1) * components + j];
|
||||
const Attr& b0 = output[(cursor * 3 + 2) * components + j];
|
||||
const Attr& a1 = output[(cursor * 3 + 3) * components + j];
|
||||
const Attr& v1 = output[(cursor * 3 + 4) * components + j];
|
||||
data.push_back(interpolateHermite(v0, b0, v1, a1, t, range, type));
|
||||
}
|
||||
break;
|
||||
@ -2168,8 +2245,7 @@ void resampleKeyframes(std::vector<Attr>& data, const cgltf_animation_sampler& s
|
||||
|
||||
for (size_t j = 0; j < components; ++j)
|
||||
{
|
||||
Attr v = {};
|
||||
cgltf_accessor_read_float(sampler.output, offset * components + j, v.f, 4);
|
||||
const Attr& v = output[offset * components + j];
|
||||
data.push_back(v);
|
||||
}
|
||||
}
|
||||
@ -2193,10 +2269,17 @@ void markAnimated(cgltf_data* data, std::vector<NodeInfo>& nodes)
|
||||
NodeInfo& ni = nodes[channel.target_node - data->nodes];
|
||||
|
||||
// mark nodes that have animation tracks that change their base transform as animated
|
||||
if (!isTrackConstant(sampler, channel.target_path, channel.target_node))
|
||||
Attr first = {};
|
||||
if (!isTrackConstant(sampler, channel.target_path, channel.target_node, &first))
|
||||
{
|
||||
ni.animated_paths |= (1 << channel.target_path);
|
||||
}
|
||||
else if (channel.target_path == cgltf_animation_path_type_weights)
|
||||
{
|
||||
// we currently preserve constant weight tracks because the usecase is very rare and
|
||||
// isTrackConstant doesn't return the full set of weights to compare against
|
||||
ni.animated_paths |= (1 << channel.target_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
Attr base = {};
|
||||
@ -2212,12 +2295,10 @@ void markAnimated(cgltf_data* data, std::vector<NodeInfo>& nodes)
|
||||
case cgltf_animation_path_type_scale:
|
||||
memcpy(base.f, channel.target_node->scale, 3 * sizeof(float));
|
||||
break;
|
||||
default:;
|
||||
default:
|
||||
assert(!"Unknown target path");
|
||||
}
|
||||
|
||||
Attr first = {};
|
||||
cgltf_accessor_read_float(sampler.output, 0, first.f, 4);
|
||||
|
||||
const float tolerance = 1e-3f;
|
||||
|
||||
if (getDelta(base, first, channel.target_path) > tolerance)
|
||||
@ -2896,6 +2977,53 @@ void writeLight(std::string& json, const cgltf_light& light)
|
||||
append(json, "}");
|
||||
}
|
||||
|
||||
void finalizeBufferViews(std::string& json, std::vector<BufferView>& views, std::string& bin, std::string& fallback)
|
||||
{
|
||||
for (size_t i = 0; i < views.size(); ++i)
|
||||
{
|
||||
BufferView& view = views[i];
|
||||
|
||||
size_t bin_offset = bin.size();
|
||||
size_t fallback_offset = fallback.size();
|
||||
|
||||
size_t count = view.data.size() / view.stride;
|
||||
|
||||
int compression = -1;
|
||||
|
||||
if (view.compressed)
|
||||
{
|
||||
if (view.kind == BufferView::Kind_Index)
|
||||
{
|
||||
compressIndexStream(bin, view.data, count, view.stride);
|
||||
compression = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
compressVertexStream(bin, view.data, count, view.stride);
|
||||
compression = 0;
|
||||
}
|
||||
|
||||
fallback += view.data;
|
||||
}
|
||||
else
|
||||
{
|
||||
bin += view.data;
|
||||
}
|
||||
|
||||
size_t raw_offset = (compression >= 0) ? fallback_offset : bin_offset;
|
||||
|
||||
comma(json);
|
||||
writeBufferView(json, view.kind, count, view.stride, raw_offset, view.data.size(), compression, bin_offset, bin.size() - bin_offset);
|
||||
|
||||
// record written bytes for statistics
|
||||
view.bytes = bin.size() - bin_offset;
|
||||
|
||||
// align each bufferView by 4 bytes
|
||||
bin.resize((bin.size() + 3) & ~3);
|
||||
fallback.resize((fallback.size() + 3) & ~3);
|
||||
}
|
||||
}
|
||||
|
||||
void printMeshStats(const std::vector<Mesh>& meshes, const char* name)
|
||||
{
|
||||
size_t triangles = 0;
|
||||
@ -2952,7 +3080,7 @@ void printAttributeStats(const std::vector<BufferView>& views, BufferView::Kind
|
||||
}
|
||||
}
|
||||
|
||||
void process(cgltf_data* data, std::vector<Mesh>& meshes, const Settings& settings, std::string& json, std::string& bin)
|
||||
void process(cgltf_data* data, std::vector<Mesh>& meshes, const Settings& settings, std::string& json, std::string& bin, std::string& fallback)
|
||||
{
|
||||
if (settings.verbose)
|
||||
{
|
||||
@ -3017,6 +3145,7 @@ void process(cgltf_data* data, std::vector<Mesh>& meshes, const Settings& settin
|
||||
reindexMesh(mesh);
|
||||
simplifyMesh(mesh, settings.simplify_threshold, settings.simplify_aggressive);
|
||||
optimizeMesh(mesh);
|
||||
sortBoneInfluences(mesh);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -3342,7 +3471,7 @@ void process(cgltf_data* data, std::vector<Mesh>& meshes, const Settings& settin
|
||||
append(json, "\"KHR_lights_punctual\"");
|
||||
}
|
||||
append(json, "]");
|
||||
if (settings.compress)
|
||||
if (settings.compress && !settings.fallback)
|
||||
{
|
||||
append(json, ",\"extensionsRequired\":[");
|
||||
// Note: ideally we should include MESHOPT_quantized_geometry in the required extension list (regardless of compression)
|
||||
@ -3353,47 +3482,13 @@ void process(cgltf_data* data, std::vector<Mesh>& meshes, const Settings& settin
|
||||
append(json, "]");
|
||||
}
|
||||
|
||||
size_t bytes[BufferView::Kind_Count] = {};
|
||||
|
||||
if (!views.empty())
|
||||
{
|
||||
std::string json_views;
|
||||
finalizeBufferViews(json_views, views, bin, fallback);
|
||||
|
||||
append(json, ",\"bufferViews\":[");
|
||||
for (size_t i = 0; i < views.size(); ++i)
|
||||
{
|
||||
BufferView& view = views[i];
|
||||
|
||||
size_t offset = bin.size();
|
||||
size_t count = view.data.size() / view.stride;
|
||||
|
||||
int compression = -1;
|
||||
|
||||
if (view.compressed)
|
||||
{
|
||||
if (view.kind == BufferView::Kind_Index)
|
||||
{
|
||||
compressIndexStream(bin, view.data, count, view.stride);
|
||||
compression = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
compressVertexStream(bin, view.data, count, view.stride);
|
||||
compression = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bin += view.data;
|
||||
}
|
||||
|
||||
comma(json);
|
||||
writeBufferView(json, view.kind, count, view.stride, offset, bin.size() - offset, compression);
|
||||
|
||||
view.bytes = bin.size() - offset;
|
||||
bytes[view.kind] += view.bytes;
|
||||
|
||||
// align each bufferView by 4 bytes
|
||||
bin.resize((bin.size() + 3) & ~3);
|
||||
}
|
||||
append(json, json_views);
|
||||
append(json, "]");
|
||||
}
|
||||
if (!json_accessors.empty())
|
||||
@ -3466,6 +3561,14 @@ void process(cgltf_data* data, std::vector<Mesh>& meshes, const Settings& settin
|
||||
|
||||
if (settings.verbose)
|
||||
{
|
||||
size_t bytes[BufferView::Kind_Count] = {};
|
||||
|
||||
for (size_t i = 0; i < views.size(); ++i)
|
||||
{
|
||||
BufferView& view = views[i];
|
||||
bytes[view.kind] += view.bytes;
|
||||
}
|
||||
|
||||
printf("output: %d nodes, %d meshes (%d primitives), %d materials\n", int(node_offset), int(mesh_offset), int(meshes.size()), int(material_offset));
|
||||
printf("output: JSON %d bytes, buffers %d bytes\n", int(json.size()), int(bin.size()));
|
||||
printf("output: buffers: vertex %d bytes, index %d bytes, skin %d bytes, time %d bytes, keyframe %d bytes, image %d bytes\n",
|
||||
@ -3495,6 +3598,56 @@ bool requiresExtension(cgltf_data* data, const char* name)
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* getBaseName(const char* path)
|
||||
{
|
||||
const char* slash = strrchr(path, '/');
|
||||
const char* backslash = strrchr(path, '\\');
|
||||
|
||||
const char* rs = slash ? slash + 1 : path;
|
||||
const char* bs = backslash ? backslash + 1 : path;
|
||||
|
||||
return std::max(rs, bs);
|
||||
}
|
||||
|
||||
std::string getBufferSpec(const char* bin_path, size_t bin_size, const char* fallback_path, size_t fallback_size, bool fallback_ref)
|
||||
{
|
||||
std::string json;
|
||||
append(json, "\"buffers\":[");
|
||||
append(json, "{");
|
||||
if (bin_path)
|
||||
{
|
||||
append(json, "\"uri\":\"");
|
||||
append(json, bin_path);
|
||||
append(json, "\"");
|
||||
}
|
||||
comma(json);
|
||||
append(json, "\"byteLength\":");
|
||||
append(json, bin_size);
|
||||
append(json, "}");
|
||||
if (fallback_ref)
|
||||
{
|
||||
comma(json);
|
||||
append(json, "{");
|
||||
if (fallback_path)
|
||||
{
|
||||
append(json, "\"uri\":\"");
|
||||
append(json, fallback_path);
|
||||
append(json, "\"");
|
||||
}
|
||||
comma(json);
|
||||
append(json, "\"byteLength\":");
|
||||
append(json, fallback_size);
|
||||
append(json, ",\"extensions\":{");
|
||||
append(json, "\"MESHOPT_compression\":{");
|
||||
append(json, "\"fallback\":true");
|
||||
append(json, "}}");
|
||||
append(json, "}");
|
||||
}
|
||||
append(json, "]");
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
int gltfpack(const char* input, const char* output, const Settings& settings)
|
||||
{
|
||||
cgltf_data* data = 0;
|
||||
@ -3549,8 +3702,8 @@ int gltfpack(const char* input, const char* output, const Settings& settings)
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::string json, bin;
|
||||
process(data, meshes, settings, json, bin);
|
||||
std::string json, bin, fallback;
|
||||
process(data, meshes, settings, json, bin, fallback);
|
||||
|
||||
cgltf_free(data);
|
||||
|
||||
@ -3566,41 +3719,52 @@ int gltfpack(const char* input, const char* output, const Settings& settings)
|
||||
std::string binpath = output;
|
||||
binpath.replace(binpath.size() - 5, 5, ".bin");
|
||||
|
||||
std::string binname = binpath;
|
||||
std::string::size_type slash = binname.find_last_of("/\\");
|
||||
if (slash != std::string::npos)
|
||||
binname.erase(0, slash + 1);
|
||||
std::string fbpath = output;
|
||||
fbpath.replace(fbpath.size() - 5, 5, ".fallback.bin");
|
||||
|
||||
FILE* outjson = fopen(output, "wb");
|
||||
FILE* outbin = fopen(binpath.c_str(), "wb");
|
||||
if (!outjson || !outbin)
|
||||
FILE* outfb = settings.fallback ? fopen(fbpath.c_str(), "wb") : NULL;
|
||||
if (!outjson || !outbin || (!outfb && settings.fallback))
|
||||
{
|
||||
fprintf(stderr, "Error saving %s\n", output);
|
||||
return 4;
|
||||
}
|
||||
|
||||
fprintf(outjson, "{\"buffers\":[{\"uri\":\"%s\",\"byteLength\":%zu}],", binname.c_str(), bin.size());
|
||||
std::string bufferspec = getBufferSpec(getBaseName(binpath.c_str()), bin.size(), settings.fallback ? getBaseName(fbpath.c_str()) : NULL, fallback.size(), settings.compress);
|
||||
|
||||
fprintf(outjson, "{");
|
||||
fwrite(bufferspec.c_str(), bufferspec.size(), 1, outjson);
|
||||
fprintf(outjson, ",");
|
||||
fwrite(json.c_str(), json.size(), 1, outjson);
|
||||
fprintf(outjson, "}");
|
||||
|
||||
fwrite(bin.c_str(), bin.size(), 1, outbin);
|
||||
|
||||
if (settings.fallback)
|
||||
fwrite(fallback.c_str(), fallback.size(), 1, outfb);
|
||||
|
||||
fclose(outjson);
|
||||
fclose(outbin);
|
||||
if (outfb)
|
||||
fclose(outfb);
|
||||
}
|
||||
else if (oext && (strcmp(oext, ".glb") == 0 || strcmp(oext, ".GLB") == 0))
|
||||
{
|
||||
std::string fbpath = output;
|
||||
fbpath.replace(fbpath.size() - 4, 4, ".fallback.bin");
|
||||
|
||||
FILE* out = fopen(output, "wb");
|
||||
if (!out)
|
||||
FILE* outfb = settings.fallback ? fopen(fbpath.c_str(), "wb") : NULL;
|
||||
if (!out || (!outfb && settings.fallback))
|
||||
{
|
||||
fprintf(stderr, "Error saving %s\n", output);
|
||||
return 4;
|
||||
}
|
||||
|
||||
char bufferspec[64];
|
||||
sprintf(bufferspec, "{\"buffers\":[{\"byteLength\":%zu}],", bin.size());
|
||||
std::string bufferspec = getBufferSpec(NULL, bin.size(), settings.fallback ? getBaseName(fbpath.c_str()) : NULL, fallback.size(), settings.compress);
|
||||
|
||||
json.insert(0, bufferspec);
|
||||
json.insert(0, "{" + bufferspec + ",");
|
||||
json.push_back('}');
|
||||
|
||||
while (json.size() % 4)
|
||||
@ -3621,7 +3785,12 @@ int gltfpack(const char* input, const char* output, const Settings& settings)
|
||||
writeU32(out, 0x004E4942);
|
||||
fwrite(bin.c_str(), bin.size(), 1, out);
|
||||
|
||||
if (settings.fallback)
|
||||
fwrite(fallback.c_str(), fallback.size(), 1, outfb);
|
||||
|
||||
fclose(out);
|
||||
if (outfb)
|
||||
fclose(outfb);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3698,6 +3867,11 @@ int main(int argc, char** argv)
|
||||
{
|
||||
settings.compress = true;
|
||||
}
|
||||
else if (strcmp(arg, "-cf") == 0)
|
||||
{
|
||||
settings.compress = true;
|
||||
settings.fallback = true;
|
||||
}
|
||||
else if (strcmp(arg, "-v") == 0)
|
||||
{
|
||||
settings.verbose = 1;
|
||||
@ -3749,7 +3923,8 @@ int main(int argc, char** argv)
|
||||
fprintf(stderr, "-kn: keep named nodes and meshes attached to named nodes so that named nodes can be transformed externally\n");
|
||||
fprintf(stderr, "-si R: simplify meshes to achieve the ratio R (default: 1; R should be between 0 and 1)\n");
|
||||
fprintf(stderr, "-sa: aggressively simplify to the target ratio disregarding quality\n");
|
||||
fprintf(stderr, "-c: produce compressed glb files\n");
|
||||
fprintf(stderr, "-c: produce compressed gltf/glb files\n");
|
||||
fprintf(stderr, "-cf: produce compressed gltf/glb files with fallback for loaders that don't support compression\n");
|
||||
fprintf(stderr, "-v: verbose output\n");
|
||||
fprintf(stderr, "-h: display this help and exit\n");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user