Ver Fonte

resource: extract Mesh from mesh_resource.*

Part-of: #207
Daniele Bartolini há 1 ano atrás
pai
commit
4e439599bc

+ 259 - 0
src/resource/mesh.cpp

@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2012-2024 Daniele Bartolini et al.
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "config.h"
+#include "core/containers/array.inl"
+#include "core/containers/vector.inl"
+#include "core/filesystem/filesystem.h"
+#include "core/json/json_object.inl"
+#include "core/json/sjson.h"
+#include "core/math/aabb.inl"
+#include "core/math/constants.h"
+#include "core/math/matrix4x4.inl"
+#include "core/math/vector2.inl"
+#include "core/math/vector3.inl"
+#include "core/memory/temp_allocator.inl"
+#include "core/strings/dynamic_string.inl"
+#include "resource/compile_options.inl"
+#include "resource/mesh.h"
+#include "resource/mesh_resource.h"
+#include <bx/error.h>
+#include <bx/readerwriter.h>
+#include <vertexlayout.h> // bgfx::write, bgfx::read
+
+namespace crown
+{
+namespace mesh
+{
+	/// Writer interface.
+	struct BgfxWriter : public bx::WriterI
+	{
+		BinaryWriter *_bw;
+
+		///
+		explicit BgfxWriter(BinaryWriter &bw)
+			: _bw(&bw)
+		{
+		}
+
+		///
+		virtual ~BgfxWriter()
+		{
+		}
+
+		///
+		virtual int32_t write(const void *_data, int32_t _size, bx::Error *_err)
+		{
+			CE_UNUSED(_err);
+			_bw->write(_data, _size);
+			return _size; // FIXME: return the actual number of bytes written
+		}
+	};
+
+	static 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;
+	}
+
+	static 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;
+	}
+
+	static bgfx::VertexLayout vertex_layout(Geometry &g)
+	{
+		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;
+	}
+
+	static 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));
+			}
+		}
+	}
+
+	static void geometry_names(Vector<DynamicString> &names, const Mesh &m, const DynamicString &geometry)
+	{
+		auto cur = hash_map::begin(m._nodes);
+		auto end = hash_map::end(m._nodes);
+		for (; cur != end; ++cur) {
+			HASH_MAP_SKIP_HOLE(m._nodes, cur);
+
+			if (cur->second._geometry == geometry)
+				vector::push_back(names, cur->first);
+		}
+	}
+
+	static 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 write(Mesh &m, CompileOptions &opts)
+	{
+		opts.write(RESOURCE_HEADER(RESOURCE_VERSION_MESH));
+		opts.write(hash_map::size(m._geometries));
+
+		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);
+
+			Vector<DynamicString> geo_names(default_allocator());
+			geometry_names(geo_names, m, cur->first);
+			u32 num_geo_names = vector::size(geo_names);
+
+			opts.write(num_geo_names);
+			for (u32 i = 0; i < num_geo_names; ++i)
+				opts.write(geo_names[i].to_string_id()._id);
+
+			Geometry *geo = (Geometry *)&cur->second;
+			mesh::generate_vertex_and_index_buffers(*geo);
+
+			bgfx::VertexLayout layout = mesh::vertex_layout(*geo);
+			u32 stride = mesh::vertex_stride(*geo);
+			OBB bbox = mesh::obb(*geo);
+
+			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;
+	}
+
+	s32 parse(Mesh &m, CompileOptions &opts, const char *path)
+	{
+		if (str_has_suffix(path, ".mesh")) {
+			Buffer buf = opts.read(path);
+			return mesh::parse(m, buf, opts);
+		} else {
+			TempAllocator512 ta;
+			DynamicString str(ta);
+			str = path;
+			str += ".mesh";
+			Buffer buf = opts.read(str.c_str());
+			return mesh::parse(m, buf, opts);
+		}
+	}
+}
+
+Node::Node(Allocator &a)
+	: _local_pose(MATRIX4X4_IDENTITY)
+	, _geometry(a)
+{
+}
+
+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);
+}
+
+Mesh::Mesh(Allocator &a)
+	: _geometries(a)
+	, _nodes(a)
+{
+}
+
+} // namespace crown

+ 75 - 0
src/resource/mesh.h

