fixed oversight in uf::Mesh::convert, fixed texture streaming not working in OpenGL (the graphic.material.textures weren't updated) fixed missing texture being used in VXGI for vulkan (think it was from reinitializing the texture object itself if it was aliased rather than just resetting the handles)
This commit is contained in:
parent
9ec35a26e6
commit
ab9aa0ae13
1
.gitignore
vendored
1
.gitignore
vendored
@ -34,6 +34,7 @@
|
||||
*.app
|
||||
*.spv
|
||||
*.elf
|
||||
*.elf.unstripped
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
@ -78,7 +78,7 @@
|
||||
"stream": {
|
||||
"tag": "worldspawn",
|
||||
"player": "info_player_spawn",
|
||||
"enabled": "auto",
|
||||
"enabled": true, // "auto",
|
||||
"radius": 32,
|
||||
"every": 4
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
vulkan
|
||||
opengl
|
||||
@ -158,7 +158,10 @@ namespace uf {
|
||||
} bounds;
|
||||
*/
|
||||
uf::stl::vector<buffer_t> buffers;
|
||||
uf::stl::vector<uf::stl::string> buffer_paths; // crunge, but it's better this way
|
||||
|
||||
// crunge, but it's better this way
|
||||
uf::stl::vector<uf::stl::string> buffer_paths;
|
||||
uf::stl::vector<ext::RENDERER::AttributeDescriptor> buffer_descriptors;
|
||||
protected:
|
||||
void _destroy( uf::Mesh::Input& input );
|
||||
void _bind( bool interleaved = uf::Mesh::defaultInterleaved );
|
||||
@ -300,32 +303,42 @@ namespace uf {
|
||||
|
||||
template<typename From, typename To>
|
||||
void convert() {
|
||||
// if mesh data is interleaved, skip conversion
|
||||
if ( this->isInterleaved() ) {
|
||||
UF_MSG_DEBUG("Downcasting/upcasting requested yet mesh is interleaved, ignoring...");
|
||||
return;
|
||||
}
|
||||
|
||||
auto fromEnum = uf::renderer::typeToEnum<From>();
|
||||
auto toEnum = uf::renderer::typeToEnum<To>();
|
||||
auto toEnum = uf::renderer::typeToEnum<To>();
|
||||
if ( toEnum == fromEnum ) return;
|
||||
|
||||
for ( auto& attribute : this->vertex.attributes ) {
|
||||
if ( attribute.descriptor.type == toEnum ) continue;
|
||||
if ( attribute.descriptor.type != fromEnum ) continue;
|
||||
|
||||
float scale = (float) sizeof(To) / (float) sizeof(From);
|
||||
|
||||
size_t elements = this->vertex.count * attribute.descriptor.components;
|
||||
size_t bytes = elements * sizeof(To);
|
||||
|
||||
size_t elementCount = this->vertex.count * attribute.descriptor.components;
|
||||
auto& srcBuffer = this->buffers[attribute.buffer];
|
||||
|
||||
if ( srcBuffer.empty() ) continue;
|
||||
|
||||
uf::stl::vector<uint8_t> dstBuffer( srcBuffer.size() * scale );
|
||||
uf::stl::vector<uint8_t> dstBuffer( elementCount * sizeof(To) );
|
||||
|
||||
attribute.pointer = (uint8_t*) dstBuffer.data();
|
||||
attribute.length *= scale;
|
||||
const From* srcPtr = (const From*) (srcBuffer.data());
|
||||
To* dstPtr = (To*) (dstBuffer.data());
|
||||
|
||||
if ( toEnum == uf::renderer::enums::Type::USHORT )
|
||||
for ( size_t i = 0; i < elementCount; ++i ) dstPtr[i] = uf::quant::quantize_f32u16(srcPtr[i]);
|
||||
else if ( fromEnum == uf::renderer::enums::Type::USHORT )
|
||||
for ( size_t i = 0; i < elementCount; ++i) dstPtr[i] = uf::quant::dequantize_u16f32(srcPtr[i]);
|
||||
else
|
||||
for ( size_t i = 0; i < elementCount; ++i ) dstPtr[i] = (To) srcPtr[i];
|
||||
|
||||
srcBuffer.swap( dstBuffer );
|
||||
|
||||
attribute.pointer = (uint8_t*) ( srcBuffer.data() );
|
||||
attribute.descriptor.type = toEnum;
|
||||
attribute.descriptor.size *= scale;
|
||||
attribute.descriptor.size = sizeof(To) * attribute.descriptor.components;
|
||||
attribute.length = sizeof(To) * elementCount;
|
||||
|
||||
if ( toEnum == uf::renderer::enums::Type::FLOAT ) {
|
||||
switch ( attribute.descriptor.components ) {
|
||||
@ -334,14 +347,16 @@ namespace uf {
|
||||
case 3: attribute.descriptor.format = uf::renderer::enums::Format::R32G32B32_SFLOAT; break;
|
||||
case 4: attribute.descriptor.format = uf::renderer::enums::Format::R32G32B32A32_SFLOAT; break;
|
||||
}
|
||||
} else if ( toEnum == uf::renderer::enums::Type::FLOAT16 ) {
|
||||
}
|
||||
else if ( toEnum == uf::renderer::enums::Type::FLOAT16 ) {
|
||||
switch ( attribute.descriptor.components ) {
|
||||
case 1: attribute.descriptor.format = uf::renderer::enums::Format::R16_SFLOAT; break;
|
||||
case 2: attribute.descriptor.format = uf::renderer::enums::Format::R16G16_SFLOAT; break;
|
||||
case 3: attribute.descriptor.format = uf::renderer::enums::Format::R16G16B16_SFLOAT; break;
|
||||
case 4: attribute.descriptor.format = uf::renderer::enums::Format::R16G16B16A16_SFLOAT; break;
|
||||
}
|
||||
} else if ( toEnum == uf::renderer::enums::Type::USHORT ) {
|
||||
}
|
||||
else if ( toEnum == uf::renderer::enums::Type::USHORT ) {
|
||||
switch ( attribute.descriptor.components ) {
|
||||
case 1: attribute.descriptor.format = uf::renderer::enums::Format::R16_UINT; break;
|
||||
case 2: attribute.descriptor.format = uf::renderer::enums::Format::R16G16_UINT; break;
|
||||
@ -349,20 +364,6 @@ namespace uf {
|
||||
case 4: attribute.descriptor.format = uf::renderer::enums::Format::R16G16B16A16_UINT; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
From* srcPtr = (From*) (srcBuffer.data());
|
||||
To* dstPtr = (To*) (dstBuffer.data());
|
||||
|
||||
if ( toEnum == uf::renderer::enums::Type::USHORT ) {
|
||||
for ( size_t i = 0; i < elements; ++i ) dstPtr[i] = uf::quant::quantize_f32u16(srcPtr[i]);
|
||||
} else if ( fromEnum == uf::renderer::enums::Type::USHORT ) {
|
||||
for ( size_t i = 0; i < elements; ++i ) dstPtr[i] = uf::quant::dequantize_u16f32(srcPtr[i]);
|
||||
} else {
|
||||
for ( size_t i = 0; i < elements; ++i ) dstPtr[i] = srcPtr[i];
|
||||
}
|
||||
|
||||
srcBuffer.swap( dstBuffer );
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -421,10 +422,10 @@ namespace ext {
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<ext::RENDERER::GraphicDescriptor> {
|
||||
size_t operator()(const ext::RENDERER::GraphicDescriptor& descriptor) const { return descriptor.hash(); }
|
||||
};
|
||||
template <>
|
||||
struct hash<ext::RENDERER::GraphicDescriptor> {
|
||||
size_t operator()(const ext::RENDERER::GraphicDescriptor& descriptor) const { return descriptor.hash(); }
|
||||
};
|
||||
}
|
||||
|
||||
#undef UF_RENDERER
|
||||
|
||||
@ -231,6 +231,7 @@ namespace {
|
||||
attribute.offset = value["offset"].as(attribute.offset);\
|
||||
attribute.stride = value["stride"].as(attribute.stride);\
|
||||
attribute.length = value["length"].as(attribute.length);\
|
||||
mesh.buffer_descriptors.emplace_back(attribute.descriptor);\
|
||||
});\
|
||||
}
|
||||
|
||||
@ -240,6 +241,8 @@ namespace {
|
||||
DESERIALIZE_MESH(indirect);
|
||||
|
||||
mesh.buffers.reserve( json["buffers"].size() );
|
||||
mesh.buffer_paths.reserve( json["buffers"].size() );
|
||||
mesh.buffer_descriptors.reserve( json["buffers"].size() );
|
||||
ext::json::forEach( json["buffers"], [&]( ext::json::Value& value ){
|
||||
const uf::stl::string filename = value.as<uf::stl::string>();
|
||||
const uf::stl::string directory = uf::io::directory( graph.name );
|
||||
@ -298,7 +301,6 @@ namespace {
|
||||
{
|
||||
#if UF_ENV_DREAMCAST && GL_QUANTIZED_SHORT
|
||||
mesh.convert<float, uint16_t>();
|
||||
UF_MSG_DEBUG("Quantizing mesh to GL_QUANTIZED_SHORT");
|
||||
#else
|
||||
auto conversion = graph.metadata["decode"]["conversion"].as<uf::stl::string>();
|
||||
if ( conversion != "" ) {
|
||||
@ -314,8 +316,9 @@ namespace {
|
||||
else if ( conversion == "float" ) mesh.convert<uint16_t, float>();
|
||||
}
|
||||
#endif
|
||||
mesh.updateDescriptor();
|
||||
}
|
||||
|
||||
mesh.updateDescriptor();
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
#if UF_USE_OPENGL
|
||||
#define UF_GRAPH_SPARSE_READ_MESH 1
|
||||
#else
|
||||
#define UF_GRAPH_SPARSE_READ_MESH 0
|
||||
#define UF_GRAPH_SPARSE_READ_MESH 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -44,6 +44,40 @@ namespace {
|
||||
return hash;
|
||||
}
|
||||
|
||||
void bindTextures( uf::renderer::Graphic& graphic ) {
|
||||
graphic.material.textures.clear();
|
||||
|
||||
auto& scene = uf::scene::getCurrentScene();
|
||||
auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent<pod::Graph::Storage>();
|
||||
|
||||
// for ( auto& s : graph.samplers ) graphic.material.samplers.emplace_back( storage.samplers.map[s] );
|
||||
// for ( auto pair : storage.samplers.map ) graphic.material.samplers.emplace_back( pair.second );
|
||||
// for ( auto& key : storage.samplers.keys ) graphic.material.samplers.emplace_back( storage.samplers.map[key] );
|
||||
|
||||
// for ( auto& i : graph.images ) graphic.material.textures.emplace_back().aliasTexture( storage.texture2Ds.map[i] );
|
||||
// for ( auto pair : storage.texture2Ds.map ) graphic.material.textures.emplace_back().aliasTexture( pair.second );
|
||||
|
||||
for ( auto& key : storage.texture2Ds.keys ) graphic.material.textures.emplace_back().aliasTexture( storage.texture2Ds.map[key] );
|
||||
|
||||
// bind scene's voxel texture
|
||||
#if UF_USE_VULKAN
|
||||
if ( uf::renderer::settings::pipelines::vxgi ) {
|
||||
auto& scene = uf::scene::getCurrentScene();
|
||||
auto& sceneTextures = scene.getComponent<pod::SceneTextures>();
|
||||
for ( auto& t : sceneTextures.voxels.drawId ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.instanceId ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.normalX ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.normalY ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.radianceR ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.radianceG ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.radianceB ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.radianceA ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.count ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.output ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// lazy load animations if requested
|
||||
void loadAnimation( const uf::stl::string& name ) {
|
||||
auto& scene = uf::scene::getCurrentScene();
|
||||
@ -344,34 +378,7 @@ void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::M
|
||||
#endif
|
||||
*/
|
||||
|
||||
{
|
||||
// for ( auto& s : graph.samplers ) graphic.material.samplers.emplace_back( storage.samplers.map[s] );
|
||||
// for ( auto pair : storage.samplers.map ) graphic.material.samplers.emplace_back( pair.second );
|
||||
// for ( auto& key : storage.samplers.keys ) graphic.material.samplers.emplace_back( storage.samplers.map[key] );
|
||||
|
||||
// for ( auto& i : graph.images ) graphic.material.textures.emplace_back().aliasTexture( storage.texture2Ds.map[i] );
|
||||
// for ( auto pair : storage.texture2Ds.map ) graphic.material.textures.emplace_back().aliasTexture( pair.second );
|
||||
|
||||
for ( auto& key : storage.texture2Ds.keys ) graphic.material.textures.emplace_back().aliasTexture( storage.texture2Ds.map[key] );
|
||||
|
||||
// bind scene's voxel texture
|
||||
#if UF_USE_VULKAN
|
||||
if ( uf::renderer::settings::pipelines::vxgi ) {
|
||||
auto& scene = uf::scene::getCurrentScene();
|
||||
auto& sceneTextures = scene.getComponent<pod::SceneTextures>();
|
||||
for ( auto& t : sceneTextures.voxels.drawId ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.instanceId ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.normalX ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.normalY ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.radianceR ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.radianceG ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.radianceB ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.radianceA ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.count ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
for ( auto& t : sceneTextures.voxels.output ) graphic.material.textures.emplace_back().aliasTexture(t);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
::bindTextures( graphic );
|
||||
|
||||
uf::stl::string root = uf::io::directory( graph.name );
|
||||
size_t texture2Ds = 0;
|
||||
@ -2166,21 +2173,18 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
|
||||
mesh.index.count += drawCommand.indices;
|
||||
}
|
||||
|
||||
// load mesh data
|
||||
for ( auto& attribute : mesh.index.attributes ) {
|
||||
if ( ranges.count(attribute.buffer) <= 0 ) {
|
||||
mesh.buffers[attribute.buffer].clear();
|
||||
} else {
|
||||
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer], ranges[attribute.buffer] );
|
||||
#define STREAM_MESH_DATA( N ) \
|
||||
for ( auto& attribute : mesh.N.attributes ) {\
|
||||
if ( ranges.count(attribute.buffer) <= 0 ) { \
|
||||
mesh.buffers[attribute.buffer].clear();\
|
||||
} else {\
|
||||
attribute.descriptor = mesh.buffer_descriptors[attribute.buffer];\
|
||||
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer], ranges[attribute.buffer] );\
|
||||
}\
|
||||
}
|
||||
}
|
||||
for ( auto& attribute : mesh.vertex.attributes ) {
|
||||
if ( ranges.count(attribute.buffer) <= 0 ) {
|
||||
mesh.buffers[attribute.buffer].clear();
|
||||
} else {
|
||||
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer], ranges[attribute.buffer] );
|
||||
}
|
||||
}
|
||||
|
||||
STREAM_MESH_DATA( index );
|
||||
STREAM_MESH_DATA( vertex );
|
||||
// keep the vertex data intact
|
||||
#else
|
||||
// disable remaining draw commands
|
||||
@ -2197,13 +2201,15 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
|
||||
}
|
||||
}
|
||||
|
||||
// load mesh data
|
||||
for ( auto& attribute : mesh.index.attributes ) {
|
||||
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] );
|
||||
}
|
||||
for ( auto& attribute : mesh.vertex.attributes ) {
|
||||
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] );
|
||||
}
|
||||
#define STREAM_MESH_DATA( N ) \
|
||||
for ( auto& attribute : mesh.N.attributes ) {\
|
||||
if ( !mesh.buffers[attribute.buffer].empty() || mesh.buffer_paths.empty() ) continue;\
|
||||
attribute.descriptor = mesh.buffer_descriptors[attribute.buffer];\
|
||||
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] );\
|
||||
}
|
||||
|
||||
STREAM_MESH_DATA( index );
|
||||
STREAM_MESH_DATA( vertex );
|
||||
#endif
|
||||
|
||||
if ( graph.settings.stream.textures ) {
|
||||
@ -2239,13 +2245,11 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
|
||||
}
|
||||
|
||||
// iterate through our ref counts
|
||||
// to-do: figure out why this doesn't work for OpenGL (texture ID handles might be wrong, might be better to store the old texture ID handle to use it and then update to that handle)
|
||||
for ( auto& [ key, count ] : textureReferences ) {
|
||||
auto& texture = storage.texture2Ds[key];
|
||||
auto& image = storage.images[key];
|
||||
bool visible = count > 0;
|
||||
|
||||
// load texture
|
||||
if ( visible && (!texture.generated() || texture.aliased) ) {
|
||||
// load image
|
||||
if ( image.getPixels().empty() ) image.open(image.getFilename(), false);
|
||||
@ -2264,7 +2268,13 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
|
||||
|
||||
// avoids manipulating the aliased texture
|
||||
if ( texture.aliased ) {
|
||||
texture = {};
|
||||
texture.aliased = false;
|
||||
#if UF_USE_OPENGL
|
||||
texture.image = 0;
|
||||
#else
|
||||
texture.image = {};
|
||||
texture.view = {};
|
||||
#endif
|
||||
}
|
||||
|
||||
texture.sampler.descriptor.filter.min = filter;
|
||||
@ -2275,7 +2285,7 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
|
||||
#if UF_ENV_DREAMCAST
|
||||
image.clear();
|
||||
#endif
|
||||
} else if ( !visible && texture.generated() ) {
|
||||
} else if ( !visible && (texture.generated() && !texture.aliased) ) {
|
||||
// unload image
|
||||
image.clear();
|
||||
// defer destruction of texture
|
||||
@ -2286,20 +2296,39 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
|
||||
}
|
||||
}
|
||||
} else { // this shouldn't be reached
|
||||
// load mesh data
|
||||
for ( auto& attribute : mesh.index.attributes ) {
|
||||
if ( !mesh.buffers[attribute.buffer].empty() || mesh.buffer_paths.empty() ) continue;
|
||||
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] );
|
||||
}
|
||||
for ( auto& attribute : mesh.vertex.attributes ) {
|
||||
if ( !mesh.buffers[attribute.buffer].empty() || mesh.buffer_paths.empty() ) continue;
|
||||
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] );
|
||||
#define LOAD_MESH_DATA( N ) \
|
||||
for ( auto& attribute : mesh.N.attributes ) {\
|
||||
if ( !mesh.buffers[attribute.buffer].empty() || mesh.buffer_paths.empty() ) continue;\
|
||||
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] );\
|
||||
}
|
||||
|
||||
LOAD_MESH_DATA( index );
|
||||
LOAD_MESH_DATA( vertex );
|
||||
}
|
||||
|
||||
{
|
||||
#if UF_ENV_DREAMCAST && GL_QUANTIZED_SHORT
|
||||
mesh.convert<float, uint16_t>();
|
||||
#else
|
||||
auto conversion = graph.metadata["decode"]["conversion"].as<uf::stl::string>();
|
||||
if ( conversion != "" ) {
|
||||
#if UF_USE_FLOAT16
|
||||
if ( conversion == "float16" ) mesh.convert<float, float16>();
|
||||
else if ( conversion == "float" ) mesh.convert<float16, float>();
|
||||
#endif
|
||||
#if UF_USE_BFLOAT16
|
||||
if ( conversion == "bfloat16" ) mesh.convert<float, bfloat16>();
|
||||
else if ( conversion == "float" ) mesh.convert<bfloat16, float>();
|
||||
#endif
|
||||
if ( conversion == "uint16_t" ) mesh.convert<float, uint16_t>();
|
||||
else if ( conversion == "float" ) mesh.convert<uint16_t, float>();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
mesh.updateDescriptor();
|
||||
// may or may not be necessary (OpenGL might need a re-record, Vulkan seems fine for the main deferred pass but VXGI doesn't ever get to update since null textures get used sometimes)
|
||||
uf::renderer::states::rebuild = true;
|
||||
|
||||
// uf::renderer::states::rebuild = true;
|
||||
|
||||
// update graphic
|
||||
if ( /*(graph.metadata["renderer"]["separate"].as<bool>()) &&*/ graph.metadata["renderer"]["render"].as<bool>() ) {
|
||||
@ -2310,11 +2339,13 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
|
||||
if ( rebuild ) {
|
||||
// uf::renderer::states::rebuild = true;
|
||||
}
|
||||
::bindTextures( graphic );
|
||||
} else {
|
||||
uf::graph::initializeGraphics( graph, entity, mesh );
|
||||
}
|
||||
}
|
||||
// bind mesh to physics state
|
||||
// to-do: figure out why the mesh just suddenly breaks when re-streamed in dreamcast (could just be the version of reactphysics)
|
||||
{
|
||||
auto phyziks = tag["physics"];
|
||||
if ( !ext::json::isObject( phyziks ) ) phyziks = metadataJson["physics"];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user