Przeglądaj źródła

resource: refactor MeshCompiler into reusable pieces

Part-of: #207
Daniele Bartolini 1 rok temu
rodzic
commit
986f3adfb7
2 zmienionych plików z 317 dodań i 188 usunięć
  1. 267 187
      src/resource/mesh_resource.cpp
  2. 50 1
      src/resource/mesh_resource.h

+ 267 - 187
src/resource/mesh_resource.cpp

@@ -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

+ 50 - 1
src/resource/mesh_resource.h

@@ -5,10 +5,10 @@
 
 #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/types.h"
@@ -53,6 +53,55 @@ struct MeshResource
 
 namespace mesh_resource_internal
 {
+	struct Node
+	{
+		ALLOCATOR_AWARE;
+
+		Matrix4x4 _local_pose;
+
+		///
+		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 compile(CompileOptions &opts);
 	void *load(File &file, Allocator &a);
 	void online(StringId64 /*id*/, ResourceManager & /*rm*/);