@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2012-2024 Daniele Bartolini et al.
+ * SPDX-License-Identifier: MIT
+ */
+
+#pragma once
+
+#include "core/filesystem/types.h"
+#include "core/math/types.h"
+#include "core/memory/types.h"
+#include "core/strings/dynamic_string.h"
+#include "resource/types.h"
+
+namespace crown
+{
+struct Node
+{
+	ALLOCATOR_AWARE;
+
+	Matrix4x4 _local_pose;
+	DynamicString _geometry;
+
+	///
+	explicit Node(Allocator &a);
+};
+
+struct Geometry
+{
+	ALLOCATOR_AWARE;
+
+	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;
+
+	Array<char> _vertex_buffer;
+	Array<u16> _index_buffer;
+
+	///
+	explicit Geometry(Allocator &a);
+};
+
+struct Mesh
+{
+	HashMap<DynamicString, Geometry> _geometries;
+	HashMap<DynamicString, Node> _nodes;
+
+	///
+	explicit Mesh(Allocator &a);
+};
+
+namespace mesh
+{
+	///
+	bool has_normals(Geometry &g);
+
+	///
+	bool has_uvs(Geometry &g);
+
+	///
+	s32 parse(Mesh &m, CompileOptions &opts, const char *path);
+
+	///
+	s32 write(Mesh &m, CompileOptions &opts);
+
+} // namespace mesh
+
+} // namespace crown

+ 109 - 341
src/resource/mesh_resource.cpp

@@ -20,9 +20,9 @@
 #include "core/strings/string_id.inl"
 #include "device/log.h"
 #include "resource/compile_options.inl"
+#include "resource/mesh.h"
 #include "resource/mesh_resource.h"
 #include "resource/resource_manager.h"
-#include <bx/readerwriter.h>
 #include <bx/error.h>
 #include <vertexlayout.h> // bgfx::write, bgfx::read
 
@@ -52,31 +52,6 @@ struct BgfxReader : public bx::ReaderI
 	}
 };
 
-/// Writer interface.
-struct BgfxWriter : public bx::WriterI
-{
-	BinaryWriter *_bw;
-
-	///
-	explicit BgfxWriter(BinaryWriter &bw)
-		: _bw(&bw)
-	{
-	}
-
-	///
-	virtual ~BgfxWriter()
-	{
-	}
-
-	///
-	virtual int32_t write(const void *_data, int32_t _size, bx::Error *_err)
-	{
-		CE_UNUSED(_err);
-		_bw->write(_data, _size);
-		return _size; // FIXME: return the actual number of bytes written
-	}
-};
-
 MeshResource::MeshResource(Allocator &a)
 	: nodes(a)
 	, geometries(a)
@@ -208,7 +183,7 @@ namespace mesh_resource_internal
 } // namespace mesh_resource_internal
 
 #if CROWN_CAN_COMPILE
-namespace mesh_resource_internal
+namespace mesh
 {
 	static void parse_float_array(Array<f32> &output, const char *json)
 	{
@@ -232,369 +207,162 @@ namespace mesh_resource_internal
 			output[i] = (u16)sjson::parse_int(indices[i]);
 	}
 
-	namespace mesh
-	{
-		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);
-			}
-
-			if (json_object::has(obj, "geometry"))
-				sjson::parse_string(n._geometry, obj["geometry"]);
-
-			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);
-		}
+	s32 parse_nodes(Mesh &m, const char *sjson, CompileOptions &opts);
 
-		bool has_normals(Geometry &g)
-		{
-			return array::size(g._normals) != 0;
-		}
+	s32 parse_node(Node &n, const char *sjson, Mesh *mesh, CompileOptions &opts)
+	{
+		TempAllocator4096 ta;
+		JsonObject obj(ta);
+		sjson::parse(obj, sjson);
 
-		bool has_uvs(Geometry &g)
-		{
-			return array::size(g._uvs) != 0;
-		}
+		n._local_pose = sjson::parse_matrix4x4(obj["matrix_local"]);
 
-		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;
+		if (json_object::has(obj, "children")) {
+			s32 err = mesh::parse_nodes(*mesh, obj["children"], opts);
+			DATA_COMPILER_ENSURE(err == 0, opts);
 		}
 
-		bgfx::VertexLayout vertex_layout(Geometry &g)
-		{
-			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);
-			}
+		if (json_object::has(obj, "geometry"))
+			sjson::parse_string(n._geometry, obj["geometry"]);
 
-			layout.end();
-			return layout;
-		}
+		return 0;
+	}
 
