-Subproject commit a8e34c508dc619ed8643875431baac64d88765b2
+Subproject commit cc062273ae5591bfa7910ca7fd71f23a35a3270e
#define BLOBS_APP_ASSETS_HPP_
#include "../graphics/ArrayTexture.hpp"
+#include "../graphics/CreatureSkin.hpp"
#include "../graphics/PlanetSurface.hpp"
#include "../graphics/SunSurface.hpp"
std::string path;
std::string tile_path;
+ std::string skin_path;
struct {
graphics::ArrayTexture tiles;
+ graphics::ArrayTexture skins;
} textures;
struct {
graphics::PlanetSurface planet_surface;
graphics::SunSurface sun_surface;
+ graphics::CreatureSkin creature_skin;
} shaders;
Assets();
Assets &operator =(Assets &&) = delete;
void LoadTileTexture(const std::string &name, graphics::ArrayTexture &, int layer) const;
+ void LoadSkinTexture(const std::string &name, graphics::ArrayTexture &, int layer) const;
};
Assets::Assets()
: path("assets/")
-, tile_path(path + "tiles/") {
+, tile_path(path + "tiles/")
+, skin_path(path + "skins/") {
graphics::Format format;
textures.tiles.Bind();
textures.tiles.Reserve(256, 256, 9, format);
LoadTileTexture("7", textures.tiles, 6);
LoadTileTexture("8", textures.tiles, 7);
LoadTileTexture("9", textures.tiles, 8);
+ textures.skins.Bind();
+ textures.skins.Reserve(256, 256, 9, format);
+ LoadSkinTexture("1", textures.skins, 0);
+ LoadSkinTexture("2", textures.skins, 1);
+ LoadSkinTexture("3", textures.skins, 2);
+ LoadSkinTexture("4", textures.skins, 3);
+ LoadSkinTexture("5", textures.skins, 4);
+ LoadSkinTexture("6", textures.skins, 5);
+ LoadSkinTexture("7", textures.skins, 6);
+ LoadSkinTexture("8", textures.skins, 7);
+ LoadSkinTexture("9", textures.skins, 8);
}
Assets::~Assets() {
SDL_FreeSurface(srf);
}
+void Assets::LoadSkinTexture(const std::string &name, graphics::ArrayTexture &tex, int layer) const {
+ std::string path = skin_path + name + ".png";
+ SDL_Surface *srf = IMG_Load(path.c_str());
+ if (!srf) {
+ throw SDLError("IMG_Load");
+ }
+ try {
+ tex.Data(layer, *srf);
+ } catch (...) {
+ SDL_FreeSurface(srf);
+ throw;
+ }
+ SDL_FreeSurface(srf);
+}
+
}
}
#include "MasterState.hpp"
#include "../world/Body.hpp"
+#include "../world/Creature.hpp"
#include "../world/Planet.hpp"
#include "../world/Simulation.hpp"
#include "../world/Sun.hpp"
}
void MasterState::OnRender(graphics::Viewport &viewport) {
- assets.shaders.planet_surface.Activate();
- assets.shaders.planet_surface.SetTexture(assets.textures.tiles);
int num_lights = 0;
for (auto sun : sim.Suns()) {
// TODO: source sun's light color and strength
- assets.shaders.planet_surface.SetLight(
- num_lights,
- glm::vec3(cam.View() * cam.Model(*sun)[3]),
- glm::vec3(1.0f, 1.0f, 1.0f),
- 1.0e6f);
+ glm::vec3 pos(cam.View() * cam.Model(*sun)[3]);
+ glm::vec3 col(1.0f, 1.0f, 1.0f);
+ float str = 1.0e6f;
+ assets.shaders.planet_surface.Activate();
+ assets.shaders.planet_surface.SetLight(num_lights, pos, col, str);
+ assets.shaders.creature_skin.Activate();
+ assets.shaders.creature_skin.SetLight(num_lights, pos, col, str);
++num_lights;
- if (num_lights >= graphics::PlanetSurface::MAX_LIGHTS) {
+ if (num_lights >= graphics::PlanetSurface::MAX_LIGHTS || num_lights >= graphics::CreatureSkin::MAX_LIGHTS) {
break;
}
}
for (auto planet : sim.Planets()) {
// TODO: indirect light from planets, calculate strength and get color somehow
- assets.shaders.planet_surface.SetLight(
- num_lights,
- glm::vec3(cam.View() * cam.Model(*planet)[3]),
- glm::vec3(1.0f, 1.0f, 1.0f),
- 10.0f);
+ glm::vec3 pos(cam.View() * cam.Model(*planet)[3]);
+ glm::vec3 col(1.0f, 1.0f, 1.0f);
+ float str = 10.0f;
+ assets.shaders.planet_surface.Activate();
+ assets.shaders.planet_surface.SetLight(num_lights, pos, col, str);
+ assets.shaders.creature_skin.Activate();
+ assets.shaders.creature_skin.SetLight(num_lights, pos, col, str);
++num_lights;
- if (num_lights >= graphics::PlanetSurface::MAX_LIGHTS) {
+ if (num_lights >= graphics::PlanetSurface::MAX_LIGHTS || num_lights >= graphics::CreatureSkin::MAX_LIGHTS) {
break;
}
}
+ assets.shaders.planet_surface.Activate();
assets.shaders.planet_surface.SetNumLights(num_lights);
+ assets.shaders.creature_skin.Activate();
+ assets.shaders.creature_skin.SetNumLights(num_lights);
+ assets.shaders.planet_surface.Activate();
+ assets.shaders.planet_surface.SetTexture(assets.textures.tiles);
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.SetLight(glm::vec3(1.0f, 1.0f, 1.0f), 1.0e6f);
assets.shaders.sun_surface.Draw();
}
+
+ assets.shaders.creature_skin.Activate();
+ assets.shaders.creature_skin.SetTexture(assets.textures.skins);
+ // TODO: extend to nearby bodies as well
+ for (auto c : cam.Reference().Creatures()) {
+ assets.shaders.creature_skin.SetMVP(cam.Model(c->GetBody()) * glm::mat4(c->LocalTransform()), cam.View(), cam.Projection());
+ c->Draw(assets, viewport);
+ }
}
}
#include "app/Assets.hpp"
#include "app/init.hpp"
#include "app/MasterState.hpp"
+#include "world/Creature.hpp"
#include "world/Planet.hpp"
#include "world/Simulation.hpp"
#include "world/Sun.hpp"
std::cout << "moon cycle in days: " << (moon.OrbitalPeriod() / planet.RotationalPeriod()) << std::endl;
std::cout << "moon cycles per year: " << (planet.OrbitalPeriod() / moon.OrbitalPeriod()) << std::endl;
+ auto blob = new world::Creature;
+ blob->BuildVAO();
+ planet.AddCreature(blob);
+ blob->Surface(0);
+ blob->Position(glm::dvec3(0.0, 0.0, 0.0));
+
app::MasterState state(assets, sim);
state.GetCamera()
.Reference(planet)
--- /dev/null
+#ifndef BLOBS_GRAPHICS_CREATURESKIN_HPP_
+#define BLOBS_GRAPHICS_CREATURESKIN_HPP_
+
+#include "Program.hpp"
+
+#include "glm.hpp"
+
+
+namespace blobs {
+namespace graphics {
+
+class ArrayTexture;
+
+class CreatureSkin {
+
+public:
+ static constexpr int MAX_LIGHTS = 8;
+
+public:
+ CreatureSkin();
+ ~CreatureSkin();
+
+ CreatureSkin(const CreatureSkin &) = delete;
+ CreatureSkin &operator =(const CreatureSkin &) = delete;
+
+ CreatureSkin(CreatureSkin &&) = delete;
+ CreatureSkin &operator =(CreatureSkin &&) = delete;
+
+public:
+ void Activate() noexcept;
+
+ void SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept;
+ void SetTexture(ArrayTexture &) noexcept;
+ void SetLight(int n, const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept;
+ void SetNumLights(int n) noexcept;
+
+ const glm::mat4 &M() const noexcept { return m; }
+ const glm::mat4 &V() const noexcept { return v; }
+ const glm::mat4 &P() const noexcept { return p; }
+ const glm::mat4 &MV() const noexcept { return mv; }
+ const glm::mat4 &MVP() const noexcept { return mvp; }
+
+private:
+ Program prog;
+
+ int num_lights;
+
+ glm::mat4 m;
+ glm::mat4 v;
+ glm::mat4 p;
+ glm::mat4 mv;
+ glm::mat4 mvp;
+
+ GLuint m_handle;
+ GLuint mv_handle;
+ GLuint mvp_handle;
+ GLuint sampler_handle;
+
+ GLuint num_lights_handle;
+ GLuint light_handle[MAX_LIGHTS * 3];
+
+};
+
+}
+}
+
+#endif
+#include "CreatureSkin.hpp"
#include "PlanetSurface.hpp"
#include "Program.hpp"
#include "Shader.hpp"
vao.DrawTriangles(36);
}
+
+constexpr int CreatureSkin::MAX_LIGHTS;
+
+CreatureSkin::CreatureSkin()
+: prog() {
+ prog.LoadShader(
+ GL_VERTEX_SHADER,
+ "#version 330 core\n"
+
+ "layout(location = 0) in vec3 vtx_position;\n"
+ "layout(location = 1) in vec3 vtx_normal;\n"
+ "layout(location = 2) in vec3 vtx_tex_uv;\n"
+
+ "uniform mat4 M;\n"
+ "uniform mat4 MV;\n"
+ "uniform mat4 MVP;\n"
+
+ "out vec3 vtx_viewspace;\n"
+ "out vec3 frag_tex_uv;\n"
+ "out vec3 normal;\n"
+
+ "void main() {\n"
+ "gl_Position = MVP * vec4(vtx_position, 1);\n"
+ "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
+ "normal = normalize((MV * vec4(vtx_normal, 0)).xyz);\n"
+ "frag_tex_uv = vtx_tex_uv;\n"
+ "}\n"
+ );
+ prog.LoadShader(
+ GL_FRAGMENT_SHADER,
+ "#version 330 core\n"
+
+ "struct LightSource {\n"
+ "vec3 position;\n"
+ "vec3 color;\n"
+ "float strength;\n"
+ "};\n"
+
+ "in vec3 vtx_viewspace;\n"
+ "in vec3 frag_tex_uv;\n"
+ "in vec3 normal;\n"
+
+ "uniform sampler2DArray tex_sampler;\n"
+ "uniform int num_lights;\n"
+ "uniform LightSource light[8];\n"
+
+ "out vec3 color;\n"
+
+ "void main() {\n"
+ "vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n"
+ "vec3 total_light = tex_color * vec3(0.01, 0.01, 0.01);\n"
+ "for (int i = 0; i < num_lights; ++i) {\n"
+ "vec3 to_light = light[i].position - vtx_viewspace;\n"
+ "float distance = length(to_light) + length(vtx_viewspace);\n"
+ "vec3 light_dir = normalize(to_light);\n"
+ "float attenuation = light[i].strength / (distance * distance);\n"
+ "vec3 diffuse = attenuation * max(0.0, dot(normal, light_dir)) * light[i].color * tex_color;\n"
+ "vec3 view_dir = vec3(0.0, 0.0, 1.0);\n"
+ "vec3 specular = vec3(0.0, 0.0, 0.0);\n"
+ "if (dot(normal, light_dir) >= 0.0) {\n"
+ "attenuation * light[i].color * pow(max(0.0, dot(reflect(-light_dir, normal), view_dir)), 25.0);\n"
+ "}\n"
+ "total_light = total_light + diffuse + specular;\n"
+ "}\n"
+ "color = total_light;\n"
+ "}\n"
+ );
+ prog.Link();
+ if (!prog.Linked()) {
+ prog.Log(std::cerr);
+ throw std::runtime_error("link program");
+ }
+ m_handle = prog.UniformLocation("M");
+ mv_handle = prog.UniformLocation("MV");
+ mvp_handle = prog.UniformLocation("MVP");
+ sampler_handle = prog.UniformLocation("tex_sampler");
+ num_lights_handle = prog.UniformLocation("num_lights");
+ for (int i = 0; i < MAX_LIGHTS; ++i) {
+ light_handle[3 * i + 0] = prog.UniformLocation("light[" + std::to_string(i) + "].position");
+ light_handle[3 * i + 1] = prog.UniformLocation("light[" + std::to_string(i) + "].color");
+ light_handle[3 * i + 2] = prog.UniformLocation("light[" + std::to_string(i) + "].strength");
+ }
+}
+
+CreatureSkin::~CreatureSkin() {
+}
+
+void CreatureSkin::Activate() noexcept {
+ prog.Use();
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glEnable(GL_CULL_FACE);
+ glDisable(GL_BLEND);
+}
+
+void CreatureSkin::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
+ m = mm;
+ v = vv;
+ p = pp;
+ mv = v * m;
+ mvp = p * mv;
+ prog.Uniform(m_handle, m);
+ prog.Uniform(mv_handle, mv);
+ prog.Uniform(mvp_handle, mvp);
+}
+
+void CreatureSkin::SetTexture(ArrayTexture &tex) noexcept {
+ glActiveTexture(GL_TEXTURE0);
+ tex.Bind();
+ prog.Uniform(sampler_handle, GLint(0));
+}
+
+void CreatureSkin::SetLight(int n, const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept {
+ prog.Uniform(light_handle[3 * n + 0], pos);
+ prog.Uniform(light_handle[3 * n + 1], color);
+ prog.Uniform(light_handle[3 * n + 2], strength);
+}
+
+void CreatureSkin::SetNumLights(int n) noexcept {
+ prog.Uniform(num_lights_handle, std::min(MAX_LIGHTS, n));
+}
+
}
}
}
namespace world {
+class Creature;
class Simulation;
class Body {
void Cache() noexcept;
+ // body takes over ownership of given pointer
+ void AddCreature(Creature *);
+ std::vector<Creature *> &Creatures() noexcept { return creatures; }
+ const std::vector<Creature *> &Creatures() const noexcept { return creatures; }
+
private:
void AddChild(Body &);
void RemoveChild(Body &);
glm::dmat4 local;
glm::dmat4 inverse_local;
+ std::vector<Creature *> creatures;
+
};
}
--- /dev/null
+#ifndef BLOBS_WORLD_CREATURE_HPP_
+#define BLOBS_WORLD_CREATURE_HPP_
+
+#include "../graphics/glm.hpp"
+#include "../graphics/SimpleVAO.hpp"
+
+
+namespace blobs {
+namespace app {
+ struct Assets;
+}
+namespace graphics {
+ class Viewport;
+}
+namespace world {
+
+class Body;
+
+class Creature {
+
+public:
+ Creature();
+ ~Creature();
+
+ Creature(const Creature &) = delete;
+ Creature &operator =(const Creature &) = delete;
+
+ Creature(Creature &&) = delete;
+ Creature &operator =(Creature &&) = delete;
+
+public:
+ void SetBody(Body &b) noexcept { body = &b; }
+ Body &GetBody() noexcept { return *body; }
+ const Body &GetBody() const noexcept { return *body; }
+
+ void Surface(int s) noexcept { surface = s; }
+ void Position(const glm::dvec3 &p) noexcept { position = p; }
+
+ glm::dmat4 LocalTransform() noexcept;
+
+ void BuildVAO();
+ void Draw(app::Assets &, graphics::Viewport &);
+
+private:
+ Body *body;
+ int surface;
+ glm::dvec3 position;
+
+ struct Attributes {
+ glm::vec3 position;
+ glm::vec3 normal;
+ glm::vec3 texture;
+ };
+ graphics::SimpleVAO<Attributes, unsigned short> vao;
+
+};
+
+}
+}
+
+#endif
#include "../graphics/SimpleVAO.hpp"
#include <cassert>
-#include <memory>
+#include <vector>
#include <GL/glew.h>
glm::dvec3 TileCenter(int surface, int x, int y) const noexcept;
- void BuildVAOs(const TileSet &);
+ void BuildVAO(const TileSet &);
void Draw(app::Assets &, graphics::Viewport &) override;
private:
int sidelength;
- std::unique_ptr<Tile []> tiles;
+ std::vector<Tile> tiles;
struct Attributes {
glm::vec3 position;
--- /dev/null
+#include "Creature.hpp"
+
+#include "Body.hpp"
+
+#include <glm/gtx/transform.hpp>
+
+
+namespace blobs {
+namespace world {
+
+Creature::Creature()
+: vao() {
+}
+
+Creature::~Creature() {
+}
+
+
+glm::dmat4 Creature::LocalTransform() noexcept {
+ // TODO: surface transform
+ constexpr double half_height = 0.25;
+ return glm::translate(glm::dvec3(position.x, position.y, position.z + body->Radius() + half_height))
+ * glm::scale(glm::dvec3(half_height, half_height, half_height));
+}
+
+void Creature::BuildVAO() {
+ vao.Bind();
+ vao.BindAttributes();
+ vao.EnableAttribute(0);
+ vao.EnableAttribute(1);
+ vao.EnableAttribute(2);
+ vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
+ vao.AttributePointer<glm::vec3>(1, false, offsetof(Attributes, normal));
+ vao.AttributePointer<glm::vec3>(2, false, offsetof(Attributes, texture));
+ vao.ReserveAttributes(6 * 4, GL_STATIC_DRAW);
+ {
+ auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
+ const float offset = 1.0f;
+ for (int surface = 0; surface < 6; ++surface) {
+ const float tex_u_begin = surface < 3 ? 1.0f : 0.0f;
+ const float tex_u_end = surface < 3 ? 0.0f : 1.0f;
+
+ attrib[4 * surface + 0].position[(surface + 0) % 3] = -offset;
+ attrib[4 * surface + 0].position[(surface + 1) % 3] = -offset;
+ attrib[4 * surface + 0].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
+ attrib[4 * surface + 0].normal[(surface + 0) % 3] = 0.0f;
+ attrib[4 * surface + 0].normal[(surface + 1) % 3] = 0.0f;
+ attrib[4 * surface + 0].normal[(surface + 2) % 3] = surface < 3 ? 1.0f : -1.0f;
+ attrib[4 * surface + 0].texture.x = tex_u_begin;
+ attrib[4 * surface + 0].texture.y = 1.0f;
+ attrib[4 * surface + 0].texture.z = surface;
+
+ attrib[4 * surface + 1].position[(surface + 0) % 3] = -offset;
+ attrib[4 * surface + 1].position[(surface + 1) % 3] = offset;
+ attrib[4 * surface + 1].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
+ attrib[4 * surface + 1].normal[(surface + 0) % 3] = 0.0f;
+ attrib[4 * surface + 1].normal[(surface + 1) % 3] = 0.0f;
+ attrib[4 * surface + 1].normal[(surface + 2) % 3] = surface < 3 ? 1.0f : -1.0f;
+ attrib[4 * surface + 1].texture.x = tex_u_end;
+ attrib[4 * surface + 1].texture.y = 1.0f;
+ attrib[4 * surface + 1].texture.z = surface;
+
+ attrib[4 * surface + 2].position[(surface + 0) % 3] = offset;
+ attrib[4 * surface + 2].position[(surface + 1) % 3] = -offset;
+ attrib[4 * surface + 2].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
+ attrib[4 * surface + 2].normal[(surface + 0) % 3] = 0.0f;
+ attrib[4 * surface + 2].normal[(surface + 1) % 3] = 0.0f;
+ attrib[4 * surface + 2].normal[(surface + 2) % 3] = surface < 3 ? 1.0f : -1.0f;
+ attrib[4 * surface + 2].texture.x = tex_u_begin;
+ attrib[4 * surface + 2].texture.y = 0.0f;
+ attrib[4 * surface + 2].texture.z = surface;
+
+ attrib[4 * surface + 3].position[(surface + 0) % 3] = offset;
+ attrib[4 * surface + 3].position[(surface + 1) % 3] = offset;
+ attrib[4 * surface + 3].position[(surface + 2) % 3] = surface < 3 ? offset : -offset;
+ attrib[4 * surface + 3].normal[(surface + 0) % 3] = 0.0f;
+ attrib[4 * surface + 3].normal[(surface + 1) % 3] = 0.0f;
+ attrib[4 * surface + 3].normal[(surface + 2) % 3] = surface < 3 ? 1.0f : -1.0f;
+ attrib[4 * surface + 3].texture.x = tex_u_end;
+ attrib[4 * surface + 3].texture.y = 0.0f;
+ attrib[4 * surface + 3].texture.z = surface;
+ }
+ }
+ vao.BindElements();
+ vao.ReserveElements(6 * 6, GL_STATIC_DRAW);
+ {
+ auto element = vao.MapElements(GL_WRITE_ONLY);
+ for (int surface = 0; surface < 3; ++surface) {
+ element[6 * surface + 0] = 4 * surface + 0;
+ element[6 * surface + 1] = 4 * surface + 2;
+ element[6 * surface + 2] = 4 * surface + 1;
+ element[6 * surface + 3] = 4 * surface + 1;
+ element[6 * surface + 4] = 4 * surface + 2;
+ element[6 * surface + 5] = 4 * surface + 3;
+ }
+ for (int surface = 3; surface < 6; ++surface) {
+ element[6 * surface + 0] = 4 * surface + 0;
+ element[6 * surface + 1] = 4 * surface + 1;
+ element[6 * surface + 2] = 4 * surface + 2;
+ element[6 * surface + 3] = 4 * surface + 2;
+ element[6 * surface + 4] = 4 * surface + 1;
+ element[6 * surface + 5] = 4 * surface + 3;
+ }
+ }
+ vao.Unbind();
+}
+
+void Creature::Draw(app::Assets &assets, graphics::Viewport &viewport) {
+ vao.Bind();
+ vao.DrawTriangles(6 * 6);
+}
+
+}
+}
#include "TileSet.hpp"
#include "TileType.hpp"
+#include "Creature.hpp"
#include "../const.hpp"
#include "../app/Assets.hpp"
#include "../graphics/Viewport.hpp"
, orbital(1.0)
, inverse_orbital(1.0)
, local(1.0)
-, inverse_local(1.0) {
+, inverse_local(1.0)
+, creatures() {
}
Body::~Body() {
+ for (Creature *c : creatures) {
+ delete c;
+ }
}
void Body::SetSimulation(Simulation &s) noexcept {
* glm::eulerAngleY(-rotation);
}
+void Body::AddCreature(Creature *c) {
+ c->SetBody(*this);
+ creatures.push_back(c);
+}
+
Orbit::Orbit()
: sma(1.0)
Planet::Planet(int sidelength)
: Body()
, sidelength(sidelength)
-, tiles(new Tile[TilesTotal()])
+, tiles(TilesTotal())
, vao() {
Radius(double(sidelength) / 2.0);
}
return center;
}
-void Planet::BuildVAOs(const TileSet &ts) {
+void Planet::BuildVAO(const TileSet &ts) {
vao.Bind();
vao.BindAttributes();
vao.EnableAttribute(0);
}
}
}
- p.BuildVAOs(tiles);
+ p.BuildVAO(tiles);
}
void GenerateTest(const TileSet &tiles, Planet &p) noexcept {
}
}
}
- p.BuildVAOs(tiles);
+ p.BuildVAO(tiles);
}