#include "../gl.h" //#define AL_LOG_ENABLE_TRACE #include "../log.h" #include "../context.h" #include "pass.h" using namespace Mauri; auto Pass::parse(Parser &pr, const json &root, PassStage stage) -> Pass * { Pass *pass = new Pass(); switch (stage) { case EFFECT0_PASS: break; case EFFECT1_PASS: { if (root.contains("material")) pass->material = Material::parse(pr, root["material"]); if (!pass->material) parse_fail(pass); if (root.contains("command")) { const json command = root["command"]; if (command == "copy") pass->command = COPY; } if (root.contains("target")) pass->target = root["target"]; if (root.contains("source")) pass->source = root["source"]; pass->binds[0] = "previous"; if (root.contains("bind")) { for (const json &bind : root["bind"]) { pass->binds[bind["index"]] = bind["name"]; } } break; } case MATERIAL_ONLY_PASS: case MATERIAL_PASS: pass->shader = root["shader"]; if (pass->shader == "genericimage4") { pass->shader = "genericimage2"; } pass->depthtest = root["depthtest"] != "disabled"; pass->depthwrite = root["depthwrite"] != "disabled"; break; } 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(uniforms, value.key(), &uniform->value, vec4(0.f)); pass->uniforms.push_back(uniform); } } if (root.contains("textures")) { const json textures = root["textures"]; for (u32 i = 0; i < textures.size(); i++) { if (textures[i].is_string()) pass->textures[i] = textures[i]; } } if (root.contains("combos")) { for (const auto &combo : root["combos"].items()) { pass->combos.emplace_back(Combo(combo.key(), combo.value())); } } return pass; } auto Pass::load(Engine *engine, PassStage stage, Pass *pass) -> void { this->intermediate = true; pass->uniforms.insert(pass->uniforms.end(), this->uniforms.begin(), this->uniforms.end()); for (u32 i = 0; i < MAX_TEXTURES; i++) { 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: break; case EFFECT1_PASS: { pass->command = this->command; pass->target = this->target; pass->source = this->source + pass->effect->buffer_id; if (!pass->target.empty()) { pass->target.append(pass->effect->buffer_id); } for (u32 i = 0; i < MAX_TEXTURES; i++) { if (this->binds[i].empty()) continue; pass->textures[i] = this->binds[i]; if (pass->textures[i] != "previous") { pass->textures[i].append(pass->effect->buffer_id); } } if (this->material) this->material->load(engine, pass->object, MATERIAL_EFFECT, pass); break; } case MATERIAL_ONLY_PASS: case MATERIAL_PASS: pass->shader = this->shader; pass->depthtest = this->depthtest; pass->depthwrite = this->depthwrite; break; } } Pass::~Pass() { if (this->material) delete this->material; if (this->intermediate) { for (UniformOverride *uniform : this->uniforms) { delete uniform; } } } RenderPass::RenderPass(Engine *engine, Pass *pass) { this->command = pass->command; this->target = engine->get_framebuffer(pass->target); if (this->command == COPY) { // Target and source is the only needed information for a COPY pass. this->source = engine->get_framebuffer(pass->source); return; } this->object = pass->object; this->model = pass->model; this->combine = pass->combine; this->depth = pass->depthtest; if (pass->depthtest || pass->depthwrite) { warn("depth test support incomplete"); } this->shader = get_shader(pass->shader); bool objects_created = false; for (u32 i = 0; i < this->shader->origin()->texture_count; i++) { if (!this->shader->origin()->texture_combos[i].empty()) { pass->combos.push_back(Combo(this->shader->origin()->texture_combos[i], !pass->textures[i].empty())); } if (pass->textures[i].empty() && !this->shader->origin()->textures[i].empty()) { pass->textures[i] = this->shader->origin()->textures[i]; } if (pass->textures[i].empty()) continue; if (pass->textures[i] == "previous") { this->texture_types[i] = PREVIOUS; continue; } this->textures[i] = engine->get_framebuffer_texture(pass->textures[i]); if (!this->textures[i]) { Asset *asset = asset_manager()->get(pass->textures[i], TEXTURE); if (asset->type != ASSET_VOID) { this->textures[i] = (Texture *)asset->lasset; } } if (this->model && i == 0 && this->textures[0]) { this->object->create_objects(engine, this->textures[0]); objects_created = true; } } if (this->model && !objects_created) { this->object->create_objects(engine, nullptr); } for (Uniform *uniform : this->shader->uniforms) { if (uniform->uname == "g_Color4") { uniform->value = &this->object->color4; } for (UniformOverride *uniform_override : pass->uniforms) { 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 immediate effect. uniform->value = &uniform_override->value; break; } } } this->render_shader = this->shader->compile(pass->combos); if (this->model && this->object->fullscreen) { this->mat = &engine->default_mat; this->render_object = engine->fullscreen_object; } else if (this->object->passthrough) { if (this->combine) { 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; } } else { if (this->combine) { this->mat = &this->object->combine_model; this->render_object = this->object->combine_object; } else if (this->model) { this->mat = &this->object->model_model; this->render_object = this->object->model_object; } else { this->mat = &engine->default_mat; this->render_object = engine->default_object; } } trace("PASS (%s)", this->command == COPY ? "copy" : "draw"); trace(" object: %s (passthrough: %s, fullscreen %s)", this->object->name.c_str(), BOOLSTR(this->object->passthrough), BOOLSTR(this->object->fullscreen)); trace(" visible: %s", BOOLSTR(this->object->visible)); trace(" shader: %s", this->shader->origin()->name.c_str()); trace(" target: %s", pass->target.c_str()); trace(" textures (%u):", this->shader->origin()->texture_count); for (u32 i = 0; i < this->shader->origin()->texture_count; i++) { switch (this->texture_types[i]) { case STATIC: if (!this->shader->origin()->textures[i].empty() && pass->textures[i] == this->shader->origin()->textures[i]) { trace(" [%u] %s (DEFAULT)", i, this->shader->origin()->textures[i].c_str()); } else { trace(" [%u] %s", i, pass->textures[i].c_str()); } break; case PREVIOUS: trace(" [%u] previous", i); break; } } trace(" uniforms (%u)", this->shader->uniforms.size()); for (Uniform *uniform : this->shader->uniforms) { trace(" %s %s %s", uniform_type_to_string(uniform->type).c_str(), uniform->uname.c_str(), glm::to_string(*uniform->value).c_str()); } trace(" model: %s", BOOLSTR(this->model)); trace(" combine: %s", BOOLSTR(this->combine)); } auto RenderPass::draw(Engine *engine) -> void { 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()); adjusted_target->buffer->bind(); for (u32 i = 0; i < this->shader->origin()->texture_count; i++) { if (this->texture_types[i] == PREVIOUS) { this->textures[i] = engine->get_framebuffer_texture(this->object->get_texture_buffer()); } } this->render_object->bind(); this->shader->bind(this->render_shader, engine, this->object, this->mat, &this->textures); if (!this->target) this->object->swap_buffers(); if (this->depth) Render::depth_test(true); 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); Render::color_mask(1.f, 1.f, 1.f, 1.f); engine->view.default_viewport(adjusted_target->buffer->width, adjusted_target->buffer->height); this->render_object->draw(); if (this->depth) Render::depth_test(false); #ifdef FRAME_STEP adjusted_target->buffer->blit(0, context()->width(), context()->height()); context()->swap_buffers(); nn_thread_sleep(NNWT_TS_FROM_USEC(1000000)); #endif break; } case COPY: this->source->buffer->blit(this->target->buffer->id, this->source->buffer->width, this->source->buffer->height); break; } } RenderPass::~RenderPass() { if (this->shader) delete this->shader; }