-		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));
-				}
-			}
-		}
+	s32 parse_indices(Geometry &g, const char *json)
+	{
+		TempAllocator4096 ta;
+		JsonObject obj(ta);
+		sjson::parse(obj, json);
 
-		OBB obb(Geometry &g)
-		{
-			AABB aabb;
-			OBB obb;
-			aabb::reset(aabb);
-			memset(&obb, 0, sizeof(obb));
+		JsonArray data_json(ta);
+		sjson::parse_array(data_json, obj["data"]);
 
-			aabb::from_points(aabb
-				, array::size(g._positions) / 3
-				, sizeof(g._positions[0]) * 3
-				, array::begin(g._positions)
-				);
+		parse_index_array(g._position_indices, data_json[0]);
 
-			obb.tm = from_quaternion_translation(QUATERNION_IDENTITY, aabb::center(aabb));
-			obb.half_extents = (aabb.max - aabb.min) * 0.5f;
-			return obb;
+		if (has_normals(g)) {
+			parse_index_array(g._normal_indices, data_json[1]);
 		}
-
-		s32 parse_indices(Geometry &g, const char *json)
-		{
-			TempAllocator4096 ta;
-			JsonObject obj(ta);
-			sjson::parse(obj, json);
-
-			JsonArray data_json(ta);
-			sjson::parse_array(data_json, obj["data"]);
-
-			parse_index_array(g._position_indices, data_json[0]);
-
-			if (has_normals(g)) {
-				parse_index_array(g._normal_indices, data_json[1]);
-			}
-			if (has_uvs(g)) {
-				parse_index_array(g._uv_indices, data_json[2]);
-			}
-
-			return 0;
+		if (has_uvs(g)) {
+			parse_index_array(g._uv_indices, data_json[2]);
 		}
 
-		s32 parse_geometry(Geometry &g, const char *sjson)
-		{
-			TempAllocator4096 ta;
-			JsonObject obj(ta);
-			sjson::parse(obj, sjson);
+		return 0;
+	}
 
-			parse_float_array(g._positions, obj["position"]);
+	s32 parse_geometry(Geometry &g, const char *sjson)
+	{
+		TempAllocator4096 ta;
+		JsonObject obj(ta);
+		sjson::parse(obj, sjson);
 
-			if (json_object::has(obj, "normal")) {
-				parse_float_array(g._normals, obj["normal"]);
-			}
-			if (json_object::has(obj, "texcoord")) {
-				parse_float_array(g._uvs, obj["texcoord"]);
-			}
+		parse_float_array(g._positions, obj["position"]);
 
-			return parse_indices(g, obj["indices"]);
+		if (json_object::has(obj, "normal")) {
+			parse_float_array(g._normals, obj["normal"]);
 		}
-
-		s32 parse_geometries(Mesh &m, const char *sjson, CompileOptions &opts)
-		{
-			TempAllocator4096 ta;
-			JsonObject geometries(ta);
-			sjson::parse(geometries, sjson);
-
-			auto cur = json_object::begin(geometries);
-			auto end = json_object::end(geometries);
-			for (; cur != end; ++cur) {
-				JSON_OBJECT_SKIP_HOLE(geometries, cur);
-
-				Geometry geo(default_allocator());
-				s32 err = mesh::parse_geometry(geo, cur->second);
-				DATA_COMPILER_ENSURE(err == 0, opts);
-
-				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);
-			}
-
-			return 0;
+		if (json_object::has(obj, "texcoord")) {
+			parse_float_array(g._uvs, obj["texcoord"]);
 		}
 
-		s32 parse_nodes(Mesh &m, const char *sjson, CompileOptions &opts)
-		{
-			TempAllocator4096 ta;
-			JsonObject nodes(ta);
-			sjson::parse(nodes, sjson);
-
-			auto cur = json_object::begin(nodes);
-			auto end = json_object::end(nodes);
-			for (; cur != end; ++cur) {
-				JSON_OBJECT_SKIP_HOLE(nodes, cur);
-
-				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()
-					);
-
-				// For backwards compatibility: originally .mesh resources
-				// enforced (implicitly) a 1:1 relationship between nodes
-				// and geometries.
-				if (node._geometry == "")
-					node._geometry = node_name;
-
-				DATA_COMPILER_ASSERT(hash_map::has(m._geometries, node._geometry)
-					, opts
-					, "Node '%s' references unexisting geometry '%s'"
-					, node_name.c_str()
-					, node._geometry.c_str()
-					);
-				hash_map::set(m._nodes, node_name, node);
-			}
+		return parse_indices(g, obj["indices"]);
+	}
 
-			return 0;
-		}
+	s32 parse_geometries(Mesh &m, const char *sjson, CompileOptions &opts)
+	{
+		TempAllocator4096 ta;
+		JsonObject geometries(ta);
+		sjson::parse(geometries, sjson);
 
-		s32 parse(Mesh &m, Buffer &buf, CompileOptions &opts)
-		{
-			TempAllocator4096 ta;
-			JsonObject nodes(ta);
-			JsonObject obj(ta);
-			sjson::parse(obj, buf);
+		auto cur = json_object::begin(geometries);
+		auto end = json_object::end(geometries);
+		for (; cur != end; ++cur) {
+			JSON_OBJECT_SKIP_HOLE(geometries, cur);
 
-			s32 err = mesh::parse_geometries(m, obj["geometries"], opts);
+			Geometry geo(default_allocator());
+			s32 err = mesh::parse_geometry(geo, cur->second);
 			DATA_COMPILER_ENSURE(err == 0, opts);
 
-			return mesh::parse_nodes(m, obj["nodes"], opts);
-		}
-
-		s32 parse(Mesh &m, CompileOptions &opts)
-		{
-			return mesh::parse(m, opts, opts._source_path.c_str());
-		}
-
-		void geometry_names(Vector<DynamicString> &names, const Mesh &m, const DynamicString &geometry)
-		{
-			auto cur = hash_map::begin(m._nodes);
-			auto end = hash_map::end(m._nodes);
-			for (; cur != end; ++cur) {
-				HASH_MAP_SKIP_HOLE(m._nodes, cur);
-
-				if (cur->second._geometry == geometry)
-					vector::push_back(names, cur->first);
-			}
+			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);
 		}
 
-		s32 write(Mesh &m, CompileOptions &opts)
-		{
-			opts.write(RESOURCE_HEADER(RESOURCE_VERSION_MESH));
-			opts.write(hash_map::size(m._geometries));
-
-			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);
-
-				Vector<DynamicString> geo_names(default_allocator());
-				geometry_names(geo_names, m, cur->first);
-				u32 num_geo_names = vector::size(geo_names);
-
-				opts.write(num_geo_names);
-				for (u32 i = 0; i < num_geo_names; ++i)
-					opts.write(geo_names[i].to_string_id()._id);
-
-				Geometry *geo = (Geometry *)&cur->second;
-				mesh::generate_vertex_and_index_buffers(*geo);
-
-				bgfx::VertexLayout layout = mesh::vertex_layout(*geo);
-				u32 stride = mesh::vertex_stride(*geo);
-				OBB bbox = mesh::obb(*geo);
-
-				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));
+		return 0;
+	}
 
