From d02daa5a4805dc3184884f3a7cd7620e5787adcb Mon Sep 17 00:00:00 2001
From: Daniel Karbach <daniel.karbach@localhorst.tv>
Date: Tue, 11 Aug 2015 15:17:54 +0200
Subject: [PATCH] allow multiple meshes per entity

organized in a tree with each node haing its local
position/orientation relative to the parent
---
 src/ai/Spawner.cpp           | 12 +++++---
 src/ai/Spawner.hpp           |  3 ++
 src/app/FPSController.cpp    |  2 +-
 src/model/CompositeModel.cpp | 55 +++++++++++++++++++++++++++++++++++
 src/model/CompositeModel.hpp | 56 ++++++++++++++++++++++++++++++++++++
 src/world/Entity.cpp         | 50 +++++++++-----------------------
 src/world/Entity.hpp         | 38 +++++++++++-------------
 src/world/World.cpp          |  5 +---
 8 files changed, 155 insertions(+), 66 deletions(-)
 create mode 100644 src/model/CompositeModel.cpp
 create mode 100644 src/model/CompositeModel.hpp

diff --git a/src/ai/Spawner.cpp b/src/ai/Spawner.cpp
index b0b9bcb..e5bc68f 100644
--- a/src/ai/Spawner.cpp
+++ b/src/ai/Spawner.cpp
@@ -18,6 +18,13 @@ Spawner::Spawner(World &world)
 , spawn_distance(16 * 16)
 , max_entities(16)
 , chunk_range(4) {
+	EntityModel::Buffer buf;
+	for (size_t i = 0; i < 14; ++i) {
+		world.BlockTypes()[i + 1].FillEntityModel(buf);
+		models[i].Update(buf);
+		buf.Clear();
+	}
+
 	timer.Start();
 	Spawn(world.Player().ChunkCoords(), { 0.5f, 0.5f, 0.5f });
 }
