You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
344 lines
9.2 KiB
344 lines
9.2 KiB
#include "level.h" |
|
#include "entity.h" |
|
#include "player.h" |
|
#include <glm/ext/vector_float3.hpp> |
|
#include "collision.h" |
|
#include <optional> |
|
|
|
|
|
#define DIRECTIONAL_LIGHT 0 |
|
#define POINT_LIGHT 1 |
|
#define SPOT_LIGHT 2 |
|
|
|
std::optional<physics_model> level::physics_from_json(json p) noexcept { |
|
|
|
if (!(p.count("position") && p.count("orientation"))) { |
|
return {}; |
|
} |
|
try { |
|
// anonymous physics obj |
|
glm::vec3 position {p["position"][0], p["position"][1], p["position"][2]}; |
|
glm::mat4 orientation = glm::mat4(1.0); // identity matrix |
|
orientation = glm::rotate(orientation, (float)p["orientation"][0], glm::vec3(1,0,0)); |
|
orientation = glm::rotate(orientation, (float)p["orientation"][1], glm::vec3(0,1,0)); |
|
orientation = glm::rotate(orientation, (float)p["orientation"][2], glm::vec3(0,0,1)); |
|
|
|
std::optional<glm::vec2> aabb {}; |
|
|
|
std::vector<collision::Collider> colliders {}; |
|
|
|
if (p.count("collision")) { |
|
for (json c : p["collision"]) { |
|
std::optional<collision::Collider> res = collision::Collider::from_json(c); |
|
if (res) { |
|
fmt::print("Added collision shape: {}\n", res->get_name()); |
|
colliders.push_back(*res); |
|
} |
|
} |
|
} |
|
|
|
return physics_model {.pos = position, .orientation = orientation, .colliders=colliders}; |
|
|
|
} catch (std::exception &e) { |
|
return {}; |
|
} |
|
|
|
}; |
|
|
|
|
|
auto LIGHT_TYPES = std::unordered_map<std::string, unsigned int> {{"directional", 0}, {"point", 1}, {"spot", 2}}; |
|
|
|
|
|
void update_lights(Shader * shader, json &j) { |
|
int i = 0; |
|
for (auto &l: j["lights"]) { |
|
if (i >= 9) { |
|
A_ERROR("More than max lights defined"); |
|
} |
|
|
|
glCheckError(); |
|
shader->setInt(fmt::format("lights[{}].type", i), LIGHT_TYPES.at(l["type"])); |
|
glCheckError(); |
|
shader->setVec3(fmt::format("lights[{}].position", i), |
|
glm::vec3(l["position"][0],l["position"][1], l["position"][2])); |
|
glCheckError(); |
|
shader->setVec3(fmt::format("lights[{}].ambient", i), |
|
glm::vec3(l["ambient"][0],l["ambient"][1], l["ambient"][2])); |
|
glCheckError(); |
|
shader->setVec3(fmt::format("lights[{}].diffuse", i), |
|
glm::vec3(l["diffuse"][0],l["diffuse"][1], l["diffuse"][2])); |
|
glCheckError(); |
|
shader->setVec3(fmt::format("lights[{}].specular", i), |
|
glm::vec3(l["specular"][0],l["specular"][1], l["specular"][2])); |
|
glCheckError(); |
|
shader->setBool(fmt::format("lights[{}].attenuate", i), l["attenuate"]); |
|
glCheckError(); |
|
shader->setVec3(fmt::format("lights[{}].attenuation", i), |
|
glm::vec3(l["attenuation"][0],l["attenuation"][1], l["attenuation"][2])); |
|
glCheckError(); |
|
|
|
i++; |
|
} |
|
shader->setInt("num_lights", i); |
|
|
|
shader->setVec3("material.specular", glm::vec3(0.7)); |
|
shader->setVec3("material.diffuse", glm::vec3(1.0, 0.7, 0.8)); |
|
shader->setVec3("material.ambient", glm::vec3(0.2)); |
|
shader->setFloat("material.shininess", 32); |
|
|
|
|
|
glCheckError(); |
|
} |
|
|
|
|
|
void level::load_physics(json j) { |
|
for (auto &p: j["physics"]) { |
|
auto id = phys_meshes.size(); |
|
auto id_slug = fmt::format("{}", id); |
|
if (j.count("id")) { |
|
id_slug = j.at("id"); |
|
} |
|
|
|
if (phys_ids.count(id_slug)) { |
|
fmt::print("warn::level::load_physics overwritten physics mesh: '{}'\n", id_slug); |
|
} |
|
|
|
auto model = physics_from_json(p); |
|
if (model) { |
|
phys_meshes.push_back(*model); |
|
phys_ids.insert({id_slug, id}); |
|
} else { |
|
fmt::print("warn::level::load_physics failed to load physics {}\n", id_slug); |
|
} |
|
|
|
} |
|
} |
|
|
|
|
|
void level::load_entities(json j) { |
|
int i = 0; |
|
for (auto &m: j["entities"]) { |
|
entity e {}; |
|
e.id = i++; |
|
bool model = false; |
|
bool physics = false; |
|
|
|
std::string entity_id = fmt::format("{}", i); |
|
if (m.count("id")) { |
|
entity_id = m.at("id"); |
|
} |
|
|
|
if (m.count("draw")) { |
|
model = true; |
|
if (m["draw"].count("model")) { |
|
e.model = model_ids.at(m["draw"].at("model")); |
|
e.type = entity_type::STATIC_MESH; |
|
} else if (m["draw"].count("billboardmodel")) { |
|
e.type = entity_type::BILLBOARD; |
|
e.model = model_ids.at(m["draw"].at("billboardmodel")); |
|
|
|
} |
|
} |
|
|
|
if (m.count("physics")) { |
|
|
|
auto p = m["physics"]; |
|
physics = true; |
|
|
|
|
|
physics_model entity_physics; |
|
if (p.count("id")) { |
|
e.physics = phys_ids[p.at("id")]; |
|
} else { |
|
auto ph = physics_from_json(p); |
|
if (ph) { |
|
entity_physics = *ph; |
|
int model_id = phys_meshes.size(); |
|
phys_meshes.emplace_back(entity_physics); |
|
e.physics = model_id; |
|
|
|
auto physics_id = fmt::format("{}", entity_id); |
|
phys_ids.insert({physics_id, model_id}); |
|
} else { |
|
throw std::exception {}; |
|
} |
|
} |
|
} |
|
|
|
|
|
if (! (model && physics)) { |
|
fmt::print("warn::level invisible entity"); |
|
} |
|
entities.push_back(e); |
|
} |
|
|
|
} |
|
|
|
|
|
level::level(const std::string &json_source) |
|
{ |
|
json l; |
|
|
|
json_source_file = json_source; |
|
std::ifstream inp (json_source); |
|
inp >> l; |
|
fmt::print("loading level{}", l["name"]); |
|
|
|
|
|
|
|
if (l == nullptr) { |
|
A_ERROR("level | failed to parse json"); |
|
} |
|
|
|
// main shader should be first |
|
for (auto &s : l["shaders"]) { |
|
if (s.size() > 1) { |
|
shaders.emplace_back(fmt::format("{}/{}",SHADER_DIR , s[0]), fmt::format("{}/{}", SHADER_DIR , s[1])); |
|
} else if (s.size() > 0) { |
|
shaders.emplace_back(fmt::format("{}/{}",SHADER_DIR , s[0])) ; |
|
} |
|
} |
|
|
|
if (l.size() <= 0) { |
|
A_ERROR("level | no shader"); |
|
} |
|
|
|
shader = &shaders[0]; |
|
|
|
shader->use(); |
|
int mid = 0; |
|
|
|
for (auto & [key, v] : l["staticmodels"].items()) { |
|
stbi_set_flip_vertically_on_load(false); |
|
if (v.count("fliptextures")) { |
|
stbi_set_flip_vertically_on_load(v.at("fliptextures")); |
|
} |
|
models.emplace_back(new Model(fmt::format("{}/{}/{}", OBJ_DIR, key, v["object"]))); |
|
model_ids.insert({key, mid}); |
|
mid++; |
|
} |
|
stbi_set_flip_vertically_on_load(false); |
|
|
|
load_physics(l); |
|
load_entities(l); |
|
|
|
update_lights(shader, l); |
|
|
|
// shader->setVec3("material.specular", glm::vec3(0.7)); |
|
// glCheckError(); |
|
// shader->setVec3("material.diffuse", glm::vec3(1.0, 0.7, 0.8)); |
|
// glCheckError(); |
|
// shader->setVec3("material.ambient", glm::vec3(0.2)); |
|
// glCheckError(); |
|
// shader->setFloat("material.shininess", 32.0); |
|
// glCheckError(); |
|
|
|
} |
|
|
|
|
|
|
|
void level::draw(const glm::mat4 view, const glm::mat4 projection) { |
|
|
|
// std::multimap<float, int> order {}; |
|
|
|
struct job { |
|
Mesh *m; |
|
physics_model phys; |
|
entity e; |
|
}; |
|
std::multimap<float, job> order {}; |
|
|
|
auto camera = player::get_camera(); |
|
auto camright = glm::vec3(view[0][0], view[1][0], view[2][0]); |
|
auto camup = camera->up; |
|
|
|
#ifdef SHOW_DRAW_ORDER |
|
static int prev = 0; |
|
static int ticks = 0; |
|
|
|
int timen = SDL_GetTicks() / 300; |
|
if (timen > ticks) { |
|
ticks = timen; |
|
prev++; |
|
} |
|
#endif |
|
|
|
|
|
for (const entity &e: entities) { |
|
for (int i = 0; i < models[e.model]->meshes.size(); i++) { |
|
Mesh *m = &models[e.model]->meshes[0] + i; |
|
glm::vec3 pos = phys_meshes[e.physics].pos + m->average_position; |
|
pos = camera->pos - pos; |
|
float depth = glm::length(pos); |
|
|
|
order.insert({depth, |
|
job {m, phys_meshes[e.physics], e} |
|
}); |
|
} |
|
} |
|
|
|
|
|
int i = 0; |
|
for (auto iter = order.rbegin(); iter != order.rend(); ++iter) { |
|
#ifdef SHOW_DRAW_ORDER |
|
if (i++ >= prev % order.size() + 1) { |
|
break; |
|
} |
|
#endif |
|
|
|
auto k = iter->first; |
|
job v = iter->second; |
|
|
|
auto model = v.phys.orientation; |
|
|
|
if (v.e.type == entity_type::BILLBOARD) { |
|
// particle billboarding |
|
// http://www.opengl-tutorial.org/intermediate-tutorials/billboards-particles/billboards/ |
|
model = glm::mat4(glm::mat3(glm::inverse(view))); |
|
} |
|
|
|
setup_shader(shader, view, projection, v.phys.pos, model); |
|
v.m->draw(shader); |
|
|
|
// glm::mat4 draw_view = view; |
|
// auto phys = phys_meshes[entities[v].physics]; |
|
|
|
// } |
|
|
|
|
|
|
|
// draw_objects(shader, {phys}, models[entities[v].model], draw_view, projection); |
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void level::update() { |
|
|
|
fmt::print("Reloading level\n"); |
|
for (auto &s: shaders) { |
|
s.reload(); |
|
} |
|
|
|
json l; |
|
try { |
|
|
|
std::ifstream inp (json_source_file); |
|
inp >> l; |
|
} catch (std::exception &e) { |
|
A_WARN("Failed to reload"); |
|
return; |
|
} |
|
|
|
shader->use(); |
|
|
|
update_lights(shader, l); |
|
entities.clear(); |
|
phys_meshes.clear(); |
|
phys_ids.clear(); |
|
load_physics(l); |
|
load_entities(l); |
|
|
|
} |
|
|
|
|