-				opts.write(geo->_vertex_buffer);
-				opts.write(array::begin(geo->_index_buffer), array::size(geo->_index_buffer) * sizeof(u16));
-			}
+	s32 parse_nodes(Mesh &m, const char *sjson, CompileOptions &opts)
+	{
+		TempAllocator4096 ta;
+		JsonObject nodes(ta);
+		sjson::parse(nodes, sjson);
 
-			return 0;
-		}
+		auto cur = json_object::begin(nodes);
+		auto end = json_object::end(nodes);
+		for (; cur != end; ++cur) {
+			JSON_OBJECT_SKIP_HOLE(nodes, cur);
 
-	} // namespace mesh
+			Node node(default_allocator());
+			s32 err = mesh::parse_node(node, cur->second, &m, opts);
+			DATA_COMPILER_ENSURE(err == 0, opts);
 
-	namespace mesh
-	{
-		s32 parse(Mesh &m, CompileOptions &opts, const char *path)
-		{
-			Buffer buf = opts.read(path);
+			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()
+				);
 
-			if (str_has_suffix(path, ".mesh"))
-				return mesh::parse(m, buf, opts);
+			// For backwards compatibility: originally .mesh resources
+			// enforced (implicitly) a 1:1 relationship between nodes
+			// and geometries.
+			if (node._geometry == "")
+				node._geometry = node_name;
 
-			DATA_COMPILER_ASSERT(false
+			DATA_COMPILER_ASSERT(hash_map::has(m._geometries, node._geometry)
 				, opts
-				, "Unknown mesh '%s'"
-				, path
+				, "Node '%s' references unexisting geometry '%s'"
+				, node_name.c_str()
+				, node._geometry.c_str()
 				);
+			hash_map::set(m._nodes, node_name, node);
 		}
 
-	} // namespace mesh
-
-	Node::Node(Allocator &a)
-		: _local_pose(MATRIX4X4_IDENTITY)
-		, _geometry(a)
-	{
+		return 0;
 	}
 
