|
|
@@ -228,81 +228,134 @@ namespace mesh_resource_internal
|
|
|
output[i] = (u16)sjson::parse_int(indices[i]);
|
|
|
}
|
|
|
|
|
|
- struct MeshCompiler
|
|
|
+ namespace mesh
|
|
|
{
|
|
|
- CompileOptions &_opts;
|
|
|
-
|
|
|
- Array<f32> _positions;
|
|
|
- Array<f32> _normals;
|
|
|
- Array<f32> _uvs;
|
|
|
- Array<f32> _tangents;
|
|
|
- Array<f32> _binormals;
|
|
|
-
|
|
|
- Array<u16> _position_indices;
|
|
|
- Array<u16> _normal_indices;
|
|
|
- Array<u16> _uv_indices;
|
|
|
- Array<u16> _tangent_indices;
|
|
|
- Array<u16> _binormal_indices;
|
|
|
-
|
|
|
- u32 _vertex_stride;
|
|
|
- Array<char> _vertex_buffer;
|
|
|
- Array<u16> _index_buffer;
|
|
|
-
|
|
|
- AABB _aabb;
|
|
|
- OBB _obb;
|
|
|
-
|
|
|
- bgfx::VertexLayout _layout;
|
|
|
-
|
|
|
- bool _has_normal;
|
|
|
- bool _has_uv;
|
|
|
-
|
|
|
- explicit MeshCompiler(CompileOptions &opts)
|
|
|
- : _opts(opts)
|
|
|
- , _positions(default_allocator())
|
|
|
- , _normals(default_allocator())
|
|
|
- , _uvs(default_allocator())
|
|
|
- , _tangents(default_allocator())
|
|
|
- , _binormals(default_allocator())
|
|
|
- , _position_indices(default_allocator())
|
|
|
- , _normal_indices(default_allocator())
|
|
|
- , _uv_indices(default_allocator())
|
|
|
- , _tangent_indices(default_allocator())
|
|
|
- , _binormal_indices(default_allocator())
|
|
|
- , _vertex_stride(0)
|
|
|
- , _vertex_buffer(default_allocator())
|
|
|
- , _index_buffer(default_allocator())
|
|
|
- , _has_normal(false)
|
|
|
- , _has_uv(false)
|
|
|
+ s32 parse_nodes(Mesh &m, const char *sjson, CompileOptions &opts);
|
|
|
+
|
|
|
+ s32 parse_node(Node &n, const char *sjson, Mesh *mesh, CompileOptions &opts)
|
|
|
+ {
|
|
|
+ TempAllocator4096 ta;
|
|
|
+ JsonObject obj(ta);
|
|
|
+ sjson::parse(obj, sjson);
|
|
|
+
|
|
|
+ n._local_pose = sjson::parse_matrix4x4(obj["matrix_local"]);
|
|
|
+
|
|
|
+ if (json_object::has(obj, "children")) {
|
|
|
+ s32 err = mesh::parse_nodes(*mesh, obj["children"], opts);
|
|
|
+ DATA_COMPILER_ENSURE(err == 0, opts);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ void reset(Geometry &g)
|
|
|
+ {
|
|
|
+ array::clear(g._positions);
|
|
|
+ array::clear(g._normals);
|
|
|
+ array::clear(g._uvs);
|
|
|
+ array::clear(g._tangents);
|
|
|
+ array::clear(g._binormals);
|
|
|
+
|
|
|
+ array::clear(g._position_indices);
|
|
|
+ array::clear(g._normal_indices);
|
|
|
+ array::clear(g._uv_indices);
|
|
|
+ array::clear(g._tangent_indices);
|
|
|
+ array::clear(g._binormal_indices);
|
|
|
+
|
|
|
+ array::clear(g._vertex_buffer);
|
|
|
+ array::clear(g._index_buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool has_normals(Geometry &g)
|
|
|
+ {
|
|
|
+ return array::size(g._normals) != 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool has_uvs(Geometry &g)
|
|
|
+ {
|
|
|
+ return array::size(g._uvs) != 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ u32 vertex_stride(Geometry &g)
|
|
|
{
|
|
|
+ u32 stride = 0;
|
|
|
+ stride += 3 * sizeof(f32);
|
|
|
+ stride += (has_normals(g) ? 3 * sizeof(f32) : 0);
|
|
|
+ stride += (has_uvs(g) ? 2 * sizeof(f32) : 0);
|
|
|
+ return stride;
|
|
|
}
|
|
|
|
|
|
- void reset()
|
|
|
+ bgfx::VertexLayout vertex_layout(Geometry &g)
|
|
|
{
|
|
|
- array::clear(_positions);
|
|
|
- array::clear(_normals);
|
|
|
- array::clear(_uvs);
|
|
|
- array::clear(_tangents);
|
|
|
- array::clear(_binormals);
|
|
|
-
|
|
|
- array::clear(_position_indices);
|
|
|
- array::clear(_normal_indices);
|
|
|
- array::clear(_uv_indices);
|
|
|
- array::clear(_tangent_indices);
|
|
|
- array::clear(_binormal_indices);
|
|
|
-
|
|
|
- _vertex_stride = 0;
|
|
|
- array::clear(_vertex_buffer);
|
|
|
- array::clear(_index_buffer);
|
|
|
-
|
|
|
- aabb::reset(_aabb);
|
|
|
- memset(&_obb, 0, sizeof(_obb));
|
|
|
- memset((void *)&_layout, 0, sizeof(_layout));
|
|
|
-
|
|
|
- _has_normal = false;
|
|
|
- _has_uv = false;
|
|
|
+ bgfx::VertexLayout layout;
|
|
|
+ memset((void *)&layout, 0, sizeof(layout));
|
|
|
+
|
|
|
+ layout.begin();
|
|
|
+ layout.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float);
|
|
|
+
|
|
|
+ if (has_normals(g)) {
|
|
|
+ layout.add(bgfx::Attrib::Normal, 3, bgfx::AttribType::Float, true);
|
|
|
+ }
|
|
|
+ if (has_uvs(g)) {
|
|
|
+ layout.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float);
|
|
|
+ }
|
|
|
+
|
|
|
+ layout.end();
|
|
|
+ return layout;
|
|
|
}
|
|
|
|
|
|
- void parse_indices(const char *json)
|
|
|
+ void generate_vertex_and_index_buffers(Geometry &g)
|
|
|
+ {
|
|
|
+ array::resize(g._index_buffer, array::size(g._position_indices));
|
|
|
+
|
|
|
+ u16 index = 0;
|
|
|
+ for (u32 i = 0; i < array::size(g._position_indices); ++i) {
|
|
|
+ g._index_buffer[i] = index++;
|
|
|
+
|
|
|
+ const u16 p_idx = g._position_indices[i] * 3;
|
|
|
+ Vector3 xyz;
|
|
|
+ xyz.x = g._positions[p_idx + 0];
|
|
|
+ xyz.y = g._positions[p_idx + 1];
|
|
|
+ xyz.z = g._positions[p_idx + 2];
|
|
|
+ array::push(g._vertex_buffer, (char *)&xyz, sizeof(xyz));
|
|
|
+
|
|
|
+ if (has_normals(g)) {
|
|
|
+ const u16 n_idx = g._normal_indices[i] * 3;
|
|
|
+ Vector3 n;
|
|
|
+ n.x = g._normals[n_idx + 0];
|
|
|
+ n.y = g._normals[n_idx + 1];
|
|
|
+ n.z = g._normals[n_idx + 2];
|
|
|
+ array::push(g._vertex_buffer, (char *)&n, sizeof(n));
|
|
|
+ }
|
|
|
+ if (has_uvs(g)) {
|
|
|
+ const u16 t_idx = g._uv_indices[i] * 2;
|
|
|
+ Vector2 uv;
|
|
|
+ uv.x = g._uvs[t_idx + 0];
|
|
|
+ uv.y = g._uvs[t_idx + 1];
|
|
|
+ array::push(g._vertex_buffer, (char *)&uv, sizeof(uv));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ OBB obb(Geometry &g)
|
|
|
+ {
|
|
|
+ AABB aabb;
|
|
|
+ OBB obb;
|
|
|
+ aabb::reset(aabb);
|
|
|
+ memset(&obb, 0, sizeof(obb));
|
|
|
+
|
|
|
+ aabb::from_points(aabb
|
|
|
+ , array::size(g._positions) / 3
|
|
|
+ , sizeof(g._positions[0]) * 3
|
|
|
+ , array::begin(g._positions)
|
|
|
+ );
|
|
|
+
|
|
|
+ obb.tm = from_quaternion_translation(QUATERNION_IDENTITY, aabb::center(aabb));
|
|
|
+ obb.half_extents = (aabb.max - aabb.min) * 0.5f;
|
|
|
+ return obb;
|
|
|
+ }
|
|
|
+
|
|
|
+ s32 parse_indices(Geometry &g, const char *json)
|
|
|
{
|
|
|
TempAllocator4096 ta;
|
|
|
JsonObject obj(ta);
|
|
|
@@ -311,173 +364,200 @@ namespace mesh_resource_internal
|
|
|
JsonArray data_json(ta);
|
|
|
sjson::parse_array(data_json, obj["data"]);
|
|
|
|
|
|
- parse_index_array(_position_indices, data_json[0]);
|
|
|
+ parse_index_array(g._position_indices, data_json[0]);
|
|
|
|
|
|
- if (_has_normal) {
|
|
|
- parse_index_array(_normal_indices, data_json[1]);
|
|
|
+ if (has_normals(g)) {
|
|
|
+ parse_index_array(g._normal_indices, data_json[1]);
|
|
|
}
|
|
|
- if (_has_uv) {
|
|
|
- parse_index_array(_uv_indices, data_json[2]);
|
|
|
+ if (has_uvs(g)) {
|
|
|
+ parse_index_array(g._uv_indices, data_json[2]);
|
|
|
}
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
- void parse(const char *geometry)
|
|
|
+ s32 parse_geometry(Geometry &g, const char *sjson)
|
|
|
{
|
|
|
TempAllocator4096 ta;
|
|
|
JsonObject obj(ta);
|
|
|
- sjson::parse(obj, geometry);
|
|
|
-
|
|
|
- _has_normal = json_object::has(obj, "normal");
|
|
|
- _has_uv = json_object::has(obj, "texcoord");
|
|
|
+ sjson::parse(obj, sjson);
|
|
|
|
|
|
- parse_float_array(_positions, obj["position"]);
|
|
|
+ parse_float_array(g._positions, obj["position"]);
|
|
|
|
|
|
- if (_has_normal) {
|
|
|
- parse_float_array(_normals, obj["normal"]);
|
|
|
+ if (json_object::has(obj, "normal")) {
|
|
|
+ parse_float_array(g._normals, obj["normal"]);
|
|
|
}
|
|
|
- if (_has_uv) {
|
|
|
- parse_float_array(_uvs, obj["texcoord"]);
|
|
|
+ if (json_object::has(obj, "texcoord")) {
|
|
|
+ parse_float_array(g._uvs, obj["texcoord"]);
|
|
|
}
|
|
|
|
|
|
- parse_indices(obj["indices"]);
|
|
|
-
|
|
|
- _vertex_stride = 0;
|
|
|
- _vertex_stride += 3 * sizeof(f32);
|
|
|
- _vertex_stride += (_has_normal ? 3 * sizeof(f32) : 0);
|
|
|
- _vertex_stride += (_has_uv ? 2 * sizeof(f32) : 0);
|
|
|
+ return parse_indices(g, obj["indices"]);
|
|
|
+ }
|
|
|
|
|
|
- // Generate vb/ib
|
|
|
- array::resize(_index_buffer, array::size(_position_indices));
|
|
|
+ s32 parse_geometries(Mesh &m, const char *sjson, CompileOptions &opts)
|
|
|
+ {
|
|
|
+ TempAllocator4096 ta;
|
|
|
+ JsonObject geometries(ta);
|
|
|
+ sjson::parse(geometries, sjson);
|
|
|
|
|
|
- u16 index = 0;
|
|
|
- for (u32 i = 0; i < array::size(_position_indices); ++i) {
|
|
|
- _index_buffer[i] = index++;
|
|
|
+ auto cur = json_object::begin(geometries);
|
|
|
+ auto end = json_object::end(geometries);
|
|
|
+ for (; cur != end; ++cur) {
|
|
|
+ JSON_OBJECT_SKIP_HOLE(geometries, cur);
|
|
|
|
|
|
- const u16 p_idx = _position_indices[i] * 3;
|
|
|
- Vector3 xyz;
|
|
|
- xyz.x = _positions[p_idx + 0];
|
|
|
- xyz.y = _positions[p_idx + 1];
|
|
|
- xyz.z = _positions[p_idx + 2];
|
|
|
- array::push(_vertex_buffer, (char *)&xyz, sizeof(xyz));
|
|
|
+ Geometry geo(default_allocator());
|
|
|
+ s32 err = mesh::parse_geometry(geo, cur->second);
|
|
|
+ DATA_COMPILER_ENSURE(err == 0, opts);
|
|
|
|
|
|
- if (_has_normal) {
|
|
|
- const u16 n_idx = _normal_indices[i] * 3;
|
|
|
- Vector3 n;
|
|
|
- n.x = _normals[n_idx + 0];
|
|
|
- n.y = _normals[n_idx + 1];
|
|
|
- n.z = _normals[n_idx + 2];
|
|
|
- array::push(_vertex_buffer, (char *)&n, sizeof(n));
|
|
|
- }
|
|
|
- if (_has_uv) {
|
|
|
- const u16 t_idx = _uv_indices[i] * 2;
|
|
|
- Vector2 uv;
|
|
|
- uv.x = _uvs[t_idx + 0];
|
|
|
- uv.y = _uvs[t_idx + 1];
|
|
|
- array::push(_vertex_buffer, (char *)&uv, sizeof(uv));
|
|
|
- }
|
|
|
+ DynamicString geometry_name(ta);
|
|
|
+ geometry_name = cur->first;
|
|
|
+ DATA_COMPILER_ASSERT(!hash_map::has(m._geometries, geometry_name)
|
|
|
+ , opts
|
|
|
+ , "Geometry redefined: '%s'"
|
|
|
+ , geometry_name.c_str()
|
|
|
+ );
|
|
|
+ hash_map::set(m._geometries, geometry_name, geo);
|
|
|
}
|
|
|
|
|
|
- // Vertex layout
|
|
|
- _layout.begin();
|
|
|
- _layout.add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
|
|
|
- if (_has_normal) {
|
|
|
- _layout.add(bgfx::Attrib::Normal, 3, bgfx::AttribType::Float, true);
|
|
|
- }
|
|
|
- if (_has_uv) {
|
|
|
- _layout.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float);
|
|
|
- }
|
|
|
+ s32 parse_nodes(Mesh &m, const char *sjson, CompileOptions &opts)
|
|
|
+ {
|
|
|
+ TempAllocator4096 ta;
|
|
|
+ JsonObject nodes(ta);
|
|
|
+ sjson::parse(nodes, sjson);
|
|
|
|
|
|
- _layout.end();
|
|
|
+ auto cur = json_object::begin(nodes);
|
|
|
+ auto end = json_object::end(nodes);
|
|
|
+ for (; cur != end; ++cur) {
|
|
|
+ JSON_OBJECT_SKIP_HOLE(nodes, cur);
|
|
|
|
|
|
- // Bounds
|
|
|
- aabb::from_points(_aabb
|
|
|
- , array::size(_positions) / 3
|
|
|
- , sizeof(_positions[0]) * 3
|
|
|
- , array::begin(_positions)
|
|
|
- );
|
|
|
+ Node node(default_allocator());
|
|
|
+ s32 err = mesh::parse_node(node, cur->second, &m, opts);
|
|
|
+ DATA_COMPILER_ENSURE(err == 0, opts);
|
|
|
+
|
|
|
+ DynamicString node_name(ta);
|
|
|
+ node_name = cur->first;
|
|
|
+ DATA_COMPILER_ASSERT(!hash_map::has(m._nodes, node_name)
|
|
|
+ , opts
|
|
|
+ , "Node redefined: '%s'"
|
|
|
+ , node_name.c_str()
|
|
|
+ );
|
|
|
+
|
|
|
+ hash_map::set(m._nodes, node_name, node);
|
|
|
+ }
|
|
|
|
|
|
- _obb.tm = from_quaternion_translation(QUATERNION_IDENTITY, aabb::center(_aabb));
|
|
|
- _obb.half_extents = (_aabb.max - _aabb.min) * 0.5f;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
- void write()
|
|
|
+ s32 parse(Mesh &m, Buffer &buf, CompileOptions &opts)
|
|
|
{
|
|
|
- BgfxWriter writer(_opts._binary_writer);
|
|
|
- bgfx::write(&writer, _layout);
|
|
|
- _opts.write(_obb);
|
|
|
+ TempAllocator4096 ta;
|
|
|
+ JsonObject nodes(ta);
|
|
|
+ JsonObject obj(ta);
|
|
|
+ sjson::parse(obj, buf);
|
|
|
|
|
|
- _opts.write(array::size(_vertex_buffer) / _vertex_stride);
|
|
|
- _opts.write(_vertex_stride);
|
|
|
- _opts.write(array::size(_index_buffer));
|
|
|
+ s32 err = mesh::parse_geometries(m, obj["geometries"], opts);
|
|
|
+ DATA_COMPILER_ENSURE(err == 0, opts);
|
|
|
|
|
|
- _opts.write(_vertex_buffer);
|
|
|
- _opts.write(array::begin(_index_buffer), array::size(_index_buffer) * sizeof(u16));
|
|
|
+ return mesh::parse_nodes(m, obj["nodes"], opts);
|
|
|
}
|
|
|
- };
|
|
|
|
|
|
- s32 compile_node(MeshCompiler &mc, CompileOptions &opts, const JsonObject &geometries, const HashMap<StringView, const char *>::Entry *entry)
|
|
|
- {
|
|
|
- TempAllocator4096 ta;
|
|
|
- const StringView key = entry->first;
|
|
|
- const char *node = entry->second;
|
|
|
- const char *geometry = geometries[key];
|
|
|
+ s32 parse(Mesh &m, CompileOptions &opts)
|
|
|
+ {
|
|
|
+ return mesh::parse(m, opts, opts._source_path.c_str());
|
|
|
+ }
|
|
|
|
|
|
- const StringId32 node_name(key.data(), key.length());
|
|
|
- opts.write(node_name._id);
|
|
|
+ s32 write(Mesh &m, CompileOptions &opts)
|
|
|
+ {
|
|
|
+ opts.write(RESOURCE_HEADER(RESOURCE_VERSION_MESH));
|
|
|
+ opts.write(hash_map::size(m._geometries));
|
|
|
|
|
|
- JsonObject obj_node(ta);
|
|
|
- sjson::parse(obj_node, node);
|
|
|
+ auto cur = hash_map::begin(m._geometries);
|
|
|
+ auto end = hash_map::end(m._geometries);
|
|
|
+ for (; cur != end; ++cur) {
|
|
|
+ HASH_MAP_SKIP_HOLE(m._geometries, cur);
|
|
|
|
|
|
- mc.reset();
|
|
|
- mc.parse(geometry);
|
|
|
- mc.write();
|
|
|
+ Geometry *geo = (Geometry *)&cur->second;
|
|
|
+ mesh::generate_vertex_and_index_buffers(*geo);
|
|
|
|
|
|
- if (json_object::has(obj_node, "children")) {
|
|
|
- JsonObject children(ta);
|
|
|
- sjson::parse_object(children, obj_node["children"]);
|
|
|
+ opts.write(cur->first.to_string_id()._id);
|
|
|
|
|
|
- auto cur = json_object::begin(children);
|
|
|
- auto end = json_object::end(children);
|
|
|
- for (; cur != end; ++cur) {
|
|
|
- JSON_OBJECT_SKIP_HOLE(children, cur);
|
|
|
+ bgfx::VertexLayout layout = mesh::vertex_layout(*geo);
|
|
|
+ u32 stride = mesh::vertex_stride(*geo);
|
|
|
+ OBB bbox = mesh::obb(*geo);
|
|
|
|
|
|
- s32 err = compile_node(mc, opts, geometries, cur);
|
|
|
- DATA_COMPILER_ENSURE(err == 0, opts);
|
|
|
+ BgfxWriter writer(opts._binary_writer);
|
|
|
+ bgfx::write(&writer, layout);
|
|
|
+ opts.write(bbox);
|
|
|
+
|
|
|
+ opts.write(array::size(geo->_vertex_buffer) / stride);
|
|
|
+ opts.write(stride);
|
|
|
+ opts.write(array::size(geo->_index_buffer));
|
|
|
+
|
|
|
+ opts.write(geo->_vertex_buffer);
|
|
|
+ opts.write(array::begin(geo->_index_buffer), array::size(geo->_index_buffer) * sizeof(u16));
|
|
|
}
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ } // namespace mesh
|
|
|
|
|
|
- s32 compile(CompileOptions &opts)
|
|
|
+ namespace mesh
|
|
|
{
|
|
|
- Buffer buf = opts.read();
|
|
|
+ s32 parse(Mesh &m, CompileOptions &opts, const char *path)
|
|
|
+ {
|
|
|
+ Buffer buf = opts.read(path);
|
|
|
|
|
|
- TempAllocator4096 ta;
|
|
|
- JsonObject obj(ta);
|
|
|
- sjson::parse(obj, buf);
|
|
|
+ if (str_has_suffix(path, ".mesh"))
|
|
|
+ return mesh::parse(m, buf, opts);
|
|
|
|
|
|
- JsonObject geometries(ta);
|
|
|
- sjson::parse(geometries, obj["geometries"]);
|
|
|
- JsonObject nodes(ta);
|
|
|
- sjson::parse(nodes, obj["nodes"]);
|
|
|
+ DATA_COMPILER_ASSERT(false
|
|
|
+ , opts
|
|
|
+ , "Unknown mesh '%s'"
|
|
|
+ , path
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- opts.write(RESOURCE_HEADER(RESOURCE_VERSION_MESH));
|
|
|
- opts.write(json_object::size(geometries));
|
|
|
+ } // namespace mesh
|
|
|
|
|
|
- MeshCompiler mc(opts);
|
|
|
+ Node::Node(Allocator &a)
|
|
|
+ : _local_pose(MATRIX4X4_IDENTITY)
|
|
|
+ {
|
|
|
+ }
|
|
|
|
|
|
- auto cur = json_object::begin(nodes);
|
|
|
- auto end = json_object::end(nodes);
|
|
|
- for (; cur != end; ++cur) {
|
|
|
- JSON_OBJECT_SKIP_HOLE(nodes, cur);
|
|
|
+ Geometry::Geometry(Allocator &a)
|
|
|
+ : _positions(a)
|
|
|
+ , _normals(a)
|
|
|
+ , _uvs(a)
|
|
|
+ , _tangents(a)
|
|
|
+ , _binormals(a)
|
|
|
+ , _position_indices(a)
|
|
|
+ , _normal_indices(a)
|
|
|
+ , _uv_indices(a)
|
|
|
+ , _tangent_indices(a)
|
|
|
+ , _binormal_indices(a)
|
|
|
+ , _vertex_buffer(a)
|
|
|
+ , _index_buffer(a)
|
|
|
+ {
|
|
|
+ mesh::reset(*this);
|
|
|
+ }
|
|
|
|
|
|
- s32 err = compile_node(mc, opts, geometries, cur);
|
|
|
- DATA_COMPILER_ENSURE(err == 0, opts);
|
|
|
- }
|
|
|
+ Mesh::Mesh(Allocator &a)
|
|
|
+ : _geometries(a)
|
|
|
+ , _nodes(a)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ s32 compile(CompileOptions &opts)
|
|
|
+ {
|
|
|
+ Mesh mesh(default_allocator());
|
|
|
+ s32 err = mesh::parse(mesh, opts);
|
|
|
+ DATA_COMPILER_ENSURE(err == 0, opts);
|
|
|
|
|
|
- return 0;
|
|
|
+ return mesh::write(mesh, opts);
|
|
|
}
|
|
|
|
|
|
} // namespace mesh_resource_internal
|