diff options
Diffstat (limited to 'src/objects')
| -rw-r--r-- | src/objects/effect.cc | 89 | ||||
| -rw-r--r-- | src/objects/effect.h | 14 | ||||
| -rw-r--r-- | src/objects/material.cc | 57 | ||||
| -rw-r--r-- | src/objects/material.h | 12 | ||||
| -rw-r--r-- | src/objects/model.cc | 36 | ||||
| -rw-r--r-- | src/objects/model.h | 4 | ||||
| -rw-r--r-- | src/objects/object.cc | 314 | ||||
| -rw-r--r-- | src/objects/object.h | 71 | ||||
| -rw-r--r-- | src/objects/pass.cc | 412 | ||||
| -rw-r--r-- | src/objects/pass.h | 51 | ||||
| -rw-r--r-- | src/objects/scene.cc | 85 | ||||
| -rw-r--r-- | src/objects/scene.h | 20 |
12 files changed, 563 insertions, 602 deletions
diff --git a/src/objects/effect.cc b/src/objects/effect.cc index 72d9e7e..5542ccc 100644 --- a/src/objects/effect.cc +++ b/src/objects/effect.cc @@ -3,86 +3,93 @@ using namespace Mauri; -Effect::Effect(Parser &p, json &root) +Effect::Effect(Parser &pr, const json &root) { - auto asset = asset_manager()->get_file(root["file"], NONE); + Asset *asset = asset_manager()->get_file(root["file"]); + if (asset->type == ASSET_VOID) return; - if (asset->type == ASSET_VOID) - return; + const json &file = json::parse(asset->as_string()); + if (file.is_discarded()) return; - auto file = json::parse(asset->as_string()); + pr.map_value<u32>(root, "id", &this->id, 0); + pr.map_value<bool>(root, "visible", &this->visible, true); + pr.map_value<std::string>(file, "name", &this->name, ""); - p.get_value<u32>(root, "id", &id, 0); - p.get_value<bool>(root, "visible", &visible, true); - p.get_value<std::string>(file, "name", &name, ""); - - for (auto &pass0 : root["passes"]) + if (root.contains("passes")) { - passes0.emplace_back(new Pass(p, pass0, EFFECT0_PASS)); + for (const json &pass0 : root["passes"]) + { + this->passes0.emplace_back(new Pass(pr, pass0, EFFECT0_PASS)); + } } - for (auto &pass1 : file["passes"]) + if (file.contains("passes")) { - passes1.emplace_back(new Pass(p, pass1, EFFECT1_PASS)); + for (const json &pass1 : file["passes"]) + { + this->passes1.emplace_back(new Pass(pr, pass1, EFFECT1_PASS)); + } } - for (auto &fbo : file["fbos"]) + if (file.contains("fbos")) { - fbos.emplace_back(EffectBuffer { fbo["name"], fbo["scale"] }); + for (const json &fbo : file["fbos"]) + { + this->fbos.emplace_back(EffectBuffer{fbo["name"], fbo.contains("scale") ? (f32)fbo["scale"] : 1.f }); + } } } -void Effect::load(Engine *engine, Object *object, bool last) +auto Effect::load(Engine *engine, Object *object, bool last) -> void { - std::stringstream _buffer_id; - _buffer_id << "_" << object->id << "_" << id; - - buffer_id = _buffer_id.str(); + this->buffer_id = str_format("_%i_%i", object->id, this->id); - for (auto &fbo : fbos) + for (const EffectBuffer &fbo : this->fbos) { - engine->create_framebuffer(fbo.name + buffer_id, object->size[0], object->size[1], fbo.scale); + engine->create_framebuffer(fbo.name + this->buffer_id, object->size[0], object->size[1], fbo.scale); } u32 index = 0; - - for (auto i = 0u; i < passes1.size(); i++) + + for (u32 i = 0; i < this->passes1.size(); i++) { Pass pass; - pass.intermidiate = false; + pass.intermediate = false; - pass.object = object; pass.effect = this; + pass.object = object; - pass.combine = object->visible && i == (passes1.size() - 1) && last; + pass.model = false; + pass.combine = object->visible && object->color_blend_mode == 0 && + i == (this->passes1.size() - 1) && last; + //pass.combine = object->visible && + // i == (this->passes1.size() - 1) && last; - if (passes1[i]->command != COPY) + if (this->passes1[i]->command != COPY) { - passes0[index]->load(engine, EFFECT0_PASS, &pass); + this->passes0[index]->load(engine, EFFECT0_PASS, &pass); index++; } - passes1[i]->load(engine, EFFECT1_PASS, &pass); + this->passes1[i]->load(engine, EFFECT1_PASS, &pass); this->passes.push_back(new RenderPass(engine, &pass)); } } -void Effect::draw(Engine *engine) +auto Effect::draw(Engine *engine) -> void { - if (!visible) - return; + if (!this->visible) return; #if FRAME_STEP - std::cout << name << ":\n"; + std::cout << this->name << ":\n"; #endif - - for (auto &pass : passes) + + for (RenderPass *pass : this->passes) { #if FRAME_STEP - if (pass->shader != nullptr) - std::cout << "\t" << pass->shader->name << "\n"; + if (pass->shader) std::cout << "\t" << pass->shader->origin()->name << "\n"; #endif pass->draw(engine); } @@ -90,17 +97,17 @@ void Effect::draw(Engine *engine) Effect::~Effect() { - for (auto &pass0 : passes0) + for (Pass *pass0 : this->passes0) { delete pass0; } - for (auto &pass1 : passes1) + for (Pass *pass1 : this->passes1) { delete pass1; } - for (auto &pass : passes) + for (RenderPass *pass : this->passes) { delete pass; } diff --git a/src/objects/effect.h b/src/objects/effect.h index 558396b..cb53a04 100644 --- a/src/objects/effect.h +++ b/src/objects/effect.h @@ -1,7 +1,6 @@ #ifndef _EFFECT_H #define _EFFECT_H -#include "../util.h" #include "../json.h" #include "../engine.h" #include "../texture.h" @@ -9,19 +8,19 @@ namespace Mauri { -class Pass; -class RenderPass; - struct EffectBuffer { std::string name; f32 scale; }; +class Pass; +class RenderPass; + class Effect { public: - Effect(Parser &p, json &root); + Effect(Parser &pr, const json &root); ~Effect(); u32 id; @@ -33,13 +32,12 @@ class Effect std::vector<RenderPass *> passes; - void load(Engine *engine, Object *object, bool last); - void draw(Engine *engine); + auto load(Engine *engine, Object *object, bool last) -> void; + auto draw(Engine *engine) -> void; private: std::vector<Pass *> passes0; std::vector<Pass *> passes1; - std::vector<EffectBuffer> fbos; }; diff --git a/src/objects/material.cc b/src/objects/material.cc index 0957214..b17a102 100644 --- a/src/objects/material.cc +++ b/src/objects/material.cc @@ -3,59 +3,64 @@ using namespace Mauri; -Material::Material(Parser &p, const std::string &path) +Material::Material(Parser &pr, const std::string &path) { - auto asset = asset_manager()->get_file(path, NONE); + Asset *asset = asset_manager()->get_file(path); + if (asset->type == ASSET_VOID) return; - if (asset->type == ASSET_VOID) - { - log_error("failed to load material.json (%s)", path.c_str()); - return; - } - - auto root = json::parse(asset->as_string()); + const json &root = json::parse(asset->as_string()); + if (root.is_discarded()) return; - for (auto &pass : root["passes"]) + if (root.contains("passes")) { - passes0.push_back(new Pass(p, pass, MATERIAL_PASS)); + for (const json &pass : root["passes"]) + { + this->passes0.push_back(new Pass(pr, pass, MATERIAL_PASS)); + } } } -void Material::load(Engine *engine, Object *object, MaterialType type, Pass *pass) +auto Material::load(Engine *engine, Object *object, MaterialType type, Pass *pass) -> void { - for (auto &pass0 : passes0) + for (Pass *pass0 : this->passes0) { switch (type) { case MATERIAL_EFFECT: + { pass0->load(engine, MATERIAL_PASS, pass); break; + } case MATERIAL_MODEL: - Pass _pass; + { + Pass model_pass; + + model_pass.intermediate = false; - _pass.intermidiate = false; + model_pass.object = object; + model_pass.effect = nullptr; - _pass.object = object; - _pass.effect = nullptr; + model_pass.model = true; + model_pass.combine = object->visible && object->effects.size() == 0 && !object->passthrough; - if (object->effects.size() == 0 - && !object->passthrough && object->visible) - _pass.combine = true; - else - _pass.combine = false; - - pass0->load(engine, MATERIAL_ONLY_PASS, &_pass); + if (model_pass.combine) + { + model_pass.combos.push_back(Combo("BLENDMODE", object->color_blend_mode)); + } - object->passes.push_back(new RenderPass(engine, &_pass)); + pass0->load(engine, MATERIAL_ONLY_PASS, &model_pass); + + object->passes.push_back(new RenderPass(engine, &model_pass)); break; } + } } } Material::~Material() { - for (auto &pass0 : passes0) + for (Pass *pass0 : this->passes0) { delete pass0; } diff --git a/src/objects/material.h b/src/objects/material.h index 5bc7c3a..cfea216 100644 --- a/src/objects/material.h +++ b/src/objects/material.h @@ -7,23 +7,19 @@ namespace Mauri { -class Pass; +enum MaterialType { MATERIAL_MODEL, MATERIAL_EFFECT }; -enum MaterialType -{ - MATERIAL_MODEL, - MATERIAL_EFFECT -}; +class Pass; class Material { public: - Material(Parser &p, const std::string &path); + Material(Parser &pr, const std::string &path); ~Material(); std::vector<Pass *> passes0; - void load(Engine *engine, Object *object, MaterialType type, Pass *pass); + auto load(Engine *engine, Object *object, MaterialType type, Pass *pass) -> void; }; } // namespace Mauri diff --git a/src/objects/model.cc b/src/objects/model.cc index 1517b23..b916ec7 100644 --- a/src/objects/model.cc +++ b/src/objects/model.cc @@ -2,38 +2,30 @@ using namespace Mauri; -Model::Model(Parser &p, const std::string &path) +Model::Model(Parser &pr, const std::string &path) { - auto asset = asset_manager()->get_file(path, NONE); + Asset *asset = asset_manager()->get_file(path); + if (asset->type == ASSET_VOID) return; - if (asset->type == ASSET_VOID) - { - log_error("failed to load model.json (%s)", path.c_str()); - return; - } + const json &root = json::parse(asset->as_string()); + if (root.is_discarded()) return; - auto root = json::parse(asset->as_string()); + pr.map_value<bool>(root, "autosize", &this->autosize, false); + pr.map_value<bool>(root, "fullscreen", &this->fullscreen, false); + pr.map_value<bool>(root, "passthrough", &this->passthrough, false); - p.get_value<bool>(root, "autosize", &autosize, false); - p.get_value<bool>(root, "fullscreen", &fullscreen, false); - p.get_value<bool>(root, "passthrough", &passthrough, false); + pr.map_value<f32>(root, "width", &this->width, 0.f); + pr.map_value<f32>(root, "height", &this->height, 0.f); - p.get_value<f32>(root, "width", &width, 0.f); - p.get_value<f32>(root, "height", &height, 0.f); - - auto _material = root["material"]; - - if (_material.is_string()) - material = new Material(p, _material); + if (root.contains("material")) this->material = new Material(pr, root["material"]); } -void Model::load(Engine *engine, Object *object) +auto Model::load(Engine *engine, Object *object) -> void { - material->load(engine, object, MATERIAL_MODEL, NULL); + this->material->load(engine, object, MATERIAL_MODEL, NULL); } Model::~Model() { - if (material != nullptr) - delete material; + if (this->material) delete this->material; } diff --git a/src/objects/model.h b/src/objects/model.h index 0d5b313..f6b532a 100644 --- a/src/objects/model.h +++ b/src/objects/model.h @@ -9,7 +9,7 @@ namespace Mauri class Model { public: - Model(Parser &p, const std::string &path); + Model(Parser &pr, const std::string &path); ~Model(); bool autosize = true; @@ -19,7 +19,7 @@ class Model f32 width; f32 height; - void load(Engine *engine, Object *object); + auto load(Engine *engine, Object *object) -> void; private: Material *material = nullptr; diff --git a/src/objects/object.cc b/src/objects/object.cc index 73a16b3..ac64a25 100644 --- a/src/objects/object.cc +++ b/src/objects/object.cc @@ -1,32 +1,33 @@ +#include "../context.h" + #include "pass.h" #include "scene.h" #include "object.h" using namespace Mauri; -Object::Object(Parser &p, json &root) +Object::Object(Parser &pr, const json &root) { - p.get_value<u32>(root, "id", &id, 0); - p.get_value<std::string>(root, "name", &name, ""); + pr.map_value<u32>(root, "id", &this->id, 0); + pr.map_value<std::string>(root, "name", &this->name, ""); - for (auto &id : root["dependencies"]) - deps.push_back(id); + if (root.contains("dependencies")) + { + for (const json &id : root["dependencies"]) + { + this->deps.push_back(id); + } + } // Filter deps that are either have the same id as this // object or are repeated. This could be handled be sorting objects // on the scene in order of deps but I'm not sure if that would // break other things. - std::vector<s32>::const_iterator it = deps.begin(); - while (it != deps.end()) + auto it = this->deps.begin(); + while (it != this->deps.end()) { - bool remove = false; - - if ((u32)(*it) == id) - { - remove = true; - } - - for (auto rt = deps.begin(); rt != deps.end(); ++rt) + bool remove = ((u32)(*it) == id); + for (auto rt = this->deps.begin(); rt != this->deps.end(); ++rt) { if (rt != it && *rt == *it) { @@ -34,217 +35,228 @@ Object::Object(Parser &p, json &root) break; } } - - if (remove) deps.erase(it); - else ++it; + if (remove) this->deps.erase(it); + else it++; } - p.get_value<vec3>(root, "angles", &angles, vec3(0.f)); - p.get_value<vec3>(root, "color", &color, vec3(0.f)); - p.get_value<vec2>(root, "size", &size, vec2(0.f)); - p.get_value<vec3>(root, "scale", &scale, vec3(0.f)); - p.get_value<vec3>(root, "origin", &origin, vec3(0.f)); - //origin[2] = 0.f; - - p.get_value<s32>(root, "colorBlendMode", &color_blend_mode, 0); - p.get_value<f32>(root, "alpha", &alpha, 1.f); - p.get_value<bool>(root, "visible", &visible, true); - - auto _image = root["image"]; + pr.map_value<bool>(root, "visible", &this->visible, true); + pr.map_value<vec3>(root, "angles", &this->angles, vec3(0.f)); + pr.map_value<vec2>(root, "size", &this->size, vec2(0.f)); + pr.map_value<vec3>(root, "scale", &this->scale, vec3(0.f)); + pr.map_value<vec3>(root, "origin", &this->origin, vec3(0.f)); + if (this->origin[2] != 0.f) + { + warn("non 0 origin[2]"); + this->origin[2] = 0.f; + } + pr.map_value<vec3>(root, "color", &this->color, vec3(0.f)); + pr.map_value<f32>(root, "alpha", &this->alpha, 1.f); + pr.map_value<s32>(root, "colorBlendMode", &this->color_blend_mode, 0); - if (_image.is_string()) - image = new Model(p, _image); + this->color4 = vec4{this->color[0], this->color[1], this->color[2], this->alpha}; - auto config = root["config"]; + if (root.contains("image")) + { + const json &image = root["image"]; + if (image.is_string()) this->model = new Model(pr, image); + } - if (!config.is_null()) - p.get_value<bool>(config, "passthrough", &passthrough, false); + if (root.contains("config")) + { + pr.map_value<bool>(root["config"], "passthrough", &this->passthrough, false); + } - for (auto &effect : root["effects"]) + if (root.contains("effects")) { - effects.emplace_back(new Effect(p, effect)); + for (const json &effect : root["effects"]) + { + this->effects.emplace_back(new Effect(pr, effect)); + } } } -void Object::get_model(Engine *engine, f32 width, f32 height, bool flip) +auto Object::load(Engine *engine) -> void { - model = mat4x4(1.f); - - model = glm::translate(model, vec3{-width, -height, -origin[2]}); - model = glm::translate(model, origin * 2.f); - - model = glm::rotate(model, angles[0], vec3{1.f, 0.f, 0.f}); - model = glm::rotate(model, angles[1], vec3{0.f, 1.f, 0.f}); - model = glm::rotate(model, angles[2], vec3{0.f, 0.f, 1.f}); + if (this->loaded_as_dep || this->loaded) return; - model = glm::scale(model, scale); - - mat4x4 _ortho = glm::ortho(-width, width, height, -height, 0.f, 1.f); + for (u32 dep : this->deps) + { + if (dep == this->id) continue; + Object *object = engine->scene->get_object_by_id(dep); + if (object) + { + object->load(engine); + object->loaded_as_dep = true; + } + } - if (flip) + if (this->model) { - model = glm::scale(model, vec3{1.f, -1.f, 1.f}); + if (this->model->fullscreen) this->fullscreen = true; + if (this->model->passthrough) this->passthrough = true; } - model = _ortho * model; -} + if (this->size[0] == 0.f) this->size[0] = this->model->width; + if (this->size[1] == 0.f) this->size[1] = this->model->height; -void Object::load(Engine *engine) -{ - if (loaded_as_dep || loaded) - return; - - for (auto &dep : deps) + if (this->fullscreen) { - auto object = engine->scene->get_object_by_id(dep); - object->load(engine); - object->loaded_as_dep = true; + this->size[0] = engine->scene->width; + this->size[1] = engine->scene->height; } - - if (image != nullptr) + else if (this->passthrough) { - if (image->passthrough) - { - passthrough = true; - } + if (this->size[0] == 0.f) this->size[0] = engine->scene->width; + if (this->size[1] == 0.f) this->size[1] = engine->scene->height; + } - if (image->fullscreen) - { - fullscreen = true; - } + this->buffer_a = str_format("_rt_imageLayerComposite_%i_a", this->id); + this->buffer_b = str_format("_rt_imageLayerComposite_%i_b", this->id); + engine->create_framebuffer(this->buffer_a, this->size[0], this->size[1], 1.f); + engine->create_framebuffer(this->buffer_b, this->size[0], this->size[1], 1.f); - if (image->width != 0.f && image->height != 0.f) + if (this->effects.size() > 0) + { + for (auto it = this->effects.begin(); it != this->effects.end();) { - size[0] = image->width; - size[1] = image->height; + if (!(*it)->visible) it = this->effects.erase(it); + else it++; } } - if (size[0] == 0 && size[1] == 0) + if (this->model) this->model->load(engine, this); + + for (u32 i = 0; i < this->effects.size(); i++) { - size[0] = engine->view.width; - size[1] = engine->view.height; - origin[0] = size[0] / 2.f; - origin[1] = size[1] / 2.f; + this->effects[i]->load(engine, this, i == (this->effects.size() - 1)); } - std::stringstream _buffer_a, _buffer_b; + if (this->effects.size() != 0 && this->color_blend_mode != 0) + { + Pass blend_pass; - _buffer_a << "_rt_imageLayerComposite_" << id << "_a"; - _buffer_b << "_rt_imageLayerComposite_" << id << "_b"; + blend_pass.intermediate = false; - buffer_a = _buffer_a.str(); - buffer_b = _buffer_b.str(); + blend_pass.object = this; + blend_pass.effect = nullptr; - engine->create_framebuffer(buffer_a, size[0], size[1], 1.f); - engine->create_framebuffer(buffer_b, size[0], size[1], 1.f); + blend_pass.model = false; + blend_pass.combine = true; - get_model(engine, engine->view.width, engine->view.height, effects.size() == 0); + blend_pass.target = "previous"; + blend_pass.textures[0] = "previous"; + blend_pass.textures[1] = COMBINE_BUFFER; + blend_pass.shader = "passthroughblend"; - ortho = glm::ortho(-size[0], size[0], -size[1], size[1], 0.f, 1.f); + blend_pass.combos.push_back(Combo("BLENDMODE", this->color_blend_mode)); + blend_pass.combos.push_back(Combo("TRANSFORM", 1)); - gl_uv_object = new RenderObject(UV_SCALE, size[0], size[1]); + RenderPass *pass = new RenderPass(engine, &blend_pass); + this->effects.back()->passes.emplace_back(pass); - gl_object = new RenderObject(OBJECT, size[0], size[1]); - gl_object_flipped = new RenderObject(OBJECT_FLIPPED, size[0], size[1]); + /* + blend_pass.combine = true; - if (image != nullptr) - { - image->load(engine, this); - } + blend_pass.target = ""; + blend_pass.textures[1] = ""; + blend_pass.shader = "minimal"; - if (effects.size() != 0) - { - // Get last effect that is visible - // to know when to make a combine pass. - Effect *last_effect = nullptr; + blend_pass.combos.clear(); - for (auto &effect : effects) - { - if (effect->visible) - { - last_effect = effect; - } - } - - if (last_effect != nullptr) - { - for (auto &effect : effects) - { - effect->load(engine, this, effect == last_effect); - } - } - else if (visible) - { - // If there are effects but no visible effects, make sure - // combine is set on the background pass. - passes.back()->combine = true; - } + pass = new RenderPass(engine, &blend_pass); + //pass->mat = &engine->default_mat; + //pass->render_object = engine->default_object; + this->effects.back()->passes.emplace_back(pass); + */ } - loaded = true; + this->loaded = true; } -void Object::update(Engine *engine) +auto Object::create_objects(Engine *engine, Texture *texture) -> void { -} - -void Object::draw(Engine *engine) -{ - for (auto &dep : deps) + this->model_object = new RenderObject(MODEL, this, texture); + this->combine_object = new RenderObject(COMBINE, this, texture); + this->combine_model = glm::ortho(0.f, (f32)engine->scene->width, (f32)engine->scene->height, 0.f, -3.f, 1.f); + this->combine_model = glm::translate(this->combine_model, this->origin); + if (texture && texture->is_gif()) { - engine->scene->get_object_by_id(dep)->draw(engine); + // Something about positioning is wrong, affects most gifs. } + if (this->passthrough) + { + this->model_model = this->combine_model; + this->model_model = glm::rotate(this->model_model, this->angles[0], vec3{1.f, 0.f, 0.f}); + this->model_model = glm::rotate(this->model_model, this->angles[1], vec3{0.f, 1.f, 0.f}); + this->model_model = glm::rotate(this->model_model, this->angles[2], vec3{0.f, 0.f, 1.f}); + this->model_model = glm::scale(this->model_model, this->scale); + this->model_model = glm::translate(this->model_model, vec3{this->size[0] / -2.f, this->size[1] / -2.f, 0.f}); + } + else + { + this->model_model = glm::ortho(0.f, this->size[0], this->size[1], 0.f, -3.f, 1.f); + } + this->combine_model = glm::rotate(this->combine_model, this->angles[0], vec3{1.f, 0.f, 0.f}); + this->combine_model = glm::rotate(this->combine_model, this->angles[1], vec3{0.f, 1.f, 0.f}); + this->combine_model = glm::rotate(this->combine_model, this->angles[2], vec3{0.f, 0.f, 1.f}); + this->combine_model = glm::scale(this->combine_model, this->scale); +} +auto Object::draw_internal(Engine *engine) -> void +{ #if FRAME_STEP - std::cout << name << " (model):\n"; + std::cout << this->name << " (model):\n"; #endif - // Reset this every frame so avoid passes that target one of - // this objects buffers to get a different result every other - // frame. - buffer_switch = true; + this->buffer_switch = true; - for (auto &pass : passes) + for (RenderPass *pass : this->passes) { #if FRAME_STEP - std::cout << "\t" << pass->shader->name << "\n";; + std::cout << "\t" << pass->shader->origin()->name << "\n";; #endif pass->draw(engine); } - - for (auto &effect : effects) + + for (Effect *effect : this->effects) { effect->draw(engine); } } -std::string Object::get_target_buffer() const +auto Object::draw(Engine *engine) -> void +{ + for (s32 dep : this->deps) + { + Object *object = engine->scene->get_object_by_id(dep); + if (object) object->draw_internal(engine); + } + this->draw_internal(engine); +} + +auto Object::get_target_buffer() const -> const std::string & { - return (buffer_switch) ? buffer_a : buffer_b; + return (this->buffer_switch) ? this->buffer_a : this->buffer_b; } -std::string Object::get_texture_buffer() const +auto Object::get_texture_buffer() const -> const std::string & { - return (buffer_switch) ? buffer_b : buffer_a; + return (this->buffer_switch) ? this->buffer_b : this->buffer_a; } Object::~Object() { - if (image != nullptr) - delete image; - - delete gl_uv_object; + if (this->model) delete this->model; - delete gl_object; - delete gl_object_flipped; + if (this->model_object) delete this->model_object; + if (this->combine_object) delete this->combine_object; - for (auto &pass : passes) + for (RenderPass *pass : this->passes) { delete pass; } - for (auto &effect : effects) + for (Effect *effect : this->effects) { delete effect; } diff --git a/src/objects/object.h b/src/objects/object.h index 1a79c6c..a171fae 100644 --- a/src/objects/object.h +++ b/src/objects/object.h @@ -7,21 +7,13 @@ namespace Mauri { -enum ObjectAlignment -{ - CENTER, -}; - -enum BlendMode -{ - TRANSLUCENT = 0, - NORMAL = 1 -}; +enum ObjectAlignment { CENTER }; +enum BlendMode { TRANSLUCENT, NORMAL }; class Object { public: - Object(Parser &p, json &root); + Object(Parser &pr, const json &root); ~Object(); u32 id; @@ -29,58 +21,51 @@ class Object bool visible; - std::vector<s32> deps; + std::vector<u32> deps; - bool passthrough = false; bool fullscreen = false; + bool passthrough = false; + vec3 angles; vec2 size; - - void get_model(Engine *engine, f32 width, f32 height, bool flip); - - mat4x4 ortho; - mat4x4 model; - - RenderObject *gl_uv_object = nullptr; - RenderObject *gl_object = nullptr; - RenderObject *gl_object_flipped = nullptr; - + vec3 scale; + vec3 origin; + vec3 color; + f32 alpha; s32 color_blend_mode; - f32 alpha; - vec3 color; + vec4 color4; - std::string get_target_buffer() const; - std::string get_texture_buffer() const; + mat4x4 model_model; + mat4x4 combine_model; - void swap_buffer() - { - buffer_switch = !buffer_switch; - } + RenderObject *model_object = nullptr; + RenderObject *combine_object = nullptr; - std::vector<RenderPass *> passes; + auto get_target_buffer() const -> const std::string &; + auto get_texture_buffer() const -> const std::string &; - std::vector<Effect *> effects; + auto swap_buffers() -> void { buffer_switch = !buffer_switch; } bool loaded = false; bool loaded_as_dep = false; - void load(Engine *engine); - void update(Engine *engine); - void draw(Engine *engine); + Model *model = nullptr; + std::vector<Effect *> effects; - vec3 origin; + std::vector<RenderPass *> passes; - private: - vec3 angles; - vec3 scale; + auto load(Engine *engine) -> void; + auto create_objects(Engine *engine, Texture *texture) -> void; - std::string buffer_a; - std::string buffer_b; + auto draw(Engine *engine) -> void; + private: bool buffer_switch; + std::string buffer_a; + std::string buffer_b; - Model *image = nullptr; + auto draw_internal(Engine *engine) -> void; }; } // namespace Mauri diff --git a/src/objects/pass.cc b/src/objects/pass.cc index e6a9bfa..49533e1 100644 --- a/src/objects/pass.cc +++ b/src/objects/pass.cc @@ -1,146 +1,139 @@ #include <thread> #include "../gl.h" +#include "../context.h" +#include "scene.h" #include "pass.h" using namespace Mauri; -Pass::Pass(Parser &p, json &root, PassStage stage) +Pass::Pass(Parser &pr, const json &root, PassStage stage) { switch (stage) { - case EFFECT0_PASS: { - auto _uniforms = root["constantshadervalues"]; - for (auto &value : _uniforms.items()) - { - uniforms.emplace_back(new UniformOverride({vec4(0.f), value.key()})); - p.get_value<vec4>(_uniforms, value.key(), &uniforms.back()->value, vec4(0.f)); - } + case EFFECT0_PASS: break; - } - case EFFECT1_PASS: { - auto _material = root["material"]; - if (_material.is_string()) - { - material = new Material(p, _material); - } - - auto _command = root["command"]; - if (_command.is_string() && _command == "copy") - { - command = COPY; - } + case EFFECT1_PASS: + { + if (root.contains("material")) this->material = new Material(pr, root["material"]); - auto _target = root["target"]; - if (_target.is_string()) + if (root.contains("command")) { - target = _target; + const json command = root["command"]; + if (command == "copy") this->command = COPY; } - auto _source = root["source"]; - if (_source.is_string()) - { - source = _source; - } + if (root.contains("target")) this->target = root["target"]; + if (root.contains("source")) this->source = root["source"]; - binds[0] = "previous"; - for (auto &bind : root["bind"]) + this->binds[0] = "previous"; + if (root.contains("bind")) { - u32 index = bind["index"]; - binds[index] = bind["name"]; + for (const json &bind : root["bind"]) + { + this->binds[bind["index"]] = bind["name"]; + } } break; } case MATERIAL_ONLY_PASS: case MATERIAL_PASS: - shader = root["shader"]; + this->shader = root["shader"]; break; } - auto _textures = root["textures"]; - for (auto i = 0u; i < _textures.size(); i++) + if (root.contains("constantshadervalues")) + { + const json uniforms = root["constantshadervalues"]; + for (const auto &value : uniforms.items()) + { + UniformOverride *uniform = new UniformOverride{vec4(0.f), value.key()}; + pr.map_value<vec4>(uniforms, value.key(), &uniform->value, vec4(0.f)); + this->uniforms.push_back(uniform); + } + } + + if (root.contains("textures")) { - auto texture = _textures[i]; - if (texture.is_string()) + const json textures = root["textures"]; + for (u32 i = 0; i < textures.size(); i++) { - textures[i] = texture; + if (textures[i].is_string()) this->textures[i] = textures[i]; } } - for (auto &combo : root["combos"].items()) + if (root.contains("combos")) { - combos.emplace_back(Combo { combo.key(), combo.value() }); + for (const auto &combo : root["combos"].items()) + { + this->combos.emplace_back(Combo(combo.key(), combo.value())); + } } } -void Pass::load(Engine *engine, PassStage stage, Pass *pass) +auto Pass::load(Engine *engine, PassStage stage, Pass *pass) -> void { - intermidiate = true; - - for (auto i = 0; i < MAX_TEXTURES; i++) - { - if (textures[i].empty()) - continue; + this->intermediate = true; - pass->textures[i] = textures[i]; - } + pass->uniforms.insert(pass->uniforms.end(), this->uniforms.begin(), this->uniforms.end()); - for (auto &combo : combos) + for (u32 i = 0; i < MAX_TEXTURES; i++) { - pass->combos.push_back(combo); + if (!this->textures[i].empty()) + { + pass->textures[i] = this->textures[i]; + } } + pass->combos.insert(pass->combos.end(), this->combos.begin(), this->combos.end()); + switch (stage) { case EFFECT0_PASS: - for (auto &uniform : uniforms) - { - pass->uniforms.push_back(uniform); - } break; case EFFECT1_PASS: - pass->command = command; + { + pass->command = this->command; - pass->source = source + pass->effect->buffer_id; - pass->target = target; + pass->target = this->target; + pass->source = this->source + pass->effect->buffer_id; if (!pass->target.empty()) + { pass->target.append(pass->effect->buffer_id); + } - for (auto i = 0; i < MAX_TEXTURES; i++) + for (u32 i = 0; i < MAX_TEXTURES; i++) { - if (binds[i].empty()) - continue; + if (this->binds[i].empty()) continue; + + pass->textures[i] = this->binds[i]; - pass->textures[i] = binds[i]; - if (pass->textures[i] != "previous") - pass->textures[i] += pass->effect->buffer_id; + { + pass->textures[i].append(pass->effect->buffer_id); + } } - if (material != nullptr) - { - material->load(engine, pass->object, MATERIAL_EFFECT, pass); - } + if (this->material) this->material->load(engine, pass->object, MATERIAL_EFFECT, pass); break; + } case MATERIAL_ONLY_PASS: case MATERIAL_PASS: - pass->shader = shader; + pass->shader = this->shader; break; } } Pass::~Pass() { - if (material != nullptr) - delete material; - - if (intermidiate) + if (this->material) delete this->material; + if (this->intermediate) { - for (auto &uniform : uniforms) + for (UniformOverride *uniform : this->uniforms) { delete uniform; } @@ -149,235 +142,220 @@ Pass::~Pass() RenderPass::RenderPass(Engine *engine, Pass *pass) { - command = pass->command; - combine = pass->combine; + this->command = pass->command; - // This object contains information needed for getting previous - // framebuffer textures and targets. - object = pass->object; + this->target = engine->get_framebuffer(pass->target); - target = engine->get_framebuffer(pass->target); - - if (command == COPY) + if (this->command == COPY) { - // Target and source is the only needed information - // for a COPY pass. - source = engine->get_framebuffer(pass->source); + // Target and source is the only needed information for a COPY pass. + this->source = engine->get_framebuffer(pass->source); return; } - shader = new Shader(engine->parser, pass->shader); + this->object = pass->object; + + this->model = pass->model; + this->combine = pass->combine; + + this->shader = get_shader(pass->shader); - for (auto i = 0u; i < shader->texture_count; i++) + bool objects_created = false; + + for (u32 i = 0; i < this->shader->origin()->texture_count; i++) { - if (!shader->textures[i].empty()) + if (!this->shader->origin()->texture_combos[i].empty()) { - // If there is no texture at "i" use default shader from the texture. - //if (pass->textures[i].empty()) - // pass->textures[i] = shader->textures[i]; + pass->combos.push_back(Combo(this->shader->origin()->texture_combos[i], !pass->textures[i].empty())); + } - if (!shader->texture_combos[i].empty() && !pass->textures[i].empty()) - pass->combos.push_back(Combo { shader->texture_combos[i], 1 }); + if (!this->shader->origin()->textures[i].empty() && pass->textures[i].empty()) + { + pass->textures[i] = this->shader->origin()->textures[i]; } - if (pass->textures[i].empty()) - continue; + if (pass->textures[i].empty()) continue; if (pass->textures[i] == "previous") { - texture_types[i] = PREVIOUS; + this->texture_types[i] = PREVIOUS; continue; } - // Check if texture is referencing a framebuffer. - textures[i] = engine->get_framebuffer_texture(pass->textures[i]); - if (textures[i] == nullptr) + this->textures[i] = engine->get_framebuffer_texture(pass->textures[i]); + + if (!this->textures[i]) { - // The texture wasn't a framebuffer so try to load it from an asset. - auto asset = asset_manager()->get_file(pass->textures[i], TEXTURE); + Asset *asset = asset_manager()->get_file(pass->textures[i], TEXTURE); if (asset->type != ASSET_VOID) { - textures[i] = (Texture *)asset->lasset; + this->textures[i] = (Texture *)asset->lasset; } } + + if (i == 0 && this->textures[0] && this->model) + { + if (this->textures[0]->is_gif()) pass->combos.push_back(Combo("SPRITESHEET", 1)); + this->object->create_objects(engine, this->textures[0]); + objects_created = true; + } } - for (auto &shader_uniform : shader->uniforms) + if (this->model && !objects_created) { - for (auto &uniform_override : pass->uniforms) + this->object->create_objects(engine, nullptr); + } + + for (Uniform *uniform : this->shader->uniforms) + { + for (UniformOverride *uniform_override : pass->uniforms) { - if (shader_uniform->name.empty()) - continue; - if (shader_uniform->name == uniform_override->name) + if (uniform->name.empty()) continue; + if (uniform->name == uniform_override->name) { // Take value as reference because "uniform_override->value" points // to the value stored in the parser. This allows changing that value // to have an instant effect. - shader_uniform->value = &uniform_override->value; + uniform->value = &uniform_override->value; break; } } } - for (auto &pass_combo : pass->combos) + this->render_shader = this->shader->compile(pass->combos); + + if (this->model && this->object->fullscreen) { - auto add = true; - for (auto &shader_combo : shader->combos) - { - if (pass_combo.name == shader_combo.name) - { - shader_combo.value = pass_combo.value; - add = false; - break; - } - } - if (add) - shader->combos.push_back(pass_combo); + this->mat = &engine->default_mat; + this->render_object = engine->fullscreen_object; } - - // If visualizer is not enabled disable audio logic - // on the shader. - if (!engine->audio_enabled) + else if (this->object->passthrough) { - for (auto &combo : shader->combos) + if (this->combine) { - if (combo.name == "AUDIOPROCESSING") - combo.value = 0; + this->mat = &this->object->model_model; + this->render_object = this->object->model_object; + } + else if (this->model) + { + this->mat = &this->object->combine_model; + this->render_object = this->object->combine_object; + } + else + { + this->mat = &engine->default_mat; + this->render_object = engine->default_object; } } - - // Compile shader only after all necessary combos are set. - shader->compile(); - - if (pass->effect == nullptr) + else { - // The fullscreen shader doesn't use ModelViewProjectionMatrix, - if (pass->object->fullscreen) + if (this->combine) { - model = &engine->default_model; - robject = engine->default_object; + this->mat = &this->object->combine_model; + this->render_object = this->object->combine_object; } - - // The passthrough shader uses ModelViewProjectionMatrix - // but, the texture is not scaled to a power of two. - else if (pass->object->passthrough) + else if (this->model) { - model = &pass->object->model; - robject = pass->object->gl_object; - } - // If there is no effect and combine is set, - // we are forced to use a model which will - // correctly position the object. - else if (combine) + this->mat = &this->object->model_model; + this->render_object = this->object->model_object; + } + else { - model = &pass->object->model; - if (textures[0]->is_gif) - { - robject = pass->object->gl_object_flipped; - } - else - { - robject = pass->object->gl_uv_object; - } + this->mat = &engine->default_mat; + this->render_object = engine->default_object; } + } - // The object with UV's that scale the texture - // also has the objects size on the verteices, - // so we need to use ortho for the model. - else +#if _DEBUG_ + debug("PASS (%s)", this->command == COPY ? "copy" : "draw"); + debug(" object: %s (passthrough: %i, fullscreen %i)", this->object->name.c_str(), + this->object->passthrough, this->object->fullscreen); + //debug(" visible: %i", ); + debug(" shader: %s", this->shader->origin()->name.c_str()); + debug(" target: %s", pass->target.c_str()); + debug(" textures (%i):", this->shader->origin()->texture_count); + for (u32 i = 0; i < this->shader->origin()->texture_count; i++) + { + switch (this->texture_types[i]) { - model = &pass->object->ortho; - if (textures[0]->is_gif) + case STATIC: + if (pass->textures[i].empty() && !this->shader->textures[i].empty()) { - robject = pass->object->gl_object_flipped; + debug(" %s (DEFAULT)", this->shader->textures[i].c_str()); } else { - robject = pass->object->gl_uv_object; + debug(" %s", pass->textures[i].c_str()); } + break; + case PREVIOUS: + debug(" previous"); + break; } } - else + debug(" uniforms (%li)", this->shader->uniforms.size()); + for (Uniform *uniform : this->shader->uniforms) { - // When combine is set this is the final pass - // that needs to position the object on the screen. - // Except when fullscreen is set the object doesn't - // want to be positioned. - if (combine && !pass->object->fullscreen) - { - robject = pass->object->gl_object; - model = &pass->object->model; - } - else // Default everything. - { - robject = engine->default_object; - model = &engine->default_model; - } + debug(" %s %s %s", uniform_type_to_string(uniform->type).c_str(), + uniform->uname.c_str(), glm::to_string(*uniform->value).c_str()); } + debug(" model: %i", this->model); + debug(" combine: %i", this->combine); +#endif } -void RenderPass::draw(Engine *engine) +auto RenderPass::draw(Engine *engine) -> void { - switch (command) { - case DRAW: { - Framebuffer *_target; - - if (combine) - _target = engine->combine_buffer; - else if (target != nullptr) - _target = target; - else - _target = engine->get_framebuffer(object->get_target_buffer()); + switch (this->command) + { + case DRAW: + { + Framebuffer *adjusted_target; + if (this->combine) adjusted_target = engine->combine_buffer; + else if (this->target) adjusted_target = this->target; + else adjusted_target = engine->get_framebuffer(this->object->get_target_buffer()); - _target->buffer->bind(); + adjusted_target->buffer->bind(); - for (auto i = 0u; i < shader->texture_count; i++) + for (u32 i = 0; i < this->shader->origin()->texture_count; i++) { - if (texture_types[i] == PREVIOUS) + if (this->texture_types[i] == PREVIOUS) { - textures[i] = engine->get_framebuffer_texture(object->get_texture_buffer()); + this->textures[i] = engine->get_framebuffer_texture(this->object->get_texture_buffer()); } } - shader->bind(engine, object, model, &textures); - robject->bind(); + this->render_object->bind(); + this->shader->bind(this->render_shader, engine, this->object, this->mat, &this->textures); - if (target == nullptr) - object->swap_buffer(); + if (!this->target) this->object->swap_buffers(); - if (combine) - { - Render::blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - Render::color_mask(1.f, 1.f, 1.f, 0.f); - } - else - { - Render::blend_func(GL_ONE, GL_ZERO); - Render::color_mask(1.f, 1.f, 1.f, 1.f); - } + if (this->combine) Render::blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + else Render::blend_func(GL_ONE, GL_ZERO); + + Render::color_mask(1.f, 1.f, 1.f, this->combine ? 0.f : 1.f); - engine->view.default_viewport(_target->buffer->width, _target->buffer->height); + engine->view.default_viewport( + adjusted_target->buffer->width, adjusted_target->buffer->height); - robject->draw(); + this->render_object->draw(); #if FRAME_STEP - _target->buffer->blit(0, context()->width, context()->height); + adjusted_target->buffer->blit(0, context()->width, context()->height); context()->swap_buffers(); std::this_thread::sleep_for(std::chrono::seconds(1)); #endif - break; } case COPY: - source->buffer->blit(target->buffer->id, source->buffer->width, source->buffer->height); + this->source->buffer->blit(this->target->buffer->id, this->source->buffer->width, this->source->buffer->height); break; } } RenderPass::~RenderPass() { - if (shader != nullptr) - delete shader; + if (this->shader) delete this->shader; } diff --git a/src/objects/pass.h b/src/objects/pass.h index 97348a3..a2b3124 100644 --- a/src/objects/pass.h +++ b/src/objects/pass.h @@ -8,25 +8,10 @@ namespace Mauri { -enum PassStage -{ - EFFECT0_PASS, - EFFECT1_PASS, - MATERIAL_PASS, - MATERIAL_ONLY_PASS -}; - -enum PassCommand -{ - DRAW, - COPY -}; +enum PassStage { EFFECT0_PASS, EFFECT1_PASS, MATERIAL_PASS, MATERIAL_ONLY_PASS }; +enum PassCommand { DRAW, COPY }; -enum TextureType -{ - STATIC, - PREVIOUS -}; +enum TextureType { STATIC, PREVIOUS }; struct UniformOverride { @@ -34,18 +19,17 @@ struct UniformOverride std::string name; }; -class RenderPass; - class Pass { public: Pass() {} - Pass(Parser &p, json &root, PassStage stage); - + Pass(Parser &pr, const json &root, PassStage stage); ~Pass(); + bool intermediate; + PassCommand command = DRAW; - + std::string target; std::string source; @@ -54,16 +38,16 @@ class Pass std::string shader; - std::array<std::string, MAX_TEXTURES> textures = {""}; - std::array<std::string, MAX_TEXTURES> binds = {""}; + std::array<std::string, MAX_TEXTURES> binds = { }; + std::array<std::string, MAX_TEXTURES> textures = { }; + bool model; bool combine; - bool intermidiate; std::vector<UniformOverride *> uniforms; std::vector<Combo> combos; - void load(Engine *engine, PassStage stage, Pass *pass); + auto load(Engine *engine, PassStage stage, Pass *pass) -> void; private: Material *material = nullptr; @@ -75,14 +59,18 @@ class RenderPass RenderPass(Engine *engine, Pass *pass); ~RenderPass(); + bool model; bool combine; Shader *shader = nullptr; - std::array<Texture *, MAX_TEXTURES> textures = {0}; - std::array<TextureType, MAX_TEXTURES> texture_types = {STATIC}; + mat4x4 *mat; + RenderObject *render_object; + + std::array<Texture *, MAX_TEXTURES> textures = { }; + std::array<TextureType, MAX_TEXTURES> texture_types = { }; - void draw(Engine *engine); + auto draw(Engine *engine) -> void; private: PassCommand command; @@ -92,8 +80,7 @@ class RenderPass Framebuffer *target; Framebuffer *source; - mat4x4 *model; - RenderObject *robject; + RenderShader *render_shader; }; } // namespace Mauri diff --git a/src/objects/scene.cc b/src/objects/scene.cc index 8331b15..06412f5 100644 --- a/src/objects/scene.cc +++ b/src/objects/scene.cc @@ -2,77 +2,78 @@ using namespace Mauri; -Scene::Scene(Parser &p, const std::string &path) +Scene::Scene(const std::string &path) { - auto asset = asset_manager()->get_file(path, NONE); - if (asset->type == ASSET_VOID) - { - log_error("%s", "failed to open scene.json"); - return; - } - - auto root = json::parse(asset->as_string()); + Asset *asset = asset_manager()->get_file(path); + if (asset->type == ASSET_VOID) return; + + const json &root = json::parse(asset->as_string()); + if (root.is_discarded()) return; - auto camera = root["camera"]; - p.get_value<vec3>(camera, "center", &cam.center, vec3(0.f)); - p.get_value<vec3>(camera, "eye", &cam.eye, vec3(0.f)); - p.get_value<vec3>(camera, "up", &cam.up, vec3(0.f)); + Parser &pr = this->parser; - auto general = root["general"]; - p.get_value<bool>(general, "clearenabled", &clear_enabled, true); - p.get_value<vec4>(general, "clearcolor", &clear_color, vec4(1.f)); - p.get_value<f32>(general, "nearz", &nearz, 0.f); - p.get_value<f32>(general, "farz", &farz, 1.f); + const json &camera = root["camera"]; + pr.map_value<vec3>(camera, "center", &this->cam.center, vec3(0.f)); + pr.map_value<vec3>(camera, "eye", &this->cam.eye, vec3(0.f)); + pr.map_value<vec3>(camera, "up", &this->cam.up, vec3(0.f)); - auto ortho = general["orthogonalprojection"]; - p.get_value<f32>(ortho, "width", &width, 0.f); - p.get_value<f32>(ortho, "height", &height, 0.f); + const json &general = root["general"]; + pr.map_value<bool>(general, "clearenabled", &this->clear_enabled, true); + pr.map_value<vec4>(general, "clearcolor", &this->clear_color, vec4(1.f)); + pr.map_value<f32>(general, "nearz", &this->nearz, -1.f); + pr.map_value<f32>(general, "farz", &this->farz, 1.f); - for (auto &object : root["objects"]) + const json &ortho = general["orthogonalprojection"]; + pr.map_value<s32>(ortho, "width", &this->width, 0); + pr.map_value<s32>(ortho, "height", &this->height, 0); + + for (const json &object : root["objects"]) { - if (object["image"].is_null()) - continue; - objects.emplace_back(new Object(p, object)); + if (!object.contains("image") || object["image"].is_null()) continue; + //if (object["name"] == "ICUE") continue; + this->objects.emplace_back(new Object(pr, object)); } } -Object *Scene::get_object_by_id(u32 id) +auto Scene::get_object_by_id(u32 id) -> Object * { - for (auto &object : objects) + for (Object *object : this->objects) { - if (object->id == id) - { - return object; - } + if (object->id == id) return object; } - return nullptr; } -void Scene::load(Engine *engine) +auto Scene::load(Engine *engine) -> void { - engine->view.width = width; - engine->view.height = height; + /* + if (this->objects.size() > 0) + { + for (auto it = this->objects.begin(); it != this->objects.end();) + { + if (!(*it)->visible) it = this->objects.erase(it); + else it++; + } + } + */ - for (auto &object : objects) + for (Object *object : this->objects) { object->load(engine); } } -void Scene::draw(Engine *engine) +auto Scene::draw(Engine *engine) -> void { - for (auto &object : objects) + for (Object *object : this->objects) { - if (object->loaded_as_dep) - continue; - object->draw(engine); + if (!object->loaded_as_dep) object->draw(engine); } } Scene::~Scene() { - for (auto &object : objects) + for (Object *object : this->objects) { delete object; } diff --git a/src/objects/scene.h b/src/objects/scene.h index 672df96..7b3d07a 100644 --- a/src/objects/scene.h +++ b/src/objects/scene.h @@ -6,25 +6,25 @@ namespace Mauri { -struct Camera { +struct Camera +{ vec3 center; vec3 eye; vec3 up; - f32 farz; - f32 nearz; - f32 fov; }; class Scene { public: - Scene(Parser &p, const std::string &path); + Scene(const std::string &path); ~Scene(); + Parser parser; + Camera cam; - f32 width; - f32 height; + s32 width; + s32 height; f32 nearz; f32 farz; @@ -32,10 +32,10 @@ class Scene bool clear_enabled; vec4 clear_color; - void load(Engine *engine); - void draw(Engine *engine); + auto get_object_by_id(u32 id) -> Object *; - Object *get_object_by_id(u32 id); + auto load(Engine *engine) -> void; + auto draw(Engine *engine) -> void; std::vector<Object *> objects; }; |