@@ -92,9 +99,6 @@ void Spawner::TrySpawn() {
 }
 
 void Spawner::Spawn(const glm::ivec3 &chunk, const glm::vec3 &pos) {
-	glm::vec3 color(rand() % 6, rand() % 6, rand() % 6);
-	color = color * 0.15f + 0.25f;
-
 	glm::vec3 rot(0.000001f);
 	rot.x *= (rand() % 1024);
 	rot.y *= (rand() % 1024);
@@ -105,7 +109,7 @@ void Spawner::Spawn(const glm::ivec3 &chunk, const glm::vec3 &pos) {
 	e.Position(chunk, pos);
 	e.Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
 	e.WorldCollidable(true);
-	e.SetShape(world.BlockTypes()[1].shape, color, 2);
+	e.GetModel().SetNodeModel(&models[rand() % 14]);
 	e.AngularVelocity(rot);
 	Controller *ctrl;
 	if (rand() % 2) {
diff --git a/src/ai/Spawner.hpp b/src/ai/Spawner.hpp
index 127a860..b36e520 100644
--- a/src/ai/Spawner.hpp
+++ b/src/ai/Spawner.hpp
@@ -2,6 +2,7 @@
 #define BLANK_AI_SPAWNER_HPP_
 
 #include "../app/IntervalTimer.hpp"
+#include "../model/EntityModel.hpp"
 
 #include <vector>
 #include <glm/glm.hpp>
@@ -29,6 +30,8 @@ private:
 	World &world;
 	std::vector<Controller *> controllers;
 
+	EntityModel models[14];
+
 	IntervalTimer timer;
 	float despawn_range;
 	float spawn_distance;
diff --git a/src/app/FPSController.cpp b/src/app/FPSController.cpp
index 48e39dc..7f8c2c1 100644
--- a/src/app/FPSController.cpp
+++ b/src/app/FPSController.cpp
@@ -42,7 +42,7 @@ void FPSController::RotateYaw(float delta) noexcept {
 
 
 void FPSController::Update(int dt) noexcept {
-	entity.Rotation(glm::quat(glm::vec3(pitch, yaw, 0.0f)));
+	entity.Orientation(glm::quat(glm::vec3(pitch, yaw, 0.0f)));
 	entity.Velocity(glm::rotateY(velocity, yaw));
 }
 
diff --git a/src/model/CompositeModel.cpp b/src/model/CompositeModel.cpp
new file mode 100644
index 0000000..d5b25af
--- /dev/null
+++ b/src/model/CompositeModel.cpp
@@ -0,0 +1,55 @@
+#include "CompositeModel.hpp"
+
+#include "EntityModel.hpp"
+#include "../graphics/DirectionalLighting.hpp"
+
+#include <glm/gtx/quaternion.hpp>
+
+
+namespace blank {
+
+CompositeModel::CompositeModel()
+: node_model(nullptr)
+, position(0.0f)
+, orientation(1.0f, 0.0f, 0.0f, 0.0f)
+, parts() {
+
+}
+
+
+CompositeModel &CompositeModel::AddPart() {
+	parts.emplace_back();
+	parts.back().parent = this;
+	return parts.back();
+}
+
+
+glm::mat4 CompositeModel::LocalTransform() const noexcept {
+	glm::mat4 transform(toMat4(orientation));
+	transform[3].x = position.x;
+	transform[3].y = position.y;
+	transform[3].z = position.z;
+	return transform;
+}
+
+glm::mat4 CompositeModel::GlobalTransform() const noexcept {
+	if (HasParent()) {
+		return Parent().GlobalTransform() * LocalTransform();
+	} else {
+		return LocalTransform();
+	}
+}
+
+
+void CompositeModel::Render(const glm::mat4 &M, DirectionalLighting &prog) const {
+	glm::mat4 transform(M * LocalTransform());
+	if (HasNodeModel()) {
+		prog.SetM(transform);
+		NodeModel().Draw();
+	}
+	for (const CompositeModel &part : parts) {
+		part.Render(transform, prog);
+	}
+}
+
+}
diff --git a/src/model/CompositeModel.hpp b/src/model/CompositeModel.hpp
new file mode 100644
index 0000000..b548a9c
--- /dev/null
+++ b/src/model/CompositeModel.hpp
@@ -0,0 +1,56 @@
+#ifndef BLANK_MODEL_COMPOSITEMODEL_HPP_
+#define BLANK_MODEL_COMPOSITEMODEL_HPP_
+
+#include <list>
+#include <glm/glm.hpp>
+#include <glm/gtc/quaternion.hpp>
+
+
+namespace blank {
+
+class DirectionalLighting;
+class EntityModel;
+
+class CompositeModel {
+
+public:
+	CompositeModel();
+
+	CompositeModel(const CompositeModel &) = delete;
+	CompositeModel &operator =(const CompositeModel &) = delete;
+
+	const glm::vec3 &Position() const noexcept { return position; }
+	void Position(const glm::vec3 &p) noexcept { position = p; }
+
+	const glm::quat &Orientation() const noexcept { return orientation; }
+	void Orientation(const glm::quat &o) noexcept { orientation = o; }
+
+	bool HasNodeModel() const noexcept { return node_model; }
+	void SetNodeModel(const EntityModel *m) noexcept { node_model = m; }
+
+	const EntityModel &NodeModel() const noexcept { return *node_model; }
+
+	CompositeModel &AddPart();
+	bool HasParent() const noexcept { return parent; }
+	CompositeModel &Parent() const noexcept { return *parent; }
+	bool IsRoot() const noexcept { return !HasParent(); }
+
+	glm::mat4 LocalTransform() const noexcept;
+	glm::mat4 GlobalTransform() const noexcept;
+
+	void Render(const glm::mat4 &, DirectionalLighting &) const;
+
+private:
+	CompositeModel *parent;
+	const EntityModel *node_model;
+
+	glm::vec3 position;
+	glm::quat orientation;
+
+	std::list<CompositeModel> parts;
+
+};
+
+}
+
+#endif
diff --git a/src/world/Entity.cpp b/src/world/Entity.cpp
index 072bb38..fb62f27 100644
--- a/src/world/Entity.cpp
+++ b/src/world/Entity.cpp
@@ -15,45 +15,25 @@ blank::EntityModel::Buffer model_buffer;
 namespace blank {
 
 Entity::Entity() noexcept
-: shape(nullptr)
-, model()
+: model()
 , name("anonymous")
 , bounds()
 , velocity(0, 0, 0)
-, position(0, 0, 0)
 , chunk(0, 0, 0)
 , angular_velocity(0.0f)
-, rotation(1.0f, 0.0f, 0.0f, 0.0f)
 , world_collision(false)
 , remove(false) {
 
 }
 
 
-void Entity::SetShape(const Shape *s, const glm::vec3 &color, float texture) {
-	shape = s;
-	model_buffer.Clear();
-	shape->Vertices(model_buffer, texture);
-	model_buffer.colors.resize(shape->VertexCount(), color);
-	model.Update(model_buffer);
-}
-
-void Entity::SetShapeless() noexcept {
-	shape = nullptr;
-}
-
-
-void Entity::Velocity(const glm::vec3 &vel) noexcept {
-	velocity = vel;
-}
-
-void Entity::Position(const Chunk::Pos &c, const Block::Pos &pos) noexcept {
+void Entity::Position(const Chunk::Pos &c, const glm::vec3 &pos) noexcept {
 	chunk = c;
-	position = pos;
+	model.Position(pos);
 }
 
-void Entity::Position(const Block::Pos &pos) noexcept {
-	position = pos;
+void Entity::Position(const glm::vec3 &pos) noexcept {
+	glm::vec3 position(pos);
 	while (position.x >= Chunk::width) {
 		position.x -= Chunk::width;
 		++chunk.x;
@@ -78,27 +58,25 @@ void Entity::Position(const Block::Pos &pos) noexcept {
 		position.z += Chunk::depth;
 		--chunk.z;
 	}
+	model.Position(position);
 }
 
 void Entity::Move(const glm::vec3 &delta) noexcept {
-	Position(position + delta);
-}
-
-void Entity::AngularVelocity(const glm::vec3 &v) noexcept {
-	angular_velocity = v;
+	Position(Position() + delta);
 }
 
-void Entity::Rotation(const glm::quat &rot) noexcept {
-	rotation = rot;
+void Entity::Rotate(const glm::quat &delta) noexcept {
+	Orientation(delta * Orientation());
 }
 
-void Entity::Rotate(const glm::quat &delta) noexcept {
-	Rotation(delta * Rotation());
+glm::mat4 Entity::ChunkTransform(const Chunk::Pos &chunk_offset) const noexcept {
+	const glm::vec3 translation = glm::vec3((chunk - chunk_offset) * Chunk::Extent());
+	return glm::translate(translation);
 }
 
 glm::mat4 Entity::Transform(const Chunk::Pos &chunk_offset) const noexcept {
-	const glm::vec3 translation = glm::vec3((chunk - chunk_offset) * Chunk::Extent()) + position;
-	glm::mat4 transform(toMat4(Rotation()));
+	const glm::vec3 translation = glm::vec3((chunk - chunk_offset) * Chunk::Extent()) + Position();
+	glm::mat4 transform(toMat4(Orientation()));
 	transform[3].x = translation.x;
 	transform[3].y = translation.y;
 	transform[3].z = translation.z;
diff --git a/src/world/Entity.hpp b/src/world/Entity.hpp
index f60bf4c..ae14297 100644
--- a/src/world/Entity.hpp
+++ b/src/world/Entity.hpp
@@ -1,10 +1,9 @@
 #ifndef BLANK_WORLD_ENTITY_HPP_
 #define BLANK_WORLD_ENTITY_HPP_
 
-#include "Block.hpp"
 #include "Chunk.hpp"
+#include "../model/CompositeModel.hpp"
 #include "../model/geometry.hpp"
-#include "../model/EntityModel.hpp"
 
 #include <string>
 #include <glm/glm.hpp>
@@ -13,6 +12,7 @@
 
 namespace blank {
 
+class DirectionalLighting;
 class Shape;
 
 class Entity {
@@ -20,10 +20,8 @@ class Entity {
 public:
 	Entity() noexcept;
 
-	bool HasShape() const noexcept { return shape; }
-	const Shape *GetShape() const noexcept { return shape; }
-	void SetShape(const Shape *, const glm::vec3 &color, float texture);
-	void SetShapeless() noexcept;
+	CompositeModel &GetModel() noexcept { return model; }
+	const CompositeModel &GetModel() const noexcept { return model; }
 
 	const std::string &Name() const noexcept { return name; }
 	void Name(const std::string &n) { name = n; }
@@ -35,30 +33,31 @@ public:
 	void WorldCollidable(bool b) noexcept { world_collision = b; }
 
 	const glm::vec3 &Velocity() const noexcept { return velocity; }
-	void Velocity(const glm::vec3 &) noexcept;
+	void Velocity(const glm::vec3 &v) noexcept { velocity = v; }
 
-	const Block::Pos &Position() const noexcept { return position; }
-	void Position(const Chunk::Pos &, const Block::Pos &) noexcept;
-	void Position(const Block::Pos &) noexcept;
+	const glm::vec3 &Position() const noexcept { return model.Position(); }
+	void Position(const Chunk::Pos &, const glm::vec3 &) noexcept;
+	void Position(const glm::vec3 &) noexcept;
 	void Move(const glm::vec3 &delta) noexcept;
 
 	const Chunk::Pos ChunkCoords() const noexcept { return chunk; }
 
 	glm::vec3 AbsolutePosition() const noexcept {
-		return glm::vec3(chunk * Chunk::Extent()) + position;
+		return glm::vec3(chunk * Chunk::Extent()) + Position();
 	}
 	glm::vec3 AbsoluteDifference(const Entity &other) const noexcept {
-		return glm::vec3((chunk - other.chunk) * Chunk::Extent()) + position - other.position;
+		return glm::vec3((chunk - other.chunk) * Chunk::Extent()) + Position() - other.Position();
 	}
 
 	/// direction is rotation axis, magnitude is speed in rad/ms
 	const glm::vec3 &AngularVelocity() const noexcept { return angular_velocity; }
-	void AngularVelocity(const glm::vec3 &) noexcept;
+	void AngularVelocity(const glm::vec3 &v) noexcept { angular_velocity = v; }
 
-	const glm::quat &Rotation() const noexcept { return rotation; }
-	void Rotation(const glm::quat &) noexcept;
+	const glm::quat &Orientation() const noexcept { return model.Orientation(); }
+	void Orientation(const glm::quat &o) noexcept { model.Orientation(o); }
 	void Rotate(const glm::quat &delta) noexcept;
 
+	glm::mat4 ChunkTransform(const Chunk::Pos &chunk_offset) const noexcept;
 	glm::mat4 Transform(const Chunk::Pos &chunk_offset) const noexcept;
 	Ray Aim(const Chunk::Pos &chunk_offset) const noexcept;
 
@@ -67,24 +66,21 @@ public:
 
 	void Update(int dt) noexcept;
 
-	void Draw() noexcept {
-		model.Draw();
+	void Render(const glm::mat4 &M, DirectionalLighting &prog) noexcept {
+		model.Render(M, prog);
 	}
 
 private:
-	const Shape *shape;
-	EntityModel model;
+	CompositeModel model;
 
 	std::string name;
 
 	AABB bounds;
 
 	glm::vec3 velocity;
-	Block::Pos position;
 	Chunk::Pos chunk;
 
 	glm::vec3 angular_velocity;
-	glm::quat rotation;
 
 	bool world_collision;
 	bool remove;
diff --git a/src/world/World.cpp b/src/world/World.cpp
index aa347e7..19219e4 100644
--- a/src/world/World.cpp
+++ b/src/world/World.cpp
@@ -357,10 +357,7 @@ void World::Render(Viewport &viewport) {
 	entity_prog.SetFogDensity(fog_density);
 
 	for (Entity &entity : entities) {
-		if (entity.HasShape()) {
-			entity_prog.SetM(entity.Transform(player->ChunkCoords()));
-			entity.Draw();
-		}
+		entity.Render(entity.ChunkTransform(player->ChunkCoords()), entity_prog);
 	}
 }
 
-- 
2.39.5