mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-17 20:52:36 +01:00
Updated meshoptimizer.
This commit is contained in:
@@ -37,7 +37,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: cmake configure
|
||||
run: cmake . -DBUILD_DEMO=ON -DBUILD_TOOLS=ON -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$<CONFIG:Debug>:Debug>" -A ${{matrix.arch}}
|
||||
run: cmake . -DMESHOPT_BUILD_DEMO=ON -DMESHOPT_BUILD_TOOLS=ON -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$<CONFIG:Debug>:Debug>" -A ${{matrix.arch}}
|
||||
- name: cmake test
|
||||
shell: bash # necessary for fail-fast
|
||||
run: |
|
||||
|
||||
2
3rdparty/meshoptimizer/.travis.yml
vendored
2
3rdparty/meshoptimizer/.travis.yml
vendored
@@ -26,7 +26,7 @@ script:
|
||||
- if [[ "$TRAVIS_OS_NAME" != "windows" ]]; then make -j2 config=debug test; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" != "windows" ]]; then make -j2 config=release test; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" != "windows" ]]; then make -j2 config=release gltfpack; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then cmake -G "$TARGET" -DBUILD_DEMO=ON -DBUILD_TOOLS=ON; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then cmake -G "$TARGET" -DMESHOPT_BUILD_DEMO=ON -DMESHOPT_BUILD_TOOLS=ON; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then cmake --build . -- -property:Configuration=Debug -verbosity:minimal; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then ./Debug/demo.exe demo/pirate.obj; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then cmake --build . -- -property:Configuration=Release -verbosity:minimal; fi
|
||||
|
||||
35
3rdparty/meshoptimizer/CMakeLists.txt
vendored
35
3rdparty/meshoptimizer/CMakeLists.txt
vendored
@@ -1,15 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.15)
|
||||
if (CMAKE_VERSION VERSION_GREATER 3.15 OR CMAKE_VERSION VERSION_EQUAL 3.15)
|
||||
cmake_policy(SET CMP0091 NEW) # Enables use of MSVC_RUNTIME_LIBRARY
|
||||
cmake_policy(SET CMP0092 NEW) # Enables clean /W4 override for MSVC
|
||||
endif()
|
||||
|
||||
project(meshoptimizer VERSION 0.13 LANGUAGES CXX)
|
||||
|
||||
option(BUILD_DEMO "Build demo" OFF)
|
||||
option(BUILD_TOOLS "Build tools" OFF)
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||
option(MESHOPT_BUILD_DEMO "Build demo" OFF)
|
||||
option(MESHOPT_BUILD_TOOLS "Build tools" OFF)
|
||||
option(MESHOPT_BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||
set(MESHOPT_BUILD_TOOLS_GLFW_FOLDER_NAME "" CACHE STRING "Custom folder to look for GLFW")
|
||||
|
||||
set(SOURCES
|
||||
src/meshoptimizer.h
|
||||
@@ -38,7 +39,7 @@ endif()
|
||||
add_library(meshoptimizer ${SOURCES})
|
||||
target_include_directories(meshoptimizer INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>")
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
if(MESHOPT_BUILD_SHARED_LIBS)
|
||||
set_target_properties(meshoptimizer PROPERTIES CXX_VISIBILITY_PRESET hidden)
|
||||
set_target_properties(meshoptimizer PROPERTIES VISIBILITY_INLINES_HIDDEN ON)
|
||||
|
||||
@@ -52,20 +53,38 @@ endif()
|
||||
|
||||
set(TARGETS meshoptimizer)
|
||||
|
||||
if(BUILD_DEMO)
|
||||
if(MESHOPT_BUILD_DEMO)
|
||||
add_executable(demo demo/main.cpp demo/miniz.cpp demo/tests.cpp tools/meshloader.cpp)
|
||||
target_link_libraries(demo meshoptimizer)
|
||||
endif()
|
||||
|
||||
if(BUILD_TOOLS)
|
||||
if(MESHOPT_BUILD_TOOLS)
|
||||
add_executable(gltfpack tools/gltfpack.cpp tools/meshloader.cpp tools/basistoktx.cpp)
|
||||
target_link_libraries(gltfpack meshoptimizer)
|
||||
list(APPEND TARGETS gltfpack)
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
if(MESHOPT_BUILD_SHARED_LIBS)
|
||||
string(CONCAT RPATH "$ORIGIN/../" ${CMAKE_INSTALL_LIBDIR})
|
||||
set_target_properties(gltfpack PROPERTIES INSTALL_RPATH ${RPATH})
|
||||
endif()
|
||||
|
||||
if(NOT (MESHOPT_BUILD_TOOLS_GLFW_FOLDER_NAME STREQUAL ""))
|
||||
message(STATUS "Using GLFW3 from: ${MESHOPT_BUILD_TOOLS_GLFW_FOLDER_NAME}")
|
||||
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "")
|
||||
set(GLFW_BUILD_TESTS OFF CACHE BOOL "")
|
||||
set(GLFW_BUILD_DOCS OFF CACHE BOOL "")
|
||||
set(GLFW_INSTALL OFF CACHE BOOL "")
|
||||
add_subdirectory(${MESHOPT_BUILD_TOOLS_GLFW_FOLDER_NAME})
|
||||
set(glfw3_FOUND TRUE)
|
||||
set(glfw3_LIBRARY glfw)
|
||||
else()
|
||||
find_package(glfw3 3.3 QUIET)
|
||||
endif()
|
||||
|
||||
if (glfw3_FOUND)
|
||||
add_executable(lodviewer tools/lodviewer.cpp tools/meshloader.cpp)
|
||||
target_link_libraries(lodviewer ${glfw3_LIBRARY} meshoptimizer)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
135
3rdparty/meshoptimizer/demo/GLTFLoader.js
vendored
135
3rdparty/meshoptimizer/demo/GLTFLoader.js
vendored
@@ -2609,108 +2609,109 @@ THREE.GLTFLoader = ( function () {
|
||||
|
||||
}
|
||||
|
||||
return Promise.all( pending ).then( function ( originalMaterials ) {
|
||||
pending.push( parser.loadGeometries( primitives ) );
|
||||
|
||||
return parser.loadGeometries( primitives ).then( function ( geometries ) {
|
||||
return Promise.all( pending ).then( function ( results ) {
|
||||
|
||||
var meshes = [];
|
||||
var materials = results.slice( 0, results.length - 1 );
|
||||
var geometries = results[ results.length - 1 ];
|
||||
|
||||
for ( var i = 0, il = geometries.length; i < il; i ++ ) {
|
||||
var meshes = [];
|
||||
|
||||
var geometry = geometries[ i ];
|
||||
var primitive = primitives[ i ];
|
||||
for ( var i = 0, il = geometries.length; i < il; i ++ ) {
|
||||
|
||||
// 1. create Mesh
|
||||
var geometry = geometries[ i ];
|
||||
var primitive = primitives[ i ];
|
||||
|
||||
var mesh;
|
||||
// 1. create Mesh
|
||||
|
||||
var material = originalMaterials[ i ];
|
||||
var mesh;
|
||||
|
||||
if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES ||
|
||||
primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ||
|
||||
primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ||
|
||||
primitive.mode === undefined ) {
|
||||
var material = materials[ i ];
|
||||
|
||||
// .isSkinnedMesh isn't in glTF spec. See .markDefs()
|
||||
mesh = meshDef.isSkinnedMesh === true
|
||||
? new THREE.SkinnedMesh( geometry, material )
|
||||
: new THREE.Mesh( geometry, material );
|
||||
if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES ||
|
||||
primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ||
|
||||
primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ||
|
||||
primitive.mode === undefined ) {
|
||||
|
||||
if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) {
|
||||
// .isSkinnedMesh isn't in glTF spec. See .markDefs()
|
||||
mesh = meshDef.isSkinnedMesh === true
|
||||
? new THREE.SkinnedMesh( geometry, material )
|
||||
: new THREE.Mesh( geometry, material );
|
||||
|
||||
// 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
|
||||
mesh.normalizeSkinWeights();
|
||||
if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) {
|
||||
|
||||
}
|
||||
|
||||
if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) {
|
||||
|
||||
mesh.drawMode = THREE.TriangleStripDrawMode;
|
||||
|
||||
} else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) {
|
||||
|
||||
mesh.drawMode = THREE.TriangleFanDrawMode;
|
||||
|
||||
}
|
||||
|
||||
} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) {
|
||||
|
||||
mesh = new THREE.LineSegments( geometry, material );
|
||||
|
||||
} else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) {
|
||||
|
||||
mesh = new THREE.Line( geometry, material );
|
||||
|
||||
} else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) {
|
||||
|
||||
mesh = new THREE.LineLoop( geometry, material );
|
||||
|
||||
} else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) {
|
||||
|
||||
mesh = new THREE.Points( geometry, material );
|
||||
|
||||
} else {
|
||||
|
||||
throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode );
|
||||
// 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
|
||||
mesh.normalizeSkinWeights();
|
||||
|
||||
}
|
||||
|
||||
if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) {
|
||||
if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) {
|
||||
|
||||
updateMorphTargets( mesh, meshDef );
|
||||
mesh.drawMode = THREE.TriangleStripDrawMode;
|
||||
|
||||
} else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) {
|
||||
|
||||
mesh.drawMode = THREE.TriangleFanDrawMode;
|
||||
|
||||
}
|
||||
|
||||
mesh.name = meshDef.name || ( 'mesh_' + meshIndex );
|
||||
} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) {
|
||||
|
||||
if ( geometries.length > 1 ) mesh.name += '_' + i;
|
||||
mesh = new THREE.LineSegments( geometry, material );
|
||||
|
||||
assignExtrasToUserData( mesh, meshDef );
|
||||
} else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) {
|
||||
|
||||
parser.assignFinalMaterial( mesh );
|
||||
mesh = new THREE.Line( geometry, material );
|
||||
|
||||
meshes.push( mesh );
|
||||
} else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) {
|
||||
|
||||
mesh = new THREE.LineLoop( geometry, material );
|
||||
|
||||
} else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) {
|
||||
|
||||
mesh = new THREE.Points( geometry, material );
|
||||
|
||||
} else {
|
||||
|
||||
throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode );
|
||||
|
||||
}
|
||||
|
||||
if ( meshes.length === 1 ) {
|
||||
if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) {
|
||||
|
||||
return meshes[ 0 ];
|
||||
updateMorphTargets( mesh, meshDef );
|
||||
|
||||
}
|
||||
|
||||
var group = new THREE.Group();
|
||||
mesh.name = meshDef.name || ( 'mesh_' + meshIndex );
|
||||
|
||||
for ( var i = 0, il = meshes.length; i < il; i ++ ) {
|
||||
if ( geometries.length > 1 ) mesh.name += '_' + i;
|
||||
|
||||
group.add( meshes[ i ] );
|
||||
assignExtrasToUserData( mesh, meshDef );
|
||||
|
||||
}
|
||||
parser.assignFinalMaterial( mesh );
|
||||
|
||||
return group;
|
||||
meshes.push( mesh );
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
if ( meshes.length === 1 ) {
|
||||
|
||||
return meshes[ 0 ];
|
||||
|
||||
}
|
||||
|
||||
var group = new THREE.Group();
|
||||
|
||||
for ( var i = 0, il = meshes.length; i < il; i ++ ) {
|
||||
|
||||
group.add( meshes[ i ] );
|
||||
|
||||
}
|
||||
|
||||
return group;
|
||||
|
||||
} );
|
||||
|
||||
|
||||
5
3rdparty/meshoptimizer/tools/basistoktx.cpp
vendored
5
3rdparty/meshoptimizer/tools/basistoktx.cpp
vendored
@@ -125,7 +125,12 @@ static void createDfd(std::vector<uint32_t>& result, int channels, bool srgb)
|
||||
|
||||
for (int i = 0; i < channels; ++i)
|
||||
{
|
||||
uint32_t qualifiers = (srgb && i == 3) ? KHR_DF_SAMPLE_DATATYPE_LINEAR : 0;
|
||||
|
||||
KHR_DFDSETSVAL(dfd, i, CHANNELID, channel_enums[i]);
|
||||
KHR_DFDSETSVAL(dfd, i, QUALIFIERS, qualifiers);
|
||||
KHR_DFDSETSVAL(dfd, i, SAMPLELOWER, 0);
|
||||
KHR_DFDSETSVAL(dfd, i, SAMPLEUPPER, ~0u);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
60
3rdparty/meshoptimizer/tools/cgltf.h
vendored
60
3rdparty/meshoptimizer/tools/cgltf.h
vendored
@@ -1258,6 +1258,66 @@ cgltf_result cgltf_validate(cgltf_data* data)
|
||||
}
|
||||
}
|
||||
|
||||
for (cgltf_size i = 0; i < data->nodes_count; ++i)
|
||||
{
|
||||
cgltf_node* p1 = data->nodes[i].parent;
|
||||
cgltf_node* p2 = p1 ? p1->parent : NULL;
|
||||
|
||||
while (p1 && p2)
|
||||
{
|
||||
if (p1 == p2)
|
||||
{
|
||||
return cgltf_result_invalid_gltf;
|
||||
}
|
||||
|
||||
p1 = p1->parent;
|
||||
p2 = p2->parent ? p2->parent->parent : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (cgltf_size i = 0; i < data->scenes_count; ++i)
|
||||
{
|
||||
for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j)
|
||||
{
|
||||
if (data->scenes[i].nodes[j]->parent)
|
||||
{
|
||||
return cgltf_result_invalid_gltf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (cgltf_size i = 0; i < data->animations_count; ++i)
|
||||
{
|
||||
for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j)
|
||||
{
|
||||
cgltf_animation_channel* channel = &data->animations[i].channels[j];
|
||||
|
||||
if (!channel->target_node)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
cgltf_size components = 1;
|
||||
|
||||
if (channel->target_path == cgltf_animation_path_type_weights)
|
||||
{
|
||||
if (!channel->target_node->mesh || !channel->target_node->mesh->primitives_count)
|
||||
{
|
||||
return cgltf_result_invalid_gltf;
|
||||
}
|
||||
|
||||
components = channel->target_node->mesh->primitives[0].targets_count;
|
||||
}
|
||||
|
||||
cgltf_size values = channel->sampler->interpolation == cgltf_interpolation_type_cubic_spline ? 3 : 1;
|
||||
|
||||
if (channel->sampler->input->count * components * values != channel->sampler->output->count)
|
||||
{
|
||||
return cgltf_result_data_too_short;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cgltf_result_success;
|
||||
}
|
||||
|
||||
|
||||
429
3rdparty/meshoptimizer/tools/gltfpack.cpp
vendored
429
3rdparty/meshoptimizer/tools/gltfpack.cpp
vendored
@@ -76,6 +76,31 @@ struct Mesh
|
||||
std::vector<const char*> target_names;
|
||||
};
|
||||
|
||||
struct Track
|
||||
{
|
||||
cgltf_node* node;
|
||||
cgltf_animation_path_type path;
|
||||
|
||||
bool dummy;
|
||||
|
||||
size_t components; // 1 unless path is cgltf_animation_path_type_weights
|
||||
|
||||
cgltf_interpolation_type interpolation;
|
||||
|
||||
std::vector<float> time; // empty for resampled or constant animations
|
||||
std::vector<Attr> data;
|
||||
};
|
||||
|
||||
struct Animation
|
||||
{
|
||||
const char* name;
|
||||
|
||||
float start;
|
||||
int frames;
|
||||
|
||||
std::vector<Track> tracks;
|
||||
};
|
||||
|
||||
struct Settings
|
||||
{
|
||||
int pos_bits;
|
||||
@@ -290,7 +315,7 @@ void transformMesh(Mesh& mesh, const cgltf_node* node)
|
||||
}
|
||||
}
|
||||
|
||||
void parseMeshesGltf(cgltf_data* data, std::vector<Mesh>& meshes)
|
||||
void parseMeshes(cgltf_data* data, std::vector<Mesh>& meshes)
|
||||
{
|
||||
for (size_t ni = 0; ni < data->nodes_count; ++ni)
|
||||
{
|
||||
@@ -318,7 +343,7 @@ void parseMeshesGltf(cgltf_data* data, std::vector<Mesh>& meshes)
|
||||
continue;
|
||||
}
|
||||
|
||||
Mesh result;
|
||||
Mesh result = {};
|
||||
|
||||
result.node = &node;
|
||||
|
||||
@@ -563,6 +588,49 @@ void parseMeshesObj(fastObjMesh* obj, cgltf_data* data, std::vector<Mesh>& meshe
|
||||
}
|
||||
}
|
||||
|
||||
void parseAnimations(cgltf_data* data, std::vector<Animation>& animations)
|
||||
{
|
||||
for (size_t i = 0; i < data->animations_count; ++i)
|
||||
{
|
||||
const cgltf_animation& animation = data->animations[i];
|
||||
|
||||
Animation result = {};
|
||||
result.name = animation.name;
|
||||
|
||||
for (size_t j = 0; j < animation.channels_count; ++j)
|
||||
{
|
||||
const cgltf_animation_channel& channel = animation.channels[j];
|
||||
|
||||
if (!channel.target_node)
|
||||
{
|
||||
fprintf(stderr, "Warning: ignoring channel %d of animation %d because it has no target node\n", int(j), int(i));
|
||||
continue;
|
||||
}
|
||||
|
||||
Track track = {};
|
||||
track.node = channel.target_node;
|
||||
track.path = channel.target_path;
|
||||
|
||||
track.components = (channel.target_path == cgltf_animation_path_type_weights) ? track.node->mesh->primitives[0].targets_count : 1;
|
||||
|
||||
track.interpolation = channel.sampler->interpolation;
|
||||
|
||||
readAccessor(track.time, channel.sampler->input);
|
||||
readAccessor(track.data, channel.sampler->output);
|
||||
|
||||
result.tracks.push_back(track);
|
||||
}
|
||||
|
||||
if (result.tracks.empty())
|
||||
{
|
||||
fprintf(stderr, "Warning: ignoring animation %d because it has no valid tracks\n", int(i));
|
||||
continue;
|
||||
}
|
||||
|
||||
animations.push_back(result);
|
||||
}
|
||||
}
|
||||
|
||||
bool areTextureViewsEqual(const cgltf_texture_view& lhs, const cgltf_texture_view& rhs)
|
||||
{
|
||||
if (lhs.has_transform != rhs.has_transform)
|
||||
@@ -2300,69 +2368,46 @@ void writeAccessor(std::string& json, size_t view, size_t offset, cgltf_type typ
|
||||
|
||||
float getDelta(const Attr& l, const Attr& r, cgltf_animation_path_type type)
|
||||
{
|
||||
if (type == cgltf_animation_path_type_rotation)
|
||||
switch (type)
|
||||
{
|
||||
float error = 1.f - fabsf(l.f[0] * r.f[0] + l.f[1] * r.f[1] + l.f[2] * r.f[2] + l.f[3] * r.f[3]);
|
||||
case cgltf_animation_path_type_translation:
|
||||
return std::max(std::max(fabsf(l.f[0] - r.f[0]), fabsf(l.f[1] - r.f[1])), fabsf(l.f[2] - r.f[2]));
|
||||
|
||||
return error;
|
||||
}
|
||||
else
|
||||
{
|
||||
float error = 0;
|
||||
for (int k = 0; k < 4; ++k)
|
||||
error += fabsf(r.f[k] - l.f[k]);
|
||||
case cgltf_animation_path_type_rotation:
|
||||
return acosf(std::min(1.f, fabsf(l.f[0] * r.f[0] + l.f[1] * r.f[1] + l.f[2] * r.f[2] + l.f[3] * r.f[3])));
|
||||
|
||||
return error;
|
||||
case cgltf_animation_path_type_scale:
|
||||
return std::max(std::max(fabsf(l.f[0] / r.f[0] - 1), fabsf(l.f[1] / r.f[1] - 1)), fabsf(l.f[2] / r.f[2] - 1));
|
||||
|
||||
case cgltf_animation_path_type_weights:
|
||||
return fabsf(l.f[0] - r.f[0]);
|
||||
|
||||
default:
|
||||
assert(!"Uknown animation path");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool isTrackConstant(const cgltf_animation_sampler& sampler, cgltf_animation_path_type type, cgltf_node* target_node, Attr* out_first = 0)
|
||||
float getDeltaTolerance(cgltf_animation_path_type type)
|
||||
{
|
||||
const float tolerance = 1e-3f;
|
||||
|
||||
size_t value_stride = (sampler.interpolation == cgltf_interpolation_type_cubic_spline) ? 3 : 1;
|
||||
size_t value_offset = (sampler.interpolation == cgltf_interpolation_type_cubic_spline) ? 1 : 0;
|
||||
|
||||
size_t components = (type == cgltf_animation_path_type_weights) ? target_node->mesh->primitives[0].targets_count : 1;
|
||||
|
||||
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)
|
||||
switch (type)
|
||||
{
|
||||
Attr first = output[j * value_stride + value_offset];
|
||||
case cgltf_animation_path_type_translation:
|
||||
return 0.001f; // linear
|
||||
|
||||
for (size_t i = 1; i < sampler.input->count; ++i)
|
||||
{
|
||||
const Attr& attr = output[(i * components + j) * value_stride + value_offset];
|
||||
case cgltf_animation_path_type_rotation:
|
||||
return 0.001f; // radians
|
||||
|
||||
if (getDelta(first, attr, type) > tolerance)
|
||||
return false;
|
||||
}
|
||||
case cgltf_animation_path_type_scale:
|
||||
return 0.001f; // ratio
|
||||
|
||||
if (sampler.interpolation == cgltf_interpolation_type_cubic_spline)
|
||||
{
|
||||
for (size_t i = 0; i < sampler.input->count; ++i)
|
||||
{
|
||||
for (int k = 0; k < 2; ++k)
|
||||
{
|
||||
const Attr& t = output[(i * components + j) * 3 + k * 2];
|
||||
case cgltf_animation_path_type_weights:
|
||||
return 0.001f; // linear
|
||||
|
||||
float error = fabsf(t.f[0]) + fabsf(t.f[1]) + fabsf(t.f[2]) + fabsf(t.f[3]);
|
||||
|
||||
if (error > tolerance)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
assert(!"Uknown animation path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (out_first)
|
||||
*out_first = output[value_offset];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Attr interpolateLinear(const Attr& l, const Attr& r, float t, cgltf_animation_path_type type)
|
||||
@@ -2447,22 +2492,15 @@ Attr interpolateHermite(const Attr& v0, const Attr& t0, const Attr& v1, const At
|
||||
return lerp;
|
||||
}
|
||||
|
||||
void resampleKeyframes(std::vector<Attr>& data, const cgltf_animation_sampler& sampler, cgltf_animation_path_type type, cgltf_node* target_node, int frames, float mint, int freq)
|
||||
void resampleKeyframes(std::vector<Attr>& data, const std::vector<float>& input, const std::vector<Attr>& output, cgltf_animation_path_type type, cgltf_interpolation_type interpolation, size_t components, int frames, float mint, int freq)
|
||||
{
|
||||
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)
|
||||
{
|
||||
float time = mint + float(i) / freq;
|
||||
|
||||
while (cursor + 1 < sampler.input->count)
|
||||
while (cursor + 1 < input.size())
|
||||
{
|
||||
float next_time = input[cursor + 1];
|
||||
|
||||
@@ -2472,7 +2510,7 @@ void resampleKeyframes(std::vector<Attr>& data, const cgltf_animation_sampler& s
|
||||
cursor++;
|
||||
}
|
||||
|
||||
if (cursor + 1 < sampler.input->count)
|
||||
if (cursor + 1 < input.size())
|
||||
{
|
||||
float cursor_time = input[cursor + 0];
|
||||
float next_time = input[cursor + 1];
|
||||
@@ -2483,7 +2521,7 @@ void resampleKeyframes(std::vector<Attr>& data, const cgltf_animation_sampler& s
|
||||
|
||||
for (size_t j = 0; j < components; ++j)
|
||||
{
|
||||
switch (sampler.interpolation)
|
||||
switch (interpolation)
|
||||
{
|
||||
case cgltf_interpolation_type_linear:
|
||||
{
|
||||
@@ -2517,7 +2555,7 @@ void resampleKeyframes(std::vector<Attr>& data, const cgltf_animation_sampler& s
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t offset = (sampler.interpolation == cgltf_interpolation_type_cubic_spline) ? cursor * 3 + 1 : cursor;
|
||||
size_t offset = (interpolation == cgltf_interpolation_type_cubic_spline) ? cursor * 3 + 1 : cursor;
|
||||
|
||||
for (size_t j = 0; j < components; ++j)
|
||||
{
|
||||
@@ -2528,59 +2566,123 @@ void resampleKeyframes(std::vector<Attr>& data, const cgltf_animation_sampler& s
|
||||
}
|
||||
}
|
||||
|
||||
void markAnimated(cgltf_data* data, std::vector<NodeInfo>& nodes)
|
||||
bool isTrackEqual(const std::vector<Attr>& data, cgltf_animation_path_type type, int frames, const Attr* value, size_t components)
|
||||
{
|
||||
for (size_t i = 0; i < data->animations_count; ++i)
|
||||
assert(data.size() == frames * components);
|
||||
|
||||
float tolerance = getDeltaTolerance(type);
|
||||
|
||||
for (int i = 0; i < frames; ++i)
|
||||
{
|
||||
const cgltf_animation& animation = data->animations[i];
|
||||
|
||||
for (size_t j = 0; j < animation.channels_count; ++j)
|
||||
for (size_t j = 0; j < components; ++j)
|
||||
{
|
||||
const cgltf_animation_channel& channel = animation.channels[j];
|
||||
const cgltf_animation_sampler& sampler = *channel.sampler;
|
||||
float delta = getDelta(value[j], data[i * components + j], type);
|
||||
|
||||
if (!channel.target_node)
|
||||
continue;
|
||||
if (delta > tolerance)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
NodeInfo& ni = nodes[channel.target_node - data->nodes];
|
||||
return true;
|
||||
}
|
||||
|
||||
void getBaseTransform(Attr* result, size_t components, cgltf_animation_path_type type, cgltf_node* node)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case cgltf_animation_path_type_translation:
|
||||
memcpy(result->f, node->translation, 3 * sizeof(float));
|
||||
break;
|
||||
|
||||
case cgltf_animation_path_type_rotation:
|
||||
memcpy(result->f, node->rotation, 4 * sizeof(float));
|
||||
break;
|
||||
|
||||
case cgltf_animation_path_type_scale:
|
||||
memcpy(result->f, node->scale, 3 * sizeof(float));
|
||||
break;
|
||||
|
||||
case cgltf_animation_path_type_weights:
|
||||
if (node->weights_count)
|
||||
{
|
||||
assert(node->weights_count == components);
|
||||
memcpy(result->f, node->weights, components * sizeof(float));
|
||||
}
|
||||
else if (node->mesh && node->mesh->weights_count)
|
||||
{
|
||||
assert(node->mesh->weights_count == components);
|
||||
memcpy(result->f, node->mesh->weights, components * sizeof(float));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(!"Unknown animation path");
|
||||
}
|
||||
}
|
||||
|
||||
void processAnimation(Animation& animation, const Settings& settings)
|
||||
{
|
||||
float mint = 0, maxt = 0;
|
||||
|
||||
for (size_t i = 0; i < animation.tracks.size(); ++i)
|
||||
{
|
||||
const Track& track = animation.tracks[i];
|
||||
assert(!track.time.empty());
|
||||
|
||||
mint = std::min(mint, track.time.front());
|
||||
maxt = std::max(maxt, track.time.back());
|
||||
}
|
||||
|
||||
// round the number of frames to nearest but favor the "up" direction
|
||||
// this means that at 10 Hz resampling, we will try to preserve the last frame <10ms
|
||||
// but if the last frame is <2ms we favor just removing this data
|
||||
int frames = 1 + int((maxt - mint) * settings.anim_freq + 0.8f);
|
||||
|
||||
animation.start = mint;
|
||||
animation.frames = frames;
|
||||
|
||||
std::vector<Attr> base;
|
||||
|
||||
for (size_t i = 0; i < animation.tracks.size(); ++i)
|
||||
{
|
||||
Track& track = animation.tracks[i];
|
||||
|
||||
std::vector<Attr> result;
|
||||
resampleKeyframes(result, track.time, track.data, track.path, track.interpolation, track.components, frames, mint, settings.anim_freq);
|
||||
|
||||
track.time.clear();
|
||||
track.data.swap(result);
|
||||
|
||||
if (isTrackEqual(track.data, track.path, frames, &track.data[0], track.components))
|
||||
{
|
||||
// track is constant (equal to first keyframe), we only need the first keyframe
|
||||
track.data.resize(track.components);
|
||||
|
||||
// track.dummy is true iff track redundantly sets up the value to be equal to default node transform
|
||||
base.resize(track.components);
|
||||
getBaseTransform(&base[0], track.components, track.path, track.node);
|
||||
|
||||
track.dummy = isTrackEqual(track.data, track.path, 1, &base[0], track.components);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void markAnimated(cgltf_data* data, std::vector<NodeInfo>& nodes, const std::vector<Animation>& animations)
|
||||
{
|
||||
for (size_t i = 0; i < animations.size(); ++i)
|
||||
{
|
||||
const Animation& animation = animations[i];
|
||||
|
||||
for (size_t j = 0; j < animation.tracks.size(); ++j)
|
||||
{
|
||||
const Track& track = animation.tracks[j];
|
||||
|
||||
// mark nodes that have animation tracks that change their base transform as animated
|
||||
Attr first = {};
|
||||
if (!isTrackConstant(sampler, channel.target_path, channel.target_node, &first))
|
||||
if (!track.dummy)
|
||||
{
|
||||
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 = {};
|
||||
NodeInfo& ni = nodes[track.node - data->nodes];
|
||||
|
||||
switch (channel.target_path)
|
||||
{
|
||||
case cgltf_animation_path_type_translation:
|
||||
memcpy(base.f, channel.target_node->translation, 3 * sizeof(float));
|
||||
break;
|
||||
case cgltf_animation_path_type_rotation:
|
||||
memcpy(base.f, channel.target_node->rotation, 4 * sizeof(float));
|
||||
break;
|
||||
case cgltf_animation_path_type_scale:
|
||||
memcpy(base.f, channel.target_node->scale, 3 * sizeof(float));
|
||||
break;
|
||||
default:
|
||||
assert(!"Unknown target path");
|
||||
}
|
||||
|
||||
const float tolerance = 1e-3f;
|
||||
|
||||
if (getDelta(base, first, channel.target_path) > tolerance)
|
||||
{
|
||||
ni.animated_paths |= (1 << channel.target_path);
|
||||
}
|
||||
ni.animated_paths |= (1 << track.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2594,7 +2696,7 @@ void markAnimated(cgltf_data* data, std::vector<NodeInfo>& nodes)
|
||||
}
|
||||
}
|
||||
|
||||
void markNeededNodes(cgltf_data* data, std::vector<NodeInfo>& nodes, const std::vector<Mesh>& meshes, const Settings& settings)
|
||||
void markNeededNodes(cgltf_data* data, std::vector<NodeInfo>& nodes, const std::vector<Mesh>& meshes, const std::vector<Animation>& animations, const Settings& settings)
|
||||
{
|
||||
// mark all joints as kept
|
||||
for (size_t i = 0; i < data->skins_count; ++i)
|
||||
@@ -2611,17 +2713,17 @@ void markNeededNodes(cgltf_data* data, std::vector<NodeInfo>& nodes, const std::
|
||||
}
|
||||
|
||||
// mark all animated nodes as kept
|
||||
for (size_t i = 0; i < data->animations_count; ++i)
|
||||
for (size_t i = 0; i < animations.size(); ++i)
|
||||
{
|
||||
const cgltf_animation& animation = data->animations[i];
|
||||
const Animation& animation = animations[i];
|
||||
|
||||
for (size_t j = 0; j < animation.channels_count; ++j)
|
||||
for (size_t j = 0; j < animation.tracks.size(); ++j)
|
||||
{
|
||||
const cgltf_animation_channel& channel = animation.channels[j];
|
||||
const Track& track = animation.tracks[j];
|
||||
|
||||
if (channel.target_node)
|
||||
if (settings.anim_const || !track.dummy)
|
||||
{
|
||||
NodeInfo& ni = nodes[channel.target_node - data->nodes];
|
||||
NodeInfo& ni = nodes[track.node - data->nodes];
|
||||
|
||||
ni.keep = true;
|
||||
}
|
||||
@@ -3355,62 +3457,46 @@ void writeNode(std::string& json, const cgltf_node& node, const std::vector<Node
|
||||
append(json, "}");
|
||||
}
|
||||
|
||||
void writeAnimation(std::string& json, std::vector<BufferView>& views, std::string& json_accessors, size_t& accr_offset, const cgltf_animation& animation, cgltf_data* data, const std::vector<NodeInfo>& nodes, const Settings& settings)
|
||||
void writeAnimation(std::string& json, std::vector<BufferView>& views, std::string& json_accessors, size_t& accr_offset, const Animation& animation, size_t i, cgltf_data* data, const std::vector<NodeInfo>& nodes, const Settings& settings)
|
||||
{
|
||||
std::vector<const cgltf_animation_channel*> tracks;
|
||||
std::vector<const Track*> tracks;
|
||||
|
||||
for (size_t j = 0; j < animation.channels_count; ++j)
|
||||
for (size_t j = 0; j < animation.tracks.size(); ++j)
|
||||
{
|
||||
const cgltf_animation_channel& channel = animation.channels[j];
|
||||
const Track& track = animation.tracks[j];
|
||||
|
||||
if (!channel.target_node)
|
||||
{
|
||||
fprintf(stderr, "Warning: ignoring channel %d of animation %d because it has no target node\n", int(j), int(&animation - data->animations));
|
||||
continue;
|
||||
}
|
||||
|
||||
const NodeInfo& ni = nodes[channel.target_node - data->nodes];
|
||||
const NodeInfo& ni = nodes[track.node - data->nodes];
|
||||
|
||||
if (!ni.keep)
|
||||
continue;
|
||||
|
||||
if (!settings.anim_const && (ni.animated_paths & (1 << channel.target_path)) == 0)
|
||||
if (!settings.anim_const && (ni.animated_paths & (1 << track.path)) == 0)
|
||||
continue;
|
||||
|
||||
tracks.push_back(&channel);
|
||||
tracks.push_back(&track);
|
||||
}
|
||||
|
||||
if (tracks.empty())
|
||||
{
|
||||
fprintf(stderr, "Warning: ignoring animation %d because it has no valid tracks\n", int(&animation - data->animations));
|
||||
fprintf(stderr, "Warning: ignoring animation %d because it has no valid tracks\n", int(i));
|
||||
return;
|
||||
}
|
||||
|
||||
float mint = 0, maxt = 0;
|
||||
bool needs_time = false;
|
||||
bool needs_pose = false;
|
||||
|
||||
for (size_t j = 0; j < tracks.size(); ++j)
|
||||
{
|
||||
const cgltf_animation_channel& channel = *tracks[j];
|
||||
const cgltf_animation_sampler& sampler = *channel.sampler;
|
||||
const Track& track = *tracks[j];
|
||||
|
||||
mint = std::min(mint, sampler.input->min[0]);
|
||||
maxt = std::max(maxt, sampler.input->max[0]);
|
||||
|
||||
bool tc = isTrackConstant(sampler, channel.target_path, channel.target_node);
|
||||
bool tc = track.data.size() == track.components;
|
||||
|
||||
needs_time = needs_time || !tc;
|
||||
needs_pose = needs_pose || tc;
|
||||
}
|
||||
|
||||
// round the number of frames to nearest but favor the "up" direction
|
||||
// this means that at 10 Hz resampling, we will try to preserve the last frame <10ms
|
||||
// but if the last frame is <2ms we favor just removing this data
|
||||
int frames = 1 + int((maxt - mint) * settings.anim_freq + 0.8f);
|
||||
|
||||
size_t time_accr = needs_time ? writeAnimationTime(views, json_accessors, accr_offset, mint, frames, settings) : 0;
|
||||
size_t pose_accr = needs_pose ? writeAnimationTime(views, json_accessors, accr_offset, mint, 1, settings) : 0;
|
||||
size_t time_accr = needs_time ? writeAnimationTime(views, json_accessors, accr_offset, animation.start, animation.frames, settings) : 0;
|
||||
size_t pose_accr = needs_pose ? writeAnimationTime(views, json_accessors, accr_offset, animation.start, 1, settings) : 0;
|
||||
|
||||
std::string json_samplers;
|
||||
std::string json_channels;
|
||||
@@ -3419,23 +3505,19 @@ void writeAnimation(std::string& json, std::vector<BufferView>& views, std::stri
|
||||
|
||||
for (size_t j = 0; j < tracks.size(); ++j)
|
||||
{
|
||||
const cgltf_animation_channel& channel = *tracks[j];
|
||||
const cgltf_animation_sampler& sampler = *channel.sampler;
|
||||
const Track& track = *tracks[j];
|
||||
|
||||
bool tc = isTrackConstant(sampler, channel.target_path, channel.target_node);
|
||||
|
||||
std::vector<Attr> track;
|
||||
resampleKeyframes(track, sampler, channel.target_path, channel.target_node, tc ? 1 : frames, mint, settings.anim_freq);
|
||||
bool tc = track.data.size() == track.components;
|
||||
|
||||
std::string scratch;
|
||||
StreamFormat format = writeKeyframeStream(scratch, channel.target_path, track);
|
||||
StreamFormat format = writeKeyframeStream(scratch, track.path, track.data);
|
||||
|
||||
size_t view = getBufferView(views, BufferView::Kind_Keyframe, channel.target_path, format.stride, settings.compress && channel.target_path != cgltf_animation_path_type_weights);
|
||||
size_t view = getBufferView(views, BufferView::Kind_Keyframe, track.path, format.stride, settings.compress && track.path != cgltf_animation_path_type_weights);
|
||||
size_t offset = views[view].data.size();
|
||||
views[view].data += scratch;
|
||||
|
||||
comma(json_accessors);
|
||||
writeAccessor(json_accessors, view, offset, format.type, format.component_type, format.normalized, track.size());
|
||||
writeAccessor(json_accessors, view, offset, format.type, format.component_type, format.normalized, track.data.size());
|
||||
|
||||
size_t data_accr = accr_offset++;
|
||||
|
||||
@@ -3446,10 +3528,10 @@ void writeAnimation(std::string& json, std::vector<BufferView>& views, std::stri
|
||||
append(json_samplers, data_accr);
|
||||
append(json_samplers, "}");
|
||||
|
||||
const NodeInfo& tni = nodes[channel.target_node - data->nodes];
|
||||
const NodeInfo& tni = nodes[track.node - data->nodes];
|
||||
size_t target_node = size_t(tni.remap);
|
||||
|
||||
if (channel.target_path == cgltf_animation_path_type_weights)
|
||||
if (track.path == cgltf_animation_path_type_weights)
|
||||
{
|
||||
assert(tni.meshes.size() == 1);
|
||||
target_node = tni.meshes[0];
|
||||
@@ -3461,7 +3543,7 @@ void writeAnimation(std::string& json, std::vector<BufferView>& views, std::stri
|
||||
append(json_channels, ",\"target\":{\"node\":");
|
||||
append(json_channels, target_node);
|
||||
append(json_channels, ",\"path\":\"");
|
||||
append(json_channels, animationPath(channel.target_path));
|
||||
append(json_channels, animationPath(track.path));
|
||||
append(json_channels, "\"}}");
|
||||
|
||||
track_offset++;
|
||||
@@ -3732,17 +3814,23 @@ void printAttributeStats(const std::vector<BufferView>& views, BufferView::Kind
|
||||
}
|
||||
}
|
||||
|
||||
void process(cgltf_data* data, const char* input_path, const char* output_path, std::vector<Mesh>& meshes, const Settings& settings, std::string& json, std::string& bin, std::string& fallback)
|
||||
void process(cgltf_data* data, const char* input_path, const char* output_path, std::vector<Mesh>& meshes, std::vector<Animation>& animations, const Settings& settings, std::string& json, std::string& bin, std::string& fallback)
|
||||
{
|
||||
if (settings.verbose)
|
||||
{
|
||||
printf("input: %d nodes, %d meshes (%d primitives), %d materials, %d skins, %d animations\n",
|
||||
int(data->nodes_count), int(data->meshes_count), int(meshes.size()), int(data->materials_count), int(data->skins_count), int(data->animations_count));
|
||||
int(data->nodes_count), int(data->meshes_count), int(meshes.size()), int(data->materials_count), int(data->skins_count), int(animations.size()));
|
||||
printMeshStats(meshes, "input");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < animations.size(); ++i)
|
||||
{
|
||||
processAnimation(animations[i], settings);
|
||||
}
|
||||
|
||||
std::vector<NodeInfo> nodes(data->nodes_count);
|
||||
|
||||
markAnimated(data, nodes);
|
||||
markAnimated(data, nodes, animations);
|
||||
|
||||
for (size_t i = 0; i < meshes.size(); ++i)
|
||||
{
|
||||
@@ -3771,17 +3859,12 @@ void process(cgltf_data* data, const char* input_path, const char* output_path,
|
||||
mergeMeshes(meshes, settings);
|
||||
filterEmptyMeshes(meshes);
|
||||
|
||||
markNeededNodes(data, nodes, meshes, settings);
|
||||
markNeededNodes(data, nodes, meshes, animations, settings);
|
||||
|
||||
std::vector<MaterialInfo> materials(data->materials_count);
|
||||
|
||||
markNeededMaterials(data, materials, meshes);
|
||||
|
||||
if (settings.verbose)
|
||||
{
|
||||
printMeshStats(meshes, "input");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < meshes.size(); ++i)
|
||||
{
|
||||
processMesh(meshes[i], settings);
|
||||
@@ -3789,11 +3872,6 @@ void process(cgltf_data* data, const char* input_path, const char* output_path,
|
||||
|
||||
filterEmptyMeshes(meshes); // some meshes may become empty after processing
|
||||
|
||||
if (settings.verbose)
|
||||
{
|
||||
printMeshStats(meshes, "output");
|
||||
}
|
||||
|
||||
std::vector<ImageInfo> images(data->images_count);
|
||||
|
||||
analyzeImages(data, images);
|
||||
@@ -4026,11 +4104,11 @@ void process(cgltf_data* data, const char* input_path, const char* output_path,
|
||||
append(json_skins, "}");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < data->animations_count; ++i)
|
||||
for (size_t i = 0; i < animations.size(); ++i)
|
||||
{
|
||||
const cgltf_animation& animation = data->animations[i];
|
||||
const Animation& animation = animations[i];
|
||||
|
||||
writeAnimation(json_animations, views, json_accessors, accr_offset, animation, data, nodes, settings);
|
||||
writeAnimation(json_animations, views, json_accessors, accr_offset, animation, i, data, nodes, settings);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < data->cameras_count; ++i)
|
||||
@@ -4110,6 +4188,7 @@ void process(cgltf_data* data, const char* input_path, const char* output_path,
|
||||
|
||||
if (settings.verbose)
|
||||
{
|
||||
printMeshStats(meshes, "output");
|
||||
printSceneStats(views, meshes, node_offset, mesh_offset, material_offset, json.size(), bin.size());
|
||||
}
|
||||
|
||||
@@ -4212,6 +4291,7 @@ int gltfpack(const char* input, const char* output, const Settings& settings)
|
||||
{
|
||||
cgltf_data* data = 0;
|
||||
std::vector<Mesh> meshes;
|
||||
std::vector<Animation> animations;
|
||||
|
||||
const char* iext = strrchr(input, '.');
|
||||
|
||||
@@ -4240,7 +4320,8 @@ int gltfpack(const char* input, const char* output, const Settings& settings)
|
||||
return 2;
|
||||
}
|
||||
|
||||
parseMeshesGltf(data, meshes);
|
||||
parseMeshes(data, meshes);
|
||||
parseAnimations(data, animations);
|
||||
}
|
||||
else if (iext && (strcmp(iext, ".obj") == 0 || strcmp(iext, ".OBJ") == 0))
|
||||
{
|
||||
@@ -4274,7 +4355,7 @@ int gltfpack(const char* input, const char* output, const Settings& settings)
|
||||
}
|
||||
|
||||
std::string json, bin, fallback;
|
||||
process(data, input, output, meshes, settings, json, bin, fallback);
|
||||
process(data, input, output, meshes, animations, settings, json, bin, fallback);
|
||||
|
||||
cgltf_free(data);
|
||||
|
||||
|
||||
93
3rdparty/meshoptimizer/tools/lodviewer.cpp
vendored
93
3rdparty/meshoptimizer/tools/lodviewer.cpp
vendored
@@ -49,6 +49,9 @@ struct Mesh
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<unsigned int> indices;
|
||||
|
||||
bool hasnormals;
|
||||
bool hastexture;
|
||||
|
||||
// TODO: this is debug only visualization and will go away at some point
|
||||
std::vector<unsigned char> kinds;
|
||||
std::vector<unsigned int> loop;
|
||||
@@ -73,6 +76,9 @@ Mesh parseObj(const char* path)
|
||||
size_t vertex_offset = 0;
|
||||
size_t index_offset = 0;
|
||||
|
||||
bool hasnormals = false;
|
||||
bool hastexture = false;
|
||||
|
||||
for (unsigned int i = 0; i < obj->face_count; ++i)
|
||||
{
|
||||
for (unsigned int j = 0; j < obj->face_vertices[i]; ++j)
|
||||
@@ -91,6 +97,9 @@ Mesh parseObj(const char* path)
|
||||
obj->texcoords[gi.t * 2 + 1],
|
||||
};
|
||||
|
||||
hasnormals |= (gi.n > 0);
|
||||
hastexture |= (gi.t > 0);
|
||||
|
||||
// triangulate polygon on the fly; offset-3 is always the first polygon vertex
|
||||
if (j >= 3)
|
||||
{
|
||||
@@ -119,6 +128,9 @@ Mesh parseObj(const char* path)
|
||||
result.vertices.resize(total_vertices);
|
||||
meshopt_remapVertexBuffer(&result.vertices[0], &vertices[0], total_indices, sizeof(Vertex), &remap[0]);
|
||||
|
||||
result.hasnormals = hasnormals;
|
||||
result.hastexture = hastexture;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -188,6 +200,9 @@ Mesh parseGltf(const char* path)
|
||||
size_t vertex_offset = 0;
|
||||
size_t index_offset = 0;
|
||||
|
||||
bool hasnormals = false;
|
||||
bool hastexture = false;
|
||||
|
||||
for (size_t ni = 0; ni < data->nodes_count; ++ni)
|
||||
{
|
||||
if (!data->nodes[ni].mesh)
|
||||
@@ -234,6 +249,8 @@ Mesh parseGltf(const char* path)
|
||||
result.vertices[vertex_offset + i].ny = ptr[0] * transform[1] + ptr[1] * transform[5] + ptr[2] * transform[9];
|
||||
result.vertices[vertex_offset + i].nz = ptr[0] * transform[2] + ptr[1] * transform[6] + ptr[2] * transform[10];
|
||||
}
|
||||
|
||||
hasnormals = true;
|
||||
}
|
||||
|
||||
if (cgltf_accessor* at = getAccessor(primitive.attributes, primitive.attributes_count, cgltf_attribute_type_texcoord))
|
||||
@@ -246,6 +263,8 @@ Mesh parseGltf(const char* path)
|
||||
result.vertices[vertex_offset + i].tx = ptr[0];
|
||||
result.vertices[vertex_offset + i].ty = ptr[1];
|
||||
}
|
||||
|
||||
hastexture = true;
|
||||
}
|
||||
|
||||
vertex_offset += ap->count;
|
||||
@@ -253,6 +272,9 @@ Mesh parseGltf(const char* path)
|
||||
}
|
||||
}
|
||||
|
||||
result.hasnormals = hasnormals;
|
||||
result.hastexture = hastexture;
|
||||
|
||||
std::vector<unsigned int> remap(total_indices);
|
||||
size_t unique_vertices = meshopt_generateVertexRemap(&remap[0], &result.indices[0], total_indices, &result.vertices[0], total_vertices, sizeof(Vertex));
|
||||
|
||||
@@ -290,8 +312,12 @@ bool saveObj(const Mesh& mesh, const char* path)
|
||||
for (size_t i = 0; i < vertcount; ++i)
|
||||
{
|
||||
fprintf(obj, "v %f %f %f\n", verts[i].px, verts[i].py, verts[i].pz);
|
||||
fprintf(obj, "vn %f %f %f\n", verts[i].nx, verts[i].ny, verts[i].nz);
|
||||
fprintf(obj, "vt %f %f %f\n", verts[i].tx, verts[i].ty, 0.f);
|
||||
|
||||
if (mesh.hasnormals)
|
||||
fprintf(obj, "vn %f %f %f\n", verts[i].nx, verts[i].ny, verts[i].nz);
|
||||
|
||||
if (mesh.hastexture)
|
||||
fprintf(obj, "vt %f %f %f\n", verts[i].tx, verts[i].ty, 0.f);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tris.size(); i += 3)
|
||||
@@ -300,7 +326,14 @@ bool saveObj(const Mesh& mesh, const char* path)
|
||||
unsigned int i1 = tris[i + 1] + 1;
|
||||
unsigned int i2 = tris[i + 2] + 1;
|
||||
|
||||
fprintf(obj, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", i0, i0, i0, i1, i1, i1, i2, i2, i2);
|
||||
if (mesh.hasnormals && mesh.hastexture)
|
||||
fprintf(obj, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", i0, i0, i0, i1, i1, i1, i2, i2, i2);
|
||||
else if (mesh.hasnormals && !mesh.hastexture)
|
||||
fprintf(obj, "f %d//%d %d//%d %d//%d\n", i0, i0, i1, i1, i2, i2);
|
||||
else if (!mesh.hasnormals && mesh.hastexture)
|
||||
fprintf(obj, "f %d/%d %d/%d %d/%d\n", i0, i0, i1, i1, i2, i2);
|
||||
else
|
||||
fprintf(obj, "f %d %d %dd\n", i0, i1, i2);
|
||||
}
|
||||
|
||||
fclose(obj);
|
||||
@@ -324,6 +357,57 @@ Mesh optimize(const Mesh& mesh, int lod)
|
||||
return result;
|
||||
}
|
||||
|
||||
void computeNormals(Mesh& mesh)
|
||||
{
|
||||
if (mesh.hasnormals)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < mesh.vertices.size(); ++i)
|
||||
{
|
||||
Vertex& v = mesh.vertices[i];
|
||||
|
||||
v.nx = v.ny = v.nz = 0.f;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mesh.indices.size(); i += 3)
|
||||
{
|
||||
Vertex& v0 = mesh.vertices[mesh.indices[i + 0]];
|
||||
Vertex& v1 = mesh.vertices[mesh.indices[i + 1]];
|
||||
Vertex& v2 = mesh.vertices[mesh.indices[i + 2]];
|
||||
|
||||
float v10[3] = {v1.px - v0.px, v1.py - v0.py, v1.pz - v0.pz};
|
||||
float v20[3] = {v2.px - v0.px, v2.py - v0.py, v2.pz - v0.pz};
|
||||
|
||||
float normalx = v10[1] * v20[2] - v10[2] * v20[1];
|
||||
float normaly = v10[2] * v20[0] - v10[0] * v20[2];
|
||||
float normalz = v10[0] * v20[1] - v10[1] * v20[0];
|
||||
|
||||
v0.nx += normalx;
|
||||
v0.ny += normaly;
|
||||
v0.nz += normalz;
|
||||
|
||||
v1.nx += normalx;
|
||||
v1.ny += normaly;
|
||||
v1.nz += normalz;
|
||||
|
||||
v2.nx += normalx;
|
||||
v2.ny += normaly;
|
||||
v2.nz += normalz;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mesh.vertices.size(); ++i)
|
||||
{
|
||||
Vertex& v = mesh.vertices[i];
|
||||
|
||||
float nl = sqrtf(v.nx * v.nx + v.ny * v.ny + v.nz * v.nz);
|
||||
float ns = (nl == 0.f) ? 0.f : 1.f / nl;
|
||||
|
||||
v.nx *= ns;
|
||||
v.ny *= ns;
|
||||
v.nz *= ns;
|
||||
}
|
||||
}
|
||||
|
||||
void display(int x, int y, int width, int height, const Mesh& mesh, const Options& options)
|
||||
{
|
||||
glViewport(x, y, width, height);
|
||||
@@ -607,6 +691,9 @@ int main(int argc, char** argv)
|
||||
int x = int(i) % cols;
|
||||
int y = int(i) / cols;
|
||||
|
||||
if (options.mode == Options::Mode_Normals)
|
||||
computeNormals(f.lodmesh);
|
||||
|
||||
display(x * tilew, y * tileh, tilew, tileh, f.lodmesh, options);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user