From cacc0641e5174d8b46e7a7086be6a45c87ab3642 Mon Sep 17 00:00:00 2001
From: Daniel Karbach <daniel.karbach@localhorst.tv>
Date: Sun, 12 Nov 2017 18:55:22 +0100
Subject: [PATCH] (slightly) better camera handling

---
 src/app/MasterState.hpp   |  4 +-
 src/app/states.cpp        | 36 +++++++--------
 src/blobs.cpp             | 31 +++++++++----
 src/graphics/Camera.hpp   | 31 ++++++++++---
 src/graphics/viewport.cpp | 96 +++++++++++++++++++++++++++++++++++----
 src/world/Simulation.hpp  | 18 ++++++--
 src/world/sim.cpp         | 20 ++++++--
 src/world/world.cpp       |  3 ++
 8 files changed, 188 insertions(+), 51 deletions(-)

diff --git a/src/app/MasterState.hpp b/src/app/MasterState.hpp
index f62c773..d2072ee 100644
--- a/src/app/MasterState.hpp
+++ b/src/app/MasterState.hpp
@@ -28,7 +28,8 @@ public:
 	MasterState &operator =(MasterState &&) = delete;
 
 public:
-	void SetReference(world::Body &r) { reference = &r; }
+	graphics::Camera &GetCamera() noexcept { return cam; }
+	const graphics::Camera &GetCamera() const noexcept { return cam; }
 
 private:
 	void OnResize(int w, int h) override;
@@ -44,7 +45,6 @@ private:
 private:
 	Assets &assets;
 	world::Simulation &sim;
-	world::Body *reference;
 
 	graphics::Camera cam;
 
diff --git a/src/app/states.cpp b/src/app/states.cpp
index 67c073a..b9fc57a 100644
--- a/src/app/states.cpp
+++ b/src/app/states.cpp
@@ -1,7 +1,9 @@
 #include "MasterState.hpp"
 
 #include "../world/Body.hpp"
+#include "../world/Planet.hpp"
 #include "../world/Simulation.hpp"
+#include "../world/Sun.hpp"
 
 #include <glm/gtx/transform.hpp>
 
@@ -13,17 +15,10 @@ MasterState::MasterState(Assets &assets, world::Simulation &sim) noexcept
 : State()
 , assets(assets)
 , sim(sim)