-	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)
+	s32 parse(Mesh &m, Buffer &buf, CompileOptions &opts)
 	{
-		mesh::reset(*this);
+		TempAllocator4096 ta;
+		JsonObject nodes(ta);
+		JsonObject obj(ta);
+		sjson::parse(obj, buf);
+
+		s32 err = mesh::parse_geometries(m, obj["geometries"], opts);
+		DATA_COMPILER_ENSURE(err == 0, opts);
+
+		return mesh::parse_nodes(m, obj["nodes"], opts);
 	}
 
-	Mesh::Mesh(Allocator &a)
-		: _geometries(a)
-		, _nodes(a)
+	s32 parse(Mesh &m, CompileOptions &opts)
 	{
+		return mesh::parse(m, opts, opts._source_path.c_str());
 	}
 
+}
+
+namespace mesh_resource_internal
+{
 	s32 compile(CompileOptions &opts)
 	{
 		Mesh mesh(default_allocator());
 		s32 err = mesh::parse(mesh, opts);
 		DATA_COMPILER_ENSURE(err == 0, opts);
-
 		return mesh::write(mesh, opts);
 	}
 

+ 17 - 51
src/resource/mesh_resource.h

@@ -5,12 +5,13 @@
 
 #pragma once
 
+#include "core/containers/types.h"
 #include "core/filesystem/types.h"
 #include "core/math/types.h"
 #include "core/memory/types.h"
 #include "core/strings/dynamic_string.h"
 #include "core/strings/string_id.h"
-#include "resource/types.h"
+#include "resource/mesh.h"
 #include "resource/types.h"
 #include <bgfx/bgfx.h>
 
@@ -57,62 +58,27 @@ struct MeshResource
 	const MeshGeometry *geometry(StringId32 name) const;
 };
 
-namespace mesh_resource_internal
+namespace mesh
 {
-	struct Node
-	{
-		ALLOCATOR_AWARE;
-
-		Matrix4x4 _local_pose;
-		DynamicString _geometry;
-
-		///
-		explicit Node(Allocator &a);
-	};
-
-	struct Geometry
-	{
-		ALLOCATOR_AWARE;
-
-		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;
-
-		Array<char> _vertex_buffer;
-		Array<u16> _index_buffer;
-
-		///
-		explicit Geometry(Allocator &a);
-	};
-
-	struct Mesh
-	{
-		HashMap<DynamicString, Geometry> _geometries;
-		HashMap<DynamicString, Node> _nodes;
-
-		///
-		explicit Mesh(Allocator &a);
-	};
-
-	namespace mesh
-	{
-		///
-		s32 parse(Mesh &m, CompileOptions &opts, const char *path);
-
-	} // namespace mesh
+	///
+	s32 parse(Mesh &m, Buffer &buf, CompileOptions &opts);
+}
 
+namespace mesh_resource_internal
+{
+	///
 	s32 compile(CompileOptions &opts);
+
+	///
 	void *load(File &file, Allocator &a);
+
+	///
 	void online(StringId64 /*id*/, ResourceManager & /*rm*/);
+
+	///
 	void offline(StringId64 /*id*/, ResourceManager & /*rm*/);
+
+	///
 	void unload(Allocator &a, void *res);
 
 } // namespace mesh_resource_internal

+ 7 - 9
src/resource/physics_resource.cpp

@@ -20,6 +20,7 @@
 #include "core/strings/string.inl"
 #include "core/strings/string_id.inl"
 #include "resource/compile_options.inl"
+#include "resource/mesh.h"
 #include "resource/mesh_resource.h"
 #include "resource/physics_resource.h"
 #include "world/types.h"
@@ -193,23 +194,20 @@ namespace physics_resource_internal
 			sjson::parse_string(name, obj["name"]);
 
 			// Parse mesh resource.