-, reference(&sim.Root())
-, cam()
+, cam(sim.Root())
 , remain(0)
 , thirds(0)
 , paused(false) {
-	// sunset view: standing in the center of surface 0 (+Z), looking west (-X)
-	//cam.View(glm::lookAt(glm::vec3(0.0f, 0.0f, 5.6f), glm::vec3(-1.0f, 0.0f, 5.6f), glm::vec3(0.0f, 0.0f, 1.0f)));
-	// sunrise view: standing in the center of surface 0 (+Z), looking east (+X)
-	cam.View(glm::lookAt(glm::vec3(0.0f, 0.0f, 5.6f), glm::vec3(1.0f, 0.0f, 5.6f), glm::vec3(0.0f, 0.0f, 1.0f)));
-	// far out, looking at planet
-	//cam.View(glm::lookAt(glm::vec3(10.0f, 10.0f, 50.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)));
 }
 
 MasterState::~MasterState() noexcept {
@@ -61,22 +56,25 @@ void MasterState::OnKeyDown(const SDL_KeyboardEvent &e) {
 }
 
 void MasterState::OnRender(graphics::Viewport &viewport) {
-	glm::dmat4 ppos = reference->InverseTransform() * reference->ToParent();
+	glm::dmat4 ppos = cam.Model(**sim.Suns().begin());
 	assets.shaders.planet_surface.Activate();
 	assets.shaders.planet_surface.SetTexture(assets.textures.tiles);
+	assets.shaders.planet_surface.SetLight(glm::vec3(cam.View() * ppos[3]), glm::vec3(1.0f, 1.0f, 1.0f), 1.0e6f);
 
-	assets.shaders.planet_surface.SetMVP(glm::mat4(1.0f), cam.View(), cam.Projection());
-	assets.shaders.planet_surface.SetLight(glm::vec3(cam.View() * ppos[3]), glm::vec3(1.0f, 1.0f, 1.0f), 2.0e4f);
-	reference->Draw(assets, viewport);
-
-	world::Body *child = reference->Children()[0];
-	assets.shaders.planet_surface.SetMVP(reference->InverseTransform() * child->FromParent() * child->LocalTransform(), cam.View(), cam.Projection());
-	child->Draw(assets, viewport);
+	for (auto planet : sim.Planets()) {
+		assets.shaders.planet_surface.SetMVP(cam.Model(*planet), cam.View(), cam.Projection());
+		planet->Draw(assets, viewport);
+	}
 
 	assets.shaders.sun_surface.Activate();
-	assets.shaders.sun_surface.SetMVP(ppos * reference->Parent().LocalTransform(), cam.View(), cam.Projection());
-	assets.shaders.sun_surface.SetLight(glm::vec3(1.0f, 1.0f, 1.0f), 2.0e4f);
-	assets.shaders.sun_surface.Draw();
+	for (auto sun : sim.Suns()) {
+		double sun_radius = sun->Radius();
+		assets.shaders.sun_surface.SetMVP(
+			cam.Model(*sun) * glm::scale(glm::vec3(sun_radius, sun_radius, sun_radius)),
+			cam.View(), cam.Projection());
+		assets.shaders.sun_surface.SetLight(glm::vec3(1.0f, 1.0f, 1.0f), 1.0e6f);
+		assets.shaders.sun_surface.Draw();
+	}
 }
 
 }