-			if (opts.resource_exists("mesh", scene.c_str()))
-				scene += ".mesh";
-
-			mesh_resource_internal::Mesh mesh(default_allocator());
-			s32 err = mesh_resource_internal::mesh::parse(mesh, opts, scene.c_str());
+			Mesh mesh(default_allocator());
+			s32 err = mesh::parse(mesh, opts, scene.c_str());
 			DATA_COMPILER_ENSURE(err == 0, opts);
 
-			mesh_resource_internal::Node deffault_node(default_allocator());
-			mesh_resource_internal::Node &node = hash_map::get(mesh._nodes, name, deffault_node);
+			Node deffault_node(default_allocator());
+			Node &node = hash_map::get(mesh._nodes, name, deffault_node);
 			DATA_COMPILER_ASSERT(&node != &deffault_node
 				, opts
 				, "Node '%s' does not exist"
 				, name.c_str()
 				);
 
-			mesh_resource_internal::Geometry deffault_geometry(default_allocator());
-			mesh_resource_internal::Geometry &geometry = hash_map::get(mesh._geometries, node._geometry, deffault_geometry);
+			Geometry deffault_geometry(default_allocator());
+			Geometry &geometry = hash_map::get(mesh._geometries, node._geometry, deffault_geometry);
 			DATA_COMPILER_ASSERT(&geometry != &deffault_geometry
 				, opts
 				, "Geometry '%s' does not exist"

+ 11 - 3
src/resource/unit_compiler.cpp

@@ -18,6 +18,7 @@
 #include "core/strings/string_id.inl"
 #include "resource/compile_options.inl"
 #include "resource/physics_resource.h"
+#include "resource/resource_id.inl"
 #include "resource/unit_compiler.h"
 #include "resource/unit_resource.h"
 #include "world/types.h"
@@ -130,10 +131,17 @@ static s32 compile_mesh_renderer(Buffer &output, const char *json, CompileOption
 
 	DynamicString mesh_resource(ta);
 	sjson::parse_string(mesh_resource, obj["mesh_resource"]);
-	DATA_COMPILER_ASSERT_RESOURCE_EXISTS("mesh"
-		, mesh_resource.c_str()
+
+	DATA_COMPILER_ENSURE(opts.resource_exists("mesh", mesh_resource.c_str())
+		|| opts.file_exists(mesh_resource.c_str())
 		, opts
 		);
+
+	const char *mesh_resource_type = resource_type(mesh_resource.c_str());
+	if (mesh_resource_type != NULL) {
+		u32 name_len = resource_name_length(mesh_resource_type, mesh_resource.c_str());
+		array::resize(mesh_resource._data, name_len);
+	}
 	opts.add_requirement("mesh", mesh_resource.c_str());
 
 	DynamicString material(ta);
@@ -145,7 +153,7 @@ static s32 compile_mesh_renderer(Buffer &output, const char *json, CompileOption
 	opts.add_requirement("material", material.c_str());
 
 	MeshRendererDesc mrd;
-	mrd.mesh_resource     = sjson::parse_resource_name(obj["mesh_resource"]);
+	mrd.mesh_resource     = StringId64(mesh_resource.c_str());
 	mrd.material_resource = sjson::parse_resource_name(obj["material"]);
 	mrd.geometry_name     = sjson::parse_string_id    (obj["geometry_name"]);
 	mrd.visible           = sjson::parse_bool         (obj["visible"]);

+ 9 - 2
tools/resource/mesh_resource.vala

@@ -41,7 +41,7 @@ public class MeshResource
 
 			unit.set_component_property_string(component_id, "data.geometry_name", node_name);
 			unit.set_component_property_string(component_id, "data.material", material_name);
-			unit.set_component_property_string(component_id, "data.mesh_resource", resource_name);
+			unit.set_component_property_string(component_id, "data.mesh_resource", resource_name + ".mesh");
 			unit.set_component_property_bool  (component_id, "data.visible", true);
 		}
 
@@ -172,7 +172,14 @@ public class MeshResource
 					new_unit_id = Guid.new_guid();
 					db.create(new_unit_id, OBJECT_TYPE_UNIT);
 				}
-				create_components(db, unit_id, new_unit_id, material_name, resource_name, entry.key, (Hashtable)entry.value);
+				create_components(db
+					, unit_id
+					, new_unit_id
+					, material_name
+					, resource_name
+					, entry.key
+					, (Hashtable)entry.value
+					);
 			}
 
 			db.save(project.absolute_path(resource_name) + ".unit", unit_id);