diff --git a/src/blobs.cpp b/src/blobs.cpp
index c302159..464273b 100644
--- a/src/blobs.cpp
+++ b/src/blobs.cpp
@@ -14,12 +14,12 @@
 using namespace blobs;
 
 int main(int argc, char *argv[]) {
-	app::Init init;
+	app::Init init(true, 8);
 	app::Assets assets;
 
 	world::Sun sun;
 	sun.Mass(1.0e12);
-	sun.Radius(1.0);
+	sun.Radius(10.0);
 	sun.SurfaceTilt(glm::dvec2(PI * 0.25, PI * 0.25));
 	sun.AngularMomentum(1.0e9);
 
@@ -27,28 +27,43 @@ int main(int argc, char *argv[]) {
 	world::GenerateTest(planet);
 	planet.SetParent(sun);
 	planet.Mass(1.0e9);
-	planet.GetOrbit().SemiMajorAxis(100.0);
+	planet.GetOrbit().SemiMajorAxis(941.7);
 	planet.SurfaceTilt(glm::dvec2(PI * 0.25, PI * 0.25));
 	planet.AxialTilt(glm::dvec2(PI * 0.127, 0.0));
-	planet.AngularMomentum(3.0e9);
+	planet.AngularMomentum(1.25e9);
 
 	world::Planet moon(3);
 	world::GenerateTest(moon);
 	moon.SetParent(planet);
 	moon.Mass(1.0e6);
 	moon.GetOrbit().SemiMajorAxis(25.0);
-	moon.AngularMomentum(1.0e5);
+	moon.Rotation(PI * 0.25);
+	moon.AngularMomentum(1.0e4);
 
 	world::Simulation sim(sun);
-	sim.AddBody(planet);
-	sim.AddBody(moon);
+	sim.AddSun(sun);
+	sim.AddPlanet(planet);
+	sim.AddPlanet(moon);
 
 	std::cout << "length of year: " << planet.OrbitalPeriod() << "s" << std::endl;
 	std::cout << "length of moon cycle: " << moon.OrbitalPeriod() << "s" << std::endl;
 	std::cout << "length of day: " << planet.RotationalPeriod() << "s" << std::endl;
+	std::cout << "days per year: " << (planet.OrbitalPeriod() / planet.RotationalPeriod()) << std::endl;
+	std::cout << "moon cycle in days: " << (moon.OrbitalPeriod() / planet.RotationalPeriod()) << std::endl;
+	std::cout << "moon cycles per year: " << (planet.OrbitalPeriod() / moon.OrbitalPeriod()) << std::endl;
 
 	app::MasterState state(assets, sim);
-	state.SetReference(planet);
+	state.GetCamera()
+		.Reference(planet)
+		// sunrise
+		.FirstPerson(0, glm::vec3(0.0f, 0.0f, 0.1f), glm::vec3(1.0f, -0.75f, 0.1f))
+		// sunset
+		//.FirstPerson(3, glm::vec3(0.0f, 0.0f, 0.1f), glm::vec3(1.0f, -0.75f, 0.1f))
+		// from afar
+		//.MapView(0, glm::vec3(0.0f, 0.0f, 25.0f), 0.0f)
+		// system view
+		//.Orbital(glm::vec3(50.0f, 2500.0f, 50.0f));
+	;
 	planet.BuildVAOs();
 
 	app::Application app(init.window, init.viewport);
diff --git a/src/graphics/Camera.hpp b/src/graphics/Camera.hpp
index 7cc141e..f4a98d5 100644
--- a/src/graphics/Camera.hpp
+++ b/src/graphics/Camera.hpp
@@ -5,12 +5,15 @@
 
 
 namespace blobs {
+namespace world {
+	class Body;
+}
 namespace graphics {
 
 class Camera {
 
 public:
-	Camera() noexcept;
+	explicit Camera(const world::Body &) noexcept;
 	~Camera() noexcept;
 
 	Camera(const Camera &) = delete;
@@ -20,14 +23,25 @@ public:
 	Camera &operator =(Camera &&) = delete;
 
 public:
-	void FOV(float f) noexcept;
-	void Aspect(float r) noexcept;
-	void Aspect(float w, float h) noexcept;
-	void Clip(float near, float far) noexcept;
+	Camera &FOV(float f) noexcept;
+	Camera &Aspect(float r) noexcept;
+	Camera &Aspect(float w, float h) noexcept;
+	Camera &Clip(float near, float far) noexcept;
+
+	const world::Body &Reference() const noexcept { return *ref; }
+	Camera &Reference(const world::Body &) noexcept;
+
+	/// standing on given surface, with pos.z being elevation over NN
+	/// looking at given coordinates
+	Camera &FirstPerson(int surface, const glm::vec3 &pos, const glm::vec3 &at) noexcept;
+	/// looking straight down at surface from above
+	Camera &MapView(int surface, const glm::vec3 &pos, float roll = 0.0f) noexcept;
+	/// look at center, position relative to orbital reference plane for children
+	Camera &Orbital(const glm::vec3 &pos) noexcept;
 
 	const glm::mat4 &Projection() const noexcept { return projection; }
 	const glm::mat4 &View() const noexcept { return view; }
-	void View(const glm::mat4 &v) noexcept;
+	glm::mat4 Model(const world::Body &) const noexcept;
 
 private:
 	void UpdateProjection() noexcept;
@@ -41,6 +55,11 @@ private:
 	glm::mat4 projection;
 	glm::mat4 view;
 
+	// reference frame
+	const world::Body *ref;
+	// track reference body's orientation
+	bool track_orient;
+
 };
 
 }
diff --git a/src/graphics/viewport.cpp b/src/graphics/viewport.cpp
index cb8d786..2d9b469 100644
--- a/src/graphics/viewport.cpp
+++ b/src/graphics/viewport.cpp
@@ -2,7 +2,9 @@
 #include "Viewport.hpp"
 
 #include "../const.hpp"
+#include "../world/Body.hpp"
 
+#include <cmath>
 #include <GL/glew.h>
 #include <glm/gtx/transform.hpp>
 
@@ -10,41 +12,117 @@
 namespace blobs {
 namespace graphics {
 
-Camera::Camera() noexcept
+Camera::Camera(const world::Body &r) noexcept
 : fov(PI_0p25)
 , aspect(1.0f)
 , near(0.1f)
-, far(256.0f)
+, far(12560.0f)
 , projection(glm::perspective(fov, aspect, near, far))
-, view(1.0f) {
+, view(1.0f)
+, ref(&r)
+, track_orient(false) {
 
 }
 
 Camera::~Camera() noexcept {
 }
 
-void Camera::FOV(float f) noexcept {
+Camera &Camera::FOV(float f) noexcept {
 	fov = f;
 	UpdateProjection();
+	return *this;
 }
 
-void Camera::Aspect(float r) noexcept {
+Camera &Camera::Aspect(float r) noexcept {
 	aspect = r;
 	UpdateProjection();
+	return *this;
 }
 
-void Camera::Aspect(float w, float h) noexcept {
+Camera &Camera::Aspect(float w, float h) noexcept {
 	Aspect(w / h);
+	return *this;
 }
 
-void Camera::Clip(float n, float f) noexcept {
+Camera &Camera::Clip(float n, float f) noexcept {
 	near = n;
 	far = f;
 	UpdateProjection();
+	return *this;
 }
 
-void Camera::View(const glm::mat4 &v) noexcept {
-	view = v;
+Camera &Camera::Reference(const world::Body &r) noexcept {
+	ref = &r;
+	return *this;
+}
+
+Camera &Camera::FirstPerson(int srf, const glm::vec3 &pos, const glm::vec3 &at) noexcept {
+	track_orient = true;
+
+	float dir = srf < 3 ? 1.0f : -1.0f;
+
+	glm::vec3 position;
+	position[(srf + 0) % 3] = pos.x;
+	position[(srf + 1) % 3] = pos.y;
+	position[(srf + 2) % 3] = dir * (pos.z + Reference().Radius());
+
+	glm::vec3 up(0.0f);
+	up[(srf + 2) % 3] = dir;
+
+	glm::vec3 target;
+	target[(srf + 0) % 3] = at.x;
+	target[(srf + 1) % 3] = at.y;
+	target[(srf + 2) % 3] = dir * (at.z + Reference().Radius());
+
+	view = glm::lookAt(position, target, up);
+
+	return *this;
+}
+
+Camera &Camera::MapView(int srf, const glm::vec3 &pos, float roll) noexcept {
+	track_orient = true;
+
+	float dir = srf < 3 ? 1.0f : -1.0f;
+
+	glm::vec3 position;
+	position[(srf + 0) % 3] = pos.x;
+	position[(srf + 1) % 3] = pos.y;
+	position[(srf + 2) % 3] = dir * (pos.z + Reference().Radius());
+
+	glm::vec3 up(0.0f);
+	up[(srf + 0) % 3] = std::cos(roll);
+	up[(srf + 1) % 3] = std::sin(roll);
+	up[(srf + 2) % 3] = 0.0f;
+
+	glm::vec3 target = position;
+	target[(srf + 2) % 3] -= dir;
+
+	view = glm::lookAt(position, target, up);
+
+	return *this;
+}
+
+Camera &Camera::Orbital(const glm::vec3 &pos) noexcept {
+	track_orient = false;
+	view = glm::lookAt(pos, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
+	return *this;
+}
+
+glm::mat4 Camera::Model(const world::Body &b) const noexcept {
+	if (&b == ref) {
+		return track_orient ? glm::mat4(1.0f) : glm::mat4(ref->LocalTransform());
+	} else if (b.HasParent() && &b.Parent() == ref) {
+		return track_orient
+			? ref->InverseTransform() * b.FromParent() * b.LocalTransform()
+			: b.FromParent() * b.LocalTransform();
+	} else if (ref->HasParent() && &ref->Parent() == &b) {
+		return track_orient
+			? ref->InverseTransform() * ref->ToParent() * b.LocalTransform()
+			: ref->ToParent() * b.LocalTransform();
+	} else {
+		// TODO: model matrices for path distances > 1
+		return track_orient ? glm::mat4(1.0f) : glm::mat4(ref->LocalTransform());
+	}
 }
 
 void Camera::UpdateProjection() noexcept {
diff --git a/src/world/Simulation.hpp b/src/world/Simulation.hpp
index 3025a74..ea81de5 100644
--- a/src/world/Simulation.hpp
+++ b/src/world/Simulation.hpp
@@ -1,13 +1,15 @@
 #ifndef BLOBS_WORLD_SIMULATION_HPP_
 #define BLOBS_WORLD_SIMULATION_HPP_
 
-#include <vector>
+#include <set>
 
 
 namespace blobs {
 namespace world {
 
 class Body;
+class Planet;
+class Sun;
 
 class Simulation {
 
@@ -25,15 +27,23 @@ public:
 	void Tick();
 
 	void AddBody(Body &);
+	void AddPlanet(Planet &);
+	void AddSun(Sun &);
 
-	Body &Root() { return root; }
-	const Body &Root() const { return root; }
+	Body &Root() noexcept { return root; }
+	const Body &Root() const noexcept { return root; }
+
+	const std::set<Body *> &Bodies() const noexcept { return bodies; }
+	const std::set<Planet *> &Planets() const noexcept { return planets; }
+	const std::set<Sun *> &Suns() const noexcept { return suns; }
 
 	double Time() const noexcept { return time; }
 
 private:
 	Body &root;
-	std::vector<Body *> all_bodies;
+	std::set<Body *> bodies;
+	std::set<Planet *> planets;
+	std::set<Sun *> suns;
 	double time;
 
 };
diff --git a/src/world/sim.cpp b/src/world/sim.cpp
index 46454fd..185046d 100644
--- a/src/world/sim.cpp
+++ b/src/world/sim.cpp
@@ -1,6 +1,8 @@
 #include "Simulation.hpp"
 
 #include "Body.hpp"
+#include "Planet.hpp"
+#include "Sun.hpp"
 
 
 namespace blobs {
@@ -8,7 +10,9 @@ namespace world {
 
 Simulation::Simulation(Body &r)
 : root(r)
-, all_bodies()
+, bodies()
+, planets()
+, suns()
 , time(0.0) {
 	AddBody(r);
 }
@@ -19,13 +23,23 @@ Simulation::~Simulation() {
 
 void Simulation::AddBody(Body &b) {
 	b.SetSimulation(*this);
-	all_bodies.push_back(&b);
+	bodies.insert(&b);
+}
+
+void Simulation::AddPlanet(Planet &p) {
+	AddBody(p);
+	planets.insert(&p);
+}
+
+void Simulation::AddSun(Sun &s) {
+	AddBody(s);
+	suns.insert(&s);
 }
 
 void Simulation::Tick() {
 	constexpr double dt = 0.01666666666666666666666666666666;
 	time += dt;
-	for (auto body : all_bodies) {
+	for (auto body : bodies) {
 		body->Rotation(body->Rotation() + dt * body->AngularMomentum() / body->Inertia());
 	}
 }
diff --git a/src/world/world.cpp b/src/world/world.cpp
index 0b1b22d..eb03490 100644
--- a/src/world/world.cpp
+++ b/src/world/world.cpp
@@ -254,6 +254,9 @@ void Planet::BuildVAOs() {
 		auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
 		float offset = sidelength * 0.5f;
 
+		// srf  0  1  2  3  4  5
+		//  up +Z +X +Y -Z -X -Y
+
 		for (int index = 0, surface = 0; surface < 6; ++surface) {
 			for (int y = 0; y < sidelength; ++y) {
 				for (int x = 0; x < sidelength; ++x, ++index) {
-- 
2.39.5