From 1e7328c1aa2f470ff42274f2dcd4d4c6cd35d802 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Wed, 20 Oct 2021 18:38:49 +0800 Subject: [PATCH] Separate render context into a render context and render queue. Change signature of render pass render functions. Pass time variables to render context. Add a visitor pattern render function to scene objects. Add a refresh function for text objects. Remove obsolete time tweens. --- src/entity/systems/render.cpp | 7 +- src/entity/systems/render.hpp | 2 + src/game/bootloader.cpp | 15 --- src/game/context.hpp | 1 - src/renderer/compositor.cpp | 4 +- src/renderer/compositor.hpp | 5 +- .../{render-context.hpp => context.hpp} | 40 +++++-- .../{render-operation.hpp => operation.hpp} | 9 +- src/renderer/passes/bloom-pass.cpp | 4 +- src/renderer/passes/bloom-pass.hpp | 2 +- src/renderer/passes/clear-pass.cpp | 2 +- src/renderer/passes/clear-pass.hpp | 2 +- src/renderer/passes/final-pass.cpp | 15 +-- src/renderer/passes/final-pass.hpp | 5 +- src/renderer/passes/material-pass.cpp | 67 ++++++------ src/renderer/passes/material-pass.hpp | 6 +- src/renderer/passes/outline-pass.cpp | 12 +-- src/renderer/passes/outline-pass.hpp | 2 +- src/renderer/passes/shadow-map-pass.cpp | 24 ++--- src/renderer/passes/shadow-map-pass.hpp | 2 +- src/renderer/passes/sky-pass.cpp | 47 ++++---- src/renderer/passes/sky-pass.hpp | 4 +- src/renderer/passes/ui-pass.cpp | 18 ++-- src/renderer/passes/ui-pass.hpp | 5 +- src/renderer/queue.hpp | 33 ++++++ src/renderer/render-pass.hpp | 6 +- src/renderer/renderer.cpp | 100 +++++++++--------- src/renderer/renderer.hpp | 23 ++-- src/renderer/shader-template.hpp | 2 +- src/renderer/simple-render-pass.cpp | 19 +--- src/renderer/simple-render-pass.hpp | 6 +- src/scene/object.cpp | 3 + src/scene/object.hpp | 13 +++ src/scene/text.cpp | 29 +++++ src/scene/text.hpp | 33 ++---- 35 files changed, 299 insertions(+), 268 deletions(-) rename src/renderer/{render-context.hpp => context.hpp} (72%) rename src/renderer/{render-operation.hpp => operation.hpp} (92%) create mode 100644 src/renderer/queue.hpp diff --git a/src/entity/systems/render.cpp b/src/entity/systems/render.cpp index 80679d4..05ed2d0 100644 --- a/src/entity/systems/render.cpp +++ b/src/entity/systems/render.cpp @@ -32,6 +32,8 @@ namespace system { render::render(entity::registry& registry): updatable(registry), + t(0.0), + dt(0.0), renderer(nullptr) { registry.on_construct().connect<&render::on_model_construct>(this); @@ -45,6 +47,9 @@ render::render(entity::registry& registry): void render::update(double t, double dt) { + this->t = t; + this->dt = dt; + // Update model instance transforms registry.view().each ( @@ -101,7 +106,7 @@ void render::draw(double alpha) { for (const scene::collection* collection: layers) { - renderer->render(alpha, *collection); + renderer->render(static_cast(t + dt * alpha), static_cast(dt), static_cast(alpha), *collection); } } } diff --git a/src/entity/systems/render.hpp b/src/entity/systems/render.hpp index 306828e..eb4a352 100644 --- a/src/entity/systems/render.hpp +++ b/src/entity/systems/render.hpp @@ -64,6 +64,8 @@ private: void on_light_replace(entity::registry& registry, entity::id entity_id, entity::component::light& light); void on_light_destroy(entity::registry& registry, entity::id entity_id); + double t; + double dt; renderer* renderer; std::vector layers; std::unordered_map model_instances; diff --git a/src/game/bootloader.cpp b/src/game/bootloader.cpp index 4e9c8e1..ba10f52 100644 --- a/src/game/bootloader.cpp +++ b/src/game/bootloader.cpp @@ -766,10 +766,6 @@ void setup_animation(game::context* ctx) // Setup animator ctx->animator = new animator(); - // Initialize time tween - ctx->time_tween = new tween(0.0); - ctx->time_tween->set_interpolator(math::lerp); - // Create fade transition ctx->fade_transition = new screen_transition(); ctx->fade_transition->get_material()->set_shader_program(ctx->resource_manager->load("fade-transition.glsl")); @@ -799,13 +795,6 @@ void setup_animation(game::context* ctx) channel->insert_keyframe({0.0f, 1.0f}); channel->insert_keyframe({duration, 0.0f}); } - - // Set material pass tweens - ctx->common_final_pass->set_time_tween(ctx->time_tween); - ctx->surface_sky_pass->set_time_tween(ctx->time_tween); - ctx->surface_material_pass->set_time_tween(ctx->time_tween); - ctx->underground_material_pass->set_time_tween(ctx->time_tween); - ctx->ui_material_pass->set_time_tween(ctx->time_tween); } void setup_entities(game::context* ctx) @@ -969,15 +958,11 @@ void setup_callbacks(game::context* ctx) ); // Update tweens - ctx->time_tween->update(); ctx->surface_sky_pass->update_tweens(); ctx->surface_scene->update_tweens(); ctx->underground_scene->update_tweens(); ctx->ui_scene->update_tweens(); - // Set time tween time - (*ctx->time_tween)[1] = t; - ctx->timeline->advance(dt); diff --git a/src/game/context.hpp b/src/game/context.hpp index 288a3e7..8f68d5f 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -202,7 +202,6 @@ struct context // Animation timeline* timeline; animator* animator; - tween* time_tween; animation* radial_transition_in; animation* radial_transition_out; screen_transition* fade_transition; diff --git a/src/renderer/compositor.cpp b/src/renderer/compositor.cpp index 38aabce..87f98bf 100644 --- a/src/renderer/compositor.cpp +++ b/src/renderer/compositor.cpp @@ -35,13 +35,13 @@ void compositor::remove_passes() passes.clear(); } -void compositor::composite(render_context* context) const +void compositor::composite(const render::context& ctx, render::queue& queue) const { for (const render_pass* pass: passes) { if (pass->is_enabled()) { - pass->render(context); + pass->render(ctx, queue); } } } diff --git a/src/renderer/compositor.hpp b/src/renderer/compositor.hpp index eed4249..da175e8 100644 --- a/src/renderer/compositor.hpp +++ b/src/renderer/compositor.hpp @@ -20,10 +20,11 @@ #ifndef ANTKEEPER_COMPOSITOR_HPP #define ANTKEEPER_COMPOSITOR_HPP +#include "renderer/context.hpp" +#include "renderer/queue.hpp" #include class render_pass; -struct render_context; /** * @@ -35,7 +36,7 @@ public: void remove_pass(render_pass* pass); void remove_passes(); - void composite(render_context* context) const; + void composite(const render::context& ctx, render::queue& queue) const; const std::list* get_passes() const; diff --git a/src/renderer/render-context.hpp b/src/renderer/context.hpp similarity index 72% rename from src/renderer/render-context.hpp rename to src/renderer/context.hpp index 3baacba..120eec1 100644 --- a/src/renderer/render-context.hpp +++ b/src/renderer/context.hpp @@ -20,27 +20,55 @@ #ifndef ANTKEEPER_RENDER_CONTEXT_HPP #define ANTKEEPER_RENDER_CONTEXT_HPP -#include "renderer/render-operation.hpp" #include "geom/plane.hpp" #include "geom/bounding-volume.hpp" #include "utility/fundamental-types.hpp" -#include "scene/camera.hpp" -#include "scene/collection.hpp" #include -struct render_context +namespace scene { + class camera; + class collection; +} + +namespace render { + +/** + * Context of a renderer. + */ +struct context +{ + /// Pointer to the camera. const scene::camera* camera; + + /// Camera transform. math::transform camera_transform; + + /// Camera forward vector float3 camera_forward; + + /// Camera up vector. float3 camera_up; + + /// Camera culling volume. const geom::bounding_volume* camera_culling_volume; + + /// Near clipping plane of the camera geom::plane clip_near; + /// Collection of scene objects being rendered. const scene::collection* collection; - std::list operations; + + /// Current time, in seconds. + float t; + + /// Timestep, in seconds. + float dt; + + /// Subframe interpolation factor. float alpha; }; -#endif // ANTKEEPER_RENDER_CONTEXT_HPP +} // namespace render +#endif // ANTKEEPER_RENDER_CONTEXT_HPP diff --git a/src/renderer/render-operation.hpp b/src/renderer/operation.hpp similarity index 92% rename from src/renderer/render-operation.hpp rename to src/renderer/operation.hpp index 3f04032..6ed5c75 100644 --- a/src/renderer/render-operation.hpp +++ b/src/renderer/operation.hpp @@ -28,10 +28,12 @@ class pose; class material; +namespace render { + /** - * Describes a render operation with a single mesh and single material. + * Encapsulates an atomic render operation. */ -struct render_operation +struct operation { const pose* pose; const material* material; @@ -44,5 +46,6 @@ struct render_operation std::size_t instance_count; }; -#endif // ANTKEEPER_RENDER_OPERATION_HPP +} // namespace render +#endif // ANTKEEPER_RENDER_OPERATION_HPP diff --git a/src/renderer/passes/bloom-pass.cpp b/src/renderer/passes/bloom-pass.cpp index 484eb46..fa65847 100644 --- a/src/renderer/passes/bloom-pass.cpp +++ b/src/renderer/passes/bloom-pass.cpp @@ -31,7 +31,7 @@ #include "gl/texture-wrapping.hpp" #include "gl/texture-filter.hpp" #include "renderer/vertex-attribute.hpp" -#include "renderer/render-context.hpp" +#include "renderer/context.hpp" #include "math/math.hpp" #include #include @@ -114,7 +114,7 @@ bloom_pass::~bloom_pass() delete quad_vbo; } -void bloom_pass::render(render_context* context) const +void bloom_pass::render(const render::context& ctx, render::queue& queue) const { glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); diff --git a/src/renderer/passes/bloom-pass.hpp b/src/renderer/passes/bloom-pass.hpp index f6b68c7..60963d3 100644 --- a/src/renderer/passes/bloom-pass.hpp +++ b/src/renderer/passes/bloom-pass.hpp @@ -38,7 +38,7 @@ class bloom_pass: public render_pass public: bloom_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); virtual ~bloom_pass(); - virtual void render(render_context* context) const final; + virtual void render(const render::context& ctx, render::queue& queue) const final; void set_source_texture(const gl::texture_2d* texture); void set_brightness_threshold(float threshold); diff --git a/src/renderer/passes/clear-pass.cpp b/src/renderer/passes/clear-pass.cpp index 563a8ce..844bf69 100644 --- a/src/renderer/passes/clear-pass.cpp +++ b/src/renderer/passes/clear-pass.cpp @@ -35,7 +35,7 @@ clear_pass::clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb clear_pass::~clear_pass() {} -void clear_pass::render(render_context* context) const +void clear_pass::render(const render::context& ctx, render::queue& queue) const { if (clear_color_buffer) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); diff --git a/src/renderer/passes/clear-pass.hpp b/src/renderer/passes/clear-pass.hpp index f9ffe29..e72d8aa 100644 --- a/src/renderer/passes/clear-pass.hpp +++ b/src/renderer/passes/clear-pass.hpp @@ -31,7 +31,7 @@ class clear_pass: public render_pass public: clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer); virtual ~clear_pass(); - virtual void render(render_context* context) const final; + virtual void render(const render::context& ctx, render::queue& queue) const final; void set_cleared_buffers(bool color, bool depth, bool stencil); diff --git a/src/renderer/passes/final-pass.cpp b/src/renderer/passes/final-pass.cpp index 4fb34ef..d2e7cf5 100644 --- a/src/renderer/passes/final-pass.cpp +++ b/src/renderer/passes/final-pass.cpp @@ -31,7 +31,7 @@ #include "gl/texture-wrapping.hpp" #include "gl/texture-filter.hpp" #include "renderer/vertex-attribute.hpp" -#include "renderer/render-context.hpp" +#include "renderer/context.hpp" #include "math/math.hpp" #include #include @@ -41,8 +41,7 @@ final_pass::final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb color_texture(nullptr), bloom_texture(nullptr), blue_noise_texture(nullptr), - blue_noise_scale(1.0), - time_tween(nullptr) + blue_noise_scale(1.0) { shader_program = resource_manager->load("final.glsl"); color_texture_input = shader_program->get_input("color_texture"); @@ -87,7 +86,7 @@ final_pass::~final_pass() delete quad_vbo; } -void final_pass::render(render_context* context) const +void final_pass::render(const render::context& ctx, render::queue& queue) const { rasterizer->use_framebuffer(*framebuffer); @@ -101,7 +100,6 @@ void final_pass::render(render_context* context) const rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); float2 resolution = {std::get<0>(viewport), std::get<1>(viewport)}; - float time = (time_tween) ? (*time_tween)[context->alpha] : 0.0f; // Change shader program rasterizer->use_program(*shader_program); @@ -117,7 +115,7 @@ void final_pass::render(render_context* context) const if (resolution_input) resolution_input->upload(resolution); if (time_input) - time_input->upload(time); + time_input->upload(ctx.t); // Draw quad rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); @@ -138,8 +136,3 @@ void final_pass::set_blue_noise_texture(const gl::texture_2d* texture) this->blue_noise_texture = texture; blue_noise_scale = 1.0f / static_cast(texture->get_dimensions()[0]); } - -void final_pass::set_time_tween(const tween* time) -{ - this->time_tween = time; -} diff --git a/src/renderer/passes/final-pass.hpp b/src/renderer/passes/final-pass.hpp index f9fdcdf..cfe7168 100644 --- a/src/renderer/passes/final-pass.hpp +++ b/src/renderer/passes/final-pass.hpp @@ -39,12 +39,11 @@ class final_pass: public render_pass public: final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); virtual ~final_pass(); - virtual void render(render_context* context) const final; + virtual void render(const render::context& ctx, render::queue& queue) const final; void set_color_texture(const gl::texture_2d* texture); void set_bloom_texture(const gl::texture_2d* texture); void set_blue_noise_texture(const gl::texture_2d* texture); - void set_time_tween(const tween* time); private: gl::shader_program* shader_program; @@ -61,8 +60,6 @@ private: const gl::texture_2d* bloom_texture; const gl::texture_2d* blue_noise_texture; float blue_noise_scale; - - const tween* time_tween; }; #endif // ANTKEEPER_FINAL_PASS_HPP diff --git a/src/renderer/passes/material-pass.cpp b/src/renderer/passes/material-pass.cpp index 5526a93..880e18c 100644 --- a/src/renderer/passes/material-pass.cpp +++ b/src/renderer/passes/material-pass.cpp @@ -34,7 +34,7 @@ #include "renderer/vertex-attribute.hpp" #include "renderer/material-flags.hpp" #include "renderer/model.hpp" -#include "renderer/render-context.hpp" +#include "renderer/context.hpp" #include "scene/camera.hpp" #include "scene/collection.hpp" #include "scene/ambient-light.hpp" @@ -48,12 +48,11 @@ #include "shadow-map-pass.hpp" -static bool operation_compare(const render_operation& a, const render_operation& b); +static bool operation_compare(const render::operation& a, const render::operation& b); material_pass::material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): render_pass(rasterizer, framebuffer), fallback_material(nullptr), - time_tween(nullptr), mouse_position({0.0f, 0.0f}), shadow_map_pass(nullptr), shadow_map(nullptr) @@ -98,7 +97,7 @@ material_pass::~material_pass() delete[] spot_light_cutoffs; } -void material_pass::render(render_context* context) const +void material_pass::render(const render::context& ctx, render::queue& queue) const { rasterizer->use_framebuffer(*framebuffer); @@ -119,10 +118,9 @@ void material_pass::render(render_context* context) const float2 resolution = {static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}; - float time = time_tween->interpolate(context->alpha); - const float3& camera_position = context->camera_transform.translation; - float4x4 view = context->camera->get_view_tween().interpolate(context->alpha); - float4x4 projection = context->camera->get_projection_tween().interpolate(context->alpha); + const float3& camera_position = ctx.camera_transform.translation; + float4x4 view = ctx.camera->get_view_tween().interpolate(ctx.alpha); + float4x4 projection = ctx.camera->get_projection_tween().interpolate(ctx.alpha); float4x4 view_projection = projection * view; float4x4 model_view_projection; float4x4 model; @@ -130,11 +128,11 @@ void material_pass::render(render_context* context) const float3x3 normal_model; float3x3 normal_model_view; float2 clip_depth; - clip_depth[0] = context->camera->get_clip_near_tween().interpolate(context->alpha); - clip_depth[1] = context->camera->get_clip_far_tween().interpolate(context->alpha); + clip_depth[0] = ctx.camera->get_clip_near_tween().interpolate(ctx.alpha); + clip_depth[1] = ctx.camera->get_clip_far_tween().interpolate(ctx.alpha); float log_depth_coef = 2.0f / std::log2(clip_depth[1] + 1.0f); - float camera_exposure = std::exp2(context->camera->get_exposure_tween().interpolate(context->alpha)); + float camera_exposure = std::exp2(ctx.camera->get_exposure_tween().interpolate(ctx.alpha)); int active_material_flags = 0; @@ -149,7 +147,7 @@ void material_pass::render(render_context* context) const spot_light_count = 0; // Collect lights - const std::list* lights = context->collection->get_objects(scene::light::object_type_id); + const std::list* lights = ctx.collection->get_objects(scene::light::object_type_id); for (const scene::object_base* object: *lights) { // Skip inactive lights @@ -165,7 +163,7 @@ void material_pass::render(render_context* context) const if (ambient_light_count < max_ambient_light_count) { // Pre-expose light - ambient_light_colors[ambient_light_count] = light->get_scaled_color_tween().interpolate(context->alpha) * camera_exposure; + ambient_light_colors[ambient_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * camera_exposure; ++ambient_light_count; } break; @@ -177,12 +175,12 @@ void material_pass::render(render_context* context) const if (point_light_count < max_point_light_count) { // Pre-expose light - point_light_colors[point_light_count] = light->get_scaled_color_tween().interpolate(context->alpha) * camera_exposure; + point_light_colors[point_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * camera_exposure; - float3 position = light->get_transform_tween().interpolate(context->alpha).translation; + float3 position = light->get_transform_tween().interpolate(ctx.alpha).translation; point_light_positions[point_light_count] = position; - point_light_attenuations[point_light_count] = static_cast(light)->get_attenuation_tween().interpolate(context->alpha); + point_light_attenuations[point_light_count] = static_cast(light)->get_attenuation_tween().interpolate(ctx.alpha); ++point_light_count; } break; @@ -196,23 +194,23 @@ void material_pass::render(render_context* context) const const scene::directional_light* directional_light = static_cast(light); // Pre-expose light - directional_light_colors[directional_light_count] = light->get_scaled_color_tween().interpolate(context->alpha) * camera_exposure; + directional_light_colors[directional_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * camera_exposure; - float3 direction = static_cast(light)->get_direction_tween().interpolate(context->alpha); + float3 direction = static_cast(light)->get_direction_tween().interpolate(ctx.alpha); directional_light_directions[directional_light_count] = direction; if (directional_light->get_light_texture()) { directional_light_textures[directional_light_count] = directional_light->get_light_texture(); - directional_light_texture_opacities[directional_light_count] = directional_light->get_light_texture_opacity_tween().interpolate(context->alpha); + directional_light_texture_opacities[directional_light_count] = directional_light->get_light_texture_opacity_tween().interpolate(ctx.alpha); - math::transform light_transform = light->get_transform_tween().interpolate(context->alpha); + math::transform light_transform = light->get_transform_tween().interpolate(ctx.alpha); float3 forward = light_transform.rotation * global_forward; float3 up = light_transform.rotation * global_up; float4x4 light_view = math::look_at(light_transform.translation, light_transform.translation + forward, up); - float2 scale = directional_light->get_light_texture_scale_tween().interpolate(context->alpha); + float2 scale = directional_light->get_light_texture_scale_tween().interpolate(ctx.alpha); float4x4 light_projection = math::ortho(-scale.x, scale.x, -scale.y, scale.y, -1.0f, 1.0f); directional_light_texture_matrices[directional_light_count] = light_projection * light_view; @@ -236,16 +234,16 @@ void material_pass::render(render_context* context) const const scene::spot_light* spot_light = static_cast(light); // Pre-expose light - spot_light_colors[spot_light_count] = light->get_scaled_color_tween().interpolate(context->alpha) * camera_exposure; + spot_light_colors[spot_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * camera_exposure; - float3 position = light->get_transform_tween().interpolate(context->alpha).translation; + float3 position = light->get_transform_tween().interpolate(ctx.alpha).translation; spot_light_positions[spot_light_count] = position; - float3 direction = spot_light->get_direction_tween().interpolate(context->alpha); + float3 direction = spot_light->get_direction_tween().interpolate(ctx.alpha); spot_light_directions[spot_light_count] = direction; - spot_light_attenuations[spot_light_count] = spot_light->get_attenuation_tween().interpolate(context->alpha); - spot_light_cutoffs[spot_light_count] = spot_light->get_cosine_cutoff_tween().interpolate(context->alpha); + spot_light_attenuations[spot_light_count] = spot_light->get_attenuation_tween().interpolate(ctx.alpha); + spot_light_cutoffs[spot_light_count] = spot_light->get_cosine_cutoff_tween().interpolate(ctx.alpha); ++spot_light_count; } @@ -270,10 +268,10 @@ void material_pass::render(render_context* context) const shadow_splits_directional[i] = shadow_map_pass->get_split_distances()[i + 1]; } - // Sort render operations - context->operations.sort(operation_compare); + // Sort render queue + queue.sort(operation_compare); - for (const render_operation& operation: context->operations) + for (const render::operation& operation: queue) { // Get operation material const ::material* material = operation.material; @@ -439,7 +437,7 @@ void material_pass::render(render_context* context) const // Upload context-dependent shader parameters if (parameters->time) - parameters->time->upload(time); + parameters->time->upload(ctx.t); if (parameters->mouse) parameters->mouse->upload(mouse_position); if (parameters->resolution) @@ -500,7 +498,7 @@ void material_pass::render(render_context* context) const } // Upload material properties to shader - active_material->upload(context->alpha); + active_material->upload(ctx.alpha); } // Calculate operation-dependent parameters @@ -539,11 +537,6 @@ void material_pass::set_fallback_material(const material* fallback) this->fallback_material = fallback; } -void material_pass::set_time_tween(const tween* time) -{ - this->time_tween = time; -} - const material_pass::parameter_set* material_pass::load_parameter_set(const gl::shader_program* program) const { // Allocate a new parameter set @@ -598,7 +591,7 @@ void material_pass::handle_event(const mouse_moved_event& event) mouse_position = {static_cast(event.x), static_cast(event.y)}; } -bool operation_compare(const render_operation& a, const render_operation& b) +bool operation_compare(const render::operation& a, const render::operation& b) { if (!a.material) return false; diff --git a/src/renderer/passes/material-pass.hpp b/src/renderer/passes/material-pass.hpp index e9d7d9a..7625b09 100644 --- a/src/renderer/passes/material-pass.hpp +++ b/src/renderer/passes/material-pass.hpp @@ -44,14 +44,11 @@ class material_pass: public render_pass, public: material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); virtual ~material_pass(); - virtual void render(render_context* context) const final; + virtual void render(const render::context& ctx, render::queue& queue) const final; /// Sets the material to be used when a render operation is missing a material. If no fallback material is specified, render operations without materials will not be processed. void set_fallback_material(const material* fallback); - /// Sets the time tween, which is interpolated between updates - void set_time_tween(const tween* time); - const ::shadow_map_pass* shadow_map_pass; const gl::texture_2d* shadow_map; @@ -107,7 +104,6 @@ private: mutable std::unordered_map parameter_sets; const material* fallback_material; - const tween* time_tween; float2 mouse_position; int max_ambient_light_count; diff --git a/src/renderer/passes/outline-pass.cpp b/src/renderer/passes/outline-pass.cpp index 99be641..b42a255 100644 --- a/src/renderer/passes/outline-pass.cpp +++ b/src/renderer/passes/outline-pass.cpp @@ -28,7 +28,7 @@ #include "gl/vertex-attribute.hpp" #include "gl/drawing-mode.hpp" #include "renderer/vertex-attribute.hpp" -#include "renderer/render-context.hpp" +#include "renderer/context.hpp" #include "renderer/material.hpp" #include "renderer/material-flags.hpp" #include "scene/camera.hpp" @@ -55,7 +55,7 @@ outline_pass::outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* fr outline_pass::~outline_pass() {} -void outline_pass::render(render_context* context) const +void outline_pass::render(const render::context& ctx, render::queue& queue) const { rasterizer->use_framebuffer(*framebuffer); @@ -64,8 +64,8 @@ void outline_pass::render(render_context* context) const rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); // Get camera matrices - float4x4 view = context->camera->get_view_tween().interpolate(context->alpha); - float4x4 view_projection = context->camera->get_view_projection_tween().interpolate(context->alpha); + float4x4 view = ctx.camera->get_view_tween().interpolate(ctx.alpha); + float4x4 view_projection = ctx.camera->get_view_projection_tween().interpolate(ctx.alpha); float4x4 model_view_projection; @@ -86,7 +86,7 @@ void outline_pass::render(render_context* context) const rasterizer->use_program(*fill_shader); // Render fills - for (const render_operation& operation: context->operations) + for (const render::operation& operation: queue) { const ::material* material = operation.material; if (!material || !(material->get_flags() & MATERIAL_FLAG_OUTLINE)) @@ -122,7 +122,7 @@ void outline_pass::render(render_context* context) const stroke_color_input->upload(outline_color); // Render strokes - for (const render_operation& operation: context->operations) + for (const render::operation& operation: queue) { const ::material* material = operation.material; if (!material || !(material->get_flags() & MATERIAL_FLAG_OUTLINE)) diff --git a/src/renderer/passes/outline-pass.hpp b/src/renderer/passes/outline-pass.hpp index 149092f..d7c9957 100644 --- a/src/renderer/passes/outline-pass.hpp +++ b/src/renderer/passes/outline-pass.hpp @@ -35,7 +35,7 @@ class outline_pass: public render_pass public: outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); virtual ~outline_pass(); - virtual void render(render_context* context) const final; + virtual void render(const render::context& ctx, render::queue& queue) const final; void set_outline_width(float width); void set_outline_color(const float4& color); diff --git a/src/renderer/passes/shadow-map-pass.cpp b/src/renderer/passes/shadow-map-pass.cpp index 0272541..5a7026e 100644 --- a/src/renderer/passes/shadow-map-pass.cpp +++ b/src/renderer/passes/shadow-map-pass.cpp @@ -24,7 +24,7 @@ #include "gl/shader-program.hpp" #include "gl/shader-input.hpp" #include "gl/drawing-mode.hpp" -#include "renderer/render-context.hpp" +#include "renderer/context.hpp" #include "renderer/material.hpp" #include "renderer/material-flags.hpp" #include "scene/camera.hpp" @@ -36,7 +36,7 @@ #include #include -static bool operation_compare(const render_operation& a, const render_operation& b); +static bool operation_compare(const render::operation& a, const render::operation& b); void shadow_map_pass::distribute_frustum_splits(float* split_distances, std::size_t split_count, float split_scheme, float near, float far) { @@ -84,7 +84,7 @@ shadow_map_pass::shadow_map_pass(gl::rasterizer* rasterizer, const gl::framebuff shadow_map_pass::~shadow_map_pass() {} -void shadow_map_pass::render(render_context* context) const +void shadow_map_pass::render(const render::context& ctx, render::queue& queue) const { // Abort if no directional light was set if (!light) @@ -110,11 +110,11 @@ void shadow_map_pass::render(render_context* context) const //glDepthRange(-1.0f, 1.0f); // Get camera - const scene::camera& camera = *context->camera; + const scene::camera& camera = *ctx.camera; // Calculate distances to the depth clipping planes of each frustum split - float clip_near = camera.get_clip_near_tween().interpolate(context->alpha); - float clip_far = camera.get_clip_far_tween().interpolate(context->alpha); + float clip_near = camera.get_clip_near_tween().interpolate(ctx.alpha); + float clip_far = camera.get_clip_far_tween().interpolate(ctx.alpha); split_distances[0] = clip_near; split_distances[4] = clip_far; distribute_frustum_splits(&split_distances[1], 3, split_scheme_weight, clip_near, clip_far); @@ -135,7 +135,7 @@ void shadow_map_pass::render(render_context* context) const } // Calculate a view-projection matrix from the directional light's transform - math::transform light_transform = light->get_transform_tween().interpolate(context->alpha); + math::transform light_transform = light->get_transform_tween().interpolate(ctx.alpha); float3 forward = light_transform.rotation * global_forward; float3 up = light_transform.rotation * global_up; float4x4 light_view = math::look_at(light_transform.translation, light_transform.translation + forward, up); @@ -143,14 +143,14 @@ void shadow_map_pass::render(render_context* context) const float4x4 light_view_projection = light_projection * light_view; // Get the camera's view matrix - float4x4 camera_view = camera.get_view_tween().interpolate(context->alpha); + float4x4 camera_view = camera.get_view_tween().interpolate(ctx.alpha); float4x4 crop_matrix; float4x4 cropped_view_projection; float4x4 model_view_projection; - // Sort render operations - context->operations.sort(operation_compare); + // Sort render queue + queue.sort(operation_compare); gl::shader_program* active_shader_program = nullptr; @@ -214,7 +214,7 @@ void shadow_map_pass::render(render_context* context) const // Calculate shadow matrix shadow_matrices[i] = bias_tile_matrices[i] * cropped_view_projection; - for (const render_operation& operation: context->operations) + for (const render::operation& operation: queue) { // Skip materials which don't cast shadows const ::material* material = operation.material; @@ -260,7 +260,7 @@ void shadow_map_pass::set_light(const scene::directional_light* light) this->light = light; } -bool operation_compare(const render_operation& a, const render_operation& b) +bool operation_compare(const render::operation& a, const render::operation& b) { // Determine transparency bool skinned_a = (a.pose != nullptr); diff --git a/src/renderer/passes/shadow-map-pass.hpp b/src/renderer/passes/shadow-map-pass.hpp index 3167d3c..fb39251 100644 --- a/src/renderer/passes/shadow-map-pass.hpp +++ b/src/renderer/passes/shadow-map-pass.hpp @@ -36,7 +36,7 @@ class shadow_map_pass: public render_pass public: shadow_map_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); virtual ~shadow_map_pass(); - virtual void render(render_context* context) const final; + virtual void render(const render::context& ctx, render::queue& queue) const final; /** * Sets the linear interpolation weight between uniform and logarithmic frustum-splitting schemes. diff --git a/src/renderer/passes/sky-pass.cpp b/src/renderer/passes/sky-pass.cpp index f386518..df73afb 100644 --- a/src/renderer/passes/sky-pass.cpp +++ b/src/renderer/passes/sky-pass.cpp @@ -32,7 +32,7 @@ #include "gl/texture-wrapping.hpp" #include "gl/texture-filter.hpp" #include "renderer/vertex-attribute.hpp" -#include "renderer/render-context.hpp" +#include "renderer/context.hpp" #include "renderer/model.hpp" #include "renderer/material.hpp" #include "scene/camera.hpp" @@ -68,7 +68,6 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe clouds_model_vao(nullptr), cloud_material(nullptr), cloud_shader_program(nullptr), - time_tween(nullptr), observer_altitude_tween(0.0f, math::lerp), sun_position_tween(float3{1.0f, 0.0f, 0.0f}, math::lerp), sun_color_outer_tween(float3{1.0f, 1.0f, 1.0f}, math::lerp), @@ -80,7 +79,7 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe sky_pass::~sky_pass() {} -void sky_pass::render(render_context* context) const +void sky_pass::render(const render::context& ctx, render::queue& queue) const { rasterizer->use_framebuffer(*framebuffer); @@ -93,38 +92,37 @@ void sky_pass::render(render_context* context) const auto viewport = framebuffer->get_dimensions(); rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); - float time = static_cast((*time_tween)[context->alpha]); float2 resolution = {static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}; - const scene::camera& camera = *context->camera; - float clip_near = camera.get_clip_near_tween().interpolate(context->alpha); - float clip_far = camera.get_clip_far_tween().interpolate(context->alpha); + const scene::camera& camera = *ctx.camera; + float clip_near = camera.get_clip_near_tween().interpolate(ctx.alpha); + float clip_far = camera.get_clip_far_tween().interpolate(ctx.alpha); float3 model_scale = float3{1.0f, 1.0f, 1.0f} * (clip_near + clip_far) * 0.5f; float4x4 model = math::scale(math::identity4x4, model_scale); - float4x4 view = math::resize<4, 4>(math::resize<3, 3>(camera.get_view_tween().interpolate(context->alpha))); + float4x4 view = math::resize<4, 4>(math::resize<3, 3>(camera.get_view_tween().interpolate(ctx.alpha))); float4x4 model_view = view * model; - float4x4 projection = camera.get_projection_tween().interpolate(context->alpha); + float4x4 projection = camera.get_projection_tween().interpolate(ctx.alpha); float4x4 view_projection = projection * view; float4x4 model_view_projection = projection * model_view; - float exposure = std::exp2(camera.get_exposure_tween().interpolate(context->alpha)); + float exposure = std::exp2(camera.get_exposure_tween().interpolate(ctx.alpha)); // Interpolate observer altitude - float observer_altitude = observer_altitude_tween.interpolate(context->alpha); + float observer_altitude = observer_altitude_tween.interpolate(ctx.alpha); // Construct tweened inertial to topocentric frame physics::frame topocentric_frame = { - topocentric_frame_translation.interpolate(context->alpha), - topocentric_frame_rotation.interpolate(context->alpha) + topocentric_frame_translation.interpolate(ctx.alpha), + topocentric_frame_rotation.interpolate(ctx.alpha) }; // Get topocentric space direction to sun - float3 sun_position = sun_position_tween.interpolate(context->alpha); + float3 sun_position = sun_position_tween.interpolate(ctx.alpha); float3 sun_direction = math::normalize(sun_position); // Interpolate sun color - float3 sun_color_outer = sun_color_outer_tween.interpolate(context->alpha); - float3 sun_color_inner = sun_color_inner_tween.interpolate(context->alpha); + float3 sun_color_outer = sun_color_outer_tween.interpolate(ctx.alpha); + float3 sun_color_inner = sun_color_inner_tween.interpolate(ctx.alpha); // Draw atmosphere if (sky_model) @@ -139,7 +137,7 @@ void sky_pass::render(render_context* context) const if (resolution_input) resolution_input->upload(resolution); if (time_input) - time_input->upload(time); + time_input->upload(ctx.t); if (exposure_input) exposure_input->upload(exposure); @@ -162,7 +160,7 @@ void sky_pass::render(render_context* context) const if (atmosphere_radii_input) atmosphere_radii_input->upload(atmosphere_radii); - sky_material->upload(context->alpha); + sky_material->upload(ctx.alpha); rasterizer->draw_arrays(*sky_model_vao, sky_model_drawing_mode, sky_model_start_index, sky_model_index_count); } @@ -179,11 +177,11 @@ void sky_pass::render(render_context* context) const if (cloud_sun_color_input) cloud_sun_color_input->upload(sun_color_inner); if (cloud_camera_position_input) - cloud_camera_position_input->upload(context->camera_transform.translation); + cloud_camera_position_input->upload(ctx.camera_transform.translation); if (cloud_camera_exposure_input) cloud_camera_exposure_input->upload(exposure); - cloud_material->upload(context->alpha); + cloud_material->upload(ctx.alpha); rasterizer->draw_arrays(*clouds_model_vao, clouds_model_drawing_mode, clouds_model_start_index, clouds_model_index_count); } @@ -212,7 +210,7 @@ void sky_pass::render(render_context* context) const if (star_exposure_input) star_exposure_input->upload(exposure); - star_material->upload(context->alpha); + star_material->upload(ctx.alpha); rasterizer->draw_arrays(*stars_model_vao, stars_model_drawing_mode, stars_model_start_index, stars_model_index_count); } @@ -246,7 +244,7 @@ void sky_pass::render(render_context* context) const moon_moon_position_input->upload(moon_position); if (moon_sun_position_input) moon_sun_position_input->upload(sun_position); - moon_material->upload(context->alpha); + moon_material->upload(ctx.alpha); rasterizer->draw_arrays(*moon_model_vao, moon_model_drawing_mode, moon_model_start_index, moon_model_index_count); } */ @@ -420,11 +418,6 @@ void sky_pass::update_tweens() topocentric_frame_rotation.update(); } -void sky_pass::set_time_tween(const tween* time) -{ - this->time_tween = time; -} - void sky_pass::set_topocentric_frame(const physics::frame& frame) { topocentric_frame_translation[1] = frame.translation; diff --git a/src/renderer/passes/sky-pass.hpp b/src/renderer/passes/sky-pass.hpp index 8fad240..0dbaecd 100644 --- a/src/renderer/passes/sky-pass.hpp +++ b/src/renderer/passes/sky-pass.hpp @@ -48,12 +48,11 @@ class sky_pass: public render_pass, public: sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); virtual ~sky_pass(); - virtual void render(render_context* context) const final; + virtual void render(const render::context& ctx, render::queue& queue) const final; void update_tweens(); void set_sky_model(const model* model); - void set_time_tween(const tween* time); void set_moon_model(const model* model); void set_stars_model(const model* model); void set_clouds_model(const model* model); @@ -139,7 +138,6 @@ private: const gl::texture_2d* sky_gradient2; float2 mouse_position; - const tween* time_tween; tween observer_altitude_tween; tween sun_position_tween; tween sun_color_outer_tween; diff --git a/src/renderer/passes/ui-pass.cpp b/src/renderer/passes/ui-pass.cpp index 3427b5e..34b3508 100644 --- a/src/renderer/passes/ui-pass.cpp +++ b/src/renderer/passes/ui-pass.cpp @@ -32,7 +32,7 @@ #include "gl/texture-filter.hpp" #include "renderer/vertex-attribute.hpp" #include "renderer/material-flags.hpp" -#include "renderer/render-context.hpp" +#include "renderer/context.hpp" #include "scene/camera.hpp" #include "scene/collection.hpp" #include "scene/ambient-light.hpp" @@ -43,14 +43,13 @@ #include ui_pass::ui_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): - render_pass(rasterizer, framebuffer), - time(0.0f) + render_pass(rasterizer, framebuffer) {} ui_pass::~ui_pass() {} -void ui_pass::render(render_context* context) const +void ui_pass::render(const render::context& ctx, render::queue& queue) const { glEnable(GL_BLEND); glDisable(GL_DEPTH_TEST); @@ -60,24 +59,19 @@ void ui_pass::render(render_context* context) const auto viewport = framebuffer->get_dimensions(); rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); - float4x4 view = context->camera->get_view_tween().interpolate(context->alpha); - float4x4 projection = context->camera->get_projection_tween().interpolate(context->alpha); + float4x4 view = ctx.camera->get_view_tween().interpolate(ctx.alpha); + float4x4 projection = ctx.camera->get_projection_tween().interpolate(ctx.alpha); float4x4 view_projection = projection * view; float4x4 model_view_projection; // Collect billboards - std::list billboards = *context->collection->get_objects(scene::billboard::object_type_id); + std::list billboards = *ctx.collection->get_objects(scene::billboard::object_type_id); // Sort billboards // Rebuild vertex buffer } -void ui_pass::set_time(float time) -{ - this->time = time; -} - const ui_pass::parameter_set* ui_pass::load_parameter_set(const gl::shader_program* program) const { // Allocate a new parameter set diff --git a/src/renderer/passes/ui-pass.hpp b/src/renderer/passes/ui-pass.hpp index 6ef682b..092c56e 100644 --- a/src/renderer/passes/ui-pass.hpp +++ b/src/renderer/passes/ui-pass.hpp @@ -37,9 +37,7 @@ class ui_pass: public render_pass public: ui_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); virtual ~ui_pass(); - virtual void render(render_context* context) const final; - - void set_time(float time); + virtual void render(const render::context& ctx, render::queue& queue) const final; private: /** @@ -54,7 +52,6 @@ private: const parameter_set* load_parameter_set(const gl::shader_program* program) const; mutable std::unordered_map parameter_sets; - float time; }; #endif // ANTKEEPER_UI_PASS_HPP diff --git a/src/renderer/queue.hpp b/src/renderer/queue.hpp new file mode 100644 index 0000000..7a3bec6 --- /dev/null +++ b/src/renderer/queue.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_QUEUE_HPP +#define ANTKEEPER_RENDER_QUEUE_HPP + +#include "renderer/operation.hpp" +#include + +namespace render { + +/// Queue of render operations +typedef std::list queue; + +} // namespace render + +#endif // ANTKEEPER_RENDER_QUEUE_HPP diff --git a/src/renderer/render-pass.hpp b/src/renderer/render-pass.hpp index 69412aa..1b05eb8 100644 --- a/src/renderer/render-pass.hpp +++ b/src/renderer/render-pass.hpp @@ -22,8 +22,8 @@ #include "gl/rasterizer.hpp" #include "gl/framebuffer.hpp" - -struct render_context; +#include "renderer/context.hpp" +#include "renderer/queue.hpp" /** * @@ -34,7 +34,7 @@ public: render_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer); virtual ~render_pass(); - virtual void render(render_context* context) const = 0; + virtual void render(const render::context& ctx, render::queue& queue) const = 0; void set_enabled(bool enabled); bool is_enabled() const; diff --git a/src/renderer/renderer.cpp b/src/renderer/renderer.cpp index 6749428..5a7c6e8 100644 --- a/src/renderer/renderer.cpp +++ b/src/renderer/renderer.cpp @@ -18,7 +18,7 @@ */ #include "renderer/renderer.hpp" -#include "renderer/render-context.hpp" +#include "renderer/context.hpp" #include "renderer/compositor.hpp" #include "scene/collection.hpp" #include "scene/camera.hpp" @@ -45,7 +45,7 @@ renderer::renderer() billboard_op.instance_count = 0; } -void renderer::render(float alpha, const scene::collection& collection) const +void renderer::render(float t, float dt, float alpha, const scene::collection& collection) const { // Get list of all objects in the collection const std::list* objects = collection.get_objects(); @@ -66,6 +66,13 @@ void renderer::render(float alpha, const scene::collection& collection) const return a->get_composite_index() < b->get_composite_index(); } ); + + // Init render context + render::context ctx; + ctx.collection = &collection; + ctx.t = t; + ctx.dt = dt; + ctx.alpha = alpha; // Process cameras in order for (const scene::camera* camera: sorted_cameras) @@ -83,22 +90,22 @@ void renderer::render(float alpha, const scene::collection& collection) const continue; } - // Setup render context - render_context context; - context.camera = camera; - context.camera_transform = camera->get_transform_tween().interpolate(alpha); - context.camera_forward = context.camera_transform.rotation * global_forward; - context.camera_up = context.camera_transform.rotation * global_up; - context.clip_near = camera->get_view_frustum().get_near(); ///< TODO: tween this - context.collection = &collection; - context.alpha = alpha; + // Update render context with camera parameters + ctx.camera = camera; + ctx.camera_transform = camera->get_transform_tween().interpolate(alpha); + ctx.camera_forward = ctx.camera_transform.rotation * global_forward; + ctx.camera_up = ctx.camera_transform.rotation * global_up; + ctx.clip_near = camera->get_view_frustum().get_near(); ///< TODO: tween this + + // Create render queue + render::queue queue; // Get camera culling volume - context.camera_culling_volume = camera->get_culling_mask(); - if (!context.camera_culling_volume) - context.camera_culling_volume = &camera->get_bounds(); + ctx.camera_culling_volume = camera->get_culling_mask(); + if (!ctx.camera_culling_volume) + ctx.camera_culling_volume = &camera->get_bounds(); - // Generate render operations for each visible scene object + // Queue render operations for each visible scene object for (const scene::object_base* object: *objects) { // Skip inactive objects @@ -106,11 +113,11 @@ void renderer::render(float alpha, const scene::collection& collection) const continue; // Process object - process_object(context, object); + process_object(ctx, queue, object); } // Pass render context to the camera's compositor - compositor->composite(&context); + compositor->composite(ctx, queue); } } @@ -119,21 +126,21 @@ void renderer::set_billboard_vao(gl::vertex_array* vao) billboard_op.vertex_array = vao; } -void renderer::process_object(render_context& context, const scene::object_base* object) const +void renderer::process_object(const render::context& ctx, render::queue& queue, const scene::object_base* object) const { std::size_t type = object->get_object_type_id(); if (type == scene::model_instance::object_type_id) - process_model_instance(context, static_cast(object)); + process_model_instance(ctx, queue, static_cast(object)); else if (type == scene::billboard::object_type_id) - process_billboard(context, static_cast(object)); + process_billboard(ctx, queue, static_cast(object)); else if (type == scene::lod_group::object_type_id) - process_lod_group(context, static_cast(object)); + process_lod_group(ctx, queue, static_cast(object)); else if (type == scene::text::object_type_id) - process_text(context, static_cast(object)); + process_text(ctx, queue, static_cast(object)); } -void renderer::process_model_instance(render_context& context, const scene::model_instance* model_instance) const +void renderer::process_model_instance(const render::context& ctx, render::queue& queue, const scene::model_instance* model_instance) const { const model* model = model_instance->get_model(); if (!model) @@ -145,7 +152,7 @@ void renderer::process_model_instance(render_context& context, const scene::mode object_culling_volume = &model_instance->get_bounds(); // Perform view-frustum culling - if (!context.camera_culling_volume->intersects(*object_culling_volume)) + if (!ctx.camera_culling_volume->intersects(*object_culling_volume)) return; const std::vector* instance_materials = model_instance->get_materials(); @@ -153,7 +160,7 @@ void renderer::process_model_instance(render_context& context, const scene::mode for (model_group* group: *groups) { - render_operation operation; + render::operation operation; // Determine operation material operation.material = group->get_material(); @@ -168,15 +175,15 @@ void renderer::process_model_instance(render_context& context, const scene::mode operation.drawing_mode = group->get_drawing_mode(); operation.start_index = group->get_start_index(); operation.index_count = group->get_index_count(); - operation.transform = math::matrix_cast(model_instance->get_transform_tween().interpolate(context.alpha)); - operation.depth = context.clip_near.signed_distance(math::resize<3>(operation.transform[3])); + operation.transform = math::matrix_cast(model_instance->get_transform_tween().interpolate(ctx.alpha)); + operation.depth = ctx.clip_near.signed_distance(math::resize<3>(operation.transform[3])); operation.instance_count = model_instance->get_instance_count(); - context.operations.push_back(operation); + queue.push_back(operation); } } -void renderer::process_billboard(render_context& context, const scene::billboard* billboard) const +void renderer::process_billboard(const render::context& ctx, render::queue& queue, const scene::billboard* billboard) const { // Get object culling volume const geom::bounding_volume* object_culling_volume = billboard->get_culling_mask(); @@ -184,22 +191,22 @@ void renderer::process_billboard(render_context& context, const scene::billboard object_culling_volume = &billboard->get_bounds(); // Perform view-frustum culling - if (!context.camera_culling_volume->intersects(*object_culling_volume)) + if (!ctx.camera_culling_volume->intersects(*object_culling_volume)) return; - math::transform billboard_transform = billboard->get_transform_tween().interpolate(context.alpha); + math::transform billboard_transform = billboard->get_transform_tween().interpolate(ctx.alpha); billboard_op.material = billboard->get_material(); - billboard_op.depth = context.clip_near.signed_distance(math::resize<3>(billboard_transform.translation)); + billboard_op.depth = ctx.clip_near.signed_distance(math::resize<3>(billboard_transform.translation)); // Align billboard if (billboard->get_billboard_type() == scene::billboard_type::spherical) { - billboard_transform.rotation = math::normalize(math::look_rotation(context.camera_forward, context.camera_up) * billboard_transform.rotation); + billboard_transform.rotation = math::normalize(math::look_rotation(ctx.camera_forward, ctx.camera_up) * billboard_transform.rotation); } else if (billboard->get_billboard_type() == scene::billboard_type::cylindrical) { const float3& alignment_axis = billboard->get_alignment_axis(); - float3 look = math::normalize(geom::project_on_plane(billboard_transform.translation - context.camera_transform.translation, {0.0f, 0.0f, 0.0f}, alignment_axis)); + float3 look = math::normalize(geom::project_on_plane(billboard_transform.translation - ctx.camera_transform.translation, {0.0f, 0.0f, 0.0f}, alignment_axis)); float3 right = math::normalize(math::cross(alignment_axis, look)); look = math::cross(right, alignment_axis); float3 up = math::cross(look, right); @@ -208,23 +215,23 @@ void renderer::process_billboard(render_context& context, const scene::billboard billboard_op.transform = math::matrix_cast(billboard_transform); - context.operations.push_back(billboard_op); + queue.push_back(billboard_op); } -void renderer::process_lod_group(render_context& context, const scene::lod_group* lod_group) const +void renderer::process_lod_group(const render::context& ctx, render::queue& queue, const scene::lod_group* lod_group) const { // Select level of detail - std::size_t level = lod_group->select_lod(*context.camera); + std::size_t level = lod_group->select_lod(*ctx.camera); // Process all objects in the group with the selected level of detail const std::list& objects = lod_group->get_objects(level); for (const scene::object_base* object: objects) { - process_object(context, object); + process_object(ctx, queue, object); } } -void renderer::process_text(render_context& context, const scene::text* text) const +void renderer::process_text(const render::context& ctx, render::queue& queue, const scene::text* text) const { // Get object culling volume const geom::bounding_volume* object_culling_volume = text->get_culling_mask(); @@ -232,19 +239,8 @@ void renderer::process_text(render_context& context, const scene::text* text) co object_culling_volume = &text->get_bounds(); // Perform view-frustum culling - if (!context.camera_culling_volume->intersects(*object_culling_volume)) + if (!ctx.camera_culling_volume->intersects(*object_culling_volume)) return; - render_operation operation; - operation.material = text->get_material(); - operation.pose = nullptr; - operation.vertex_array = text->get_vertex_array(); - operation.drawing_mode = gl::drawing_mode::triangles; - operation.start_index = 0; - operation.index_count = text->get_vertex_count(); - operation.transform = math::matrix_cast(text->get_transform_tween().interpolate(context.alpha)); - operation.depth = context.clip_near.signed_distance(math::resize<3>(operation.transform[3])); - operation.instance_count = 0; - - context.operations.push_back(operation); + text->render(ctx, queue); } diff --git a/src/renderer/renderer.hpp b/src/renderer/renderer.hpp index e50bcaf..a18484e 100644 --- a/src/renderer/renderer.hpp +++ b/src/renderer/renderer.hpp @@ -20,11 +20,11 @@ #ifndef ANTKEEPER_RENDERER_HPP #define ANTKEEPER_RENDERER_HPP -#include "render-operation.hpp" +#include "renderer/operation.hpp" +#include "renderer/context.hpp" +#include "renderer/queue.hpp" #include "gl/vertex-array.hpp" -struct render_context; - namespace scene { class collection; @@ -57,10 +57,12 @@ public: /** * Renders a collection of scene objects. * + * @param t Current time, in seconds. + * @param dt Timestep, in seconds. * @param alpha Subframe interpolation factor. * @parma collection Collection of scene objects to render. */ - void render(float alpha, const scene::collection& collection) const; + void render(float t, float dt, float alpha, const scene::collection& collection) const; /** * Sets the VAO to be used when generating render operations for billboards. @@ -68,14 +70,13 @@ public: void set_billboard_vao(gl::vertex_array* vao); private: - void process_object(render_context& context, const scene::object_base* object) const; - void process_model_instance(render_context& context, const scene::model_instance* model_instance) const; - void process_billboard(render_context& context, const scene::billboard* billboard) const; - void process_lod_group(render_context& context, const scene::lod_group* lod_group) const; - void process_text(render_context& context, const scene::text* text) const; + void process_object(const render::context& ctx, render::queue& queue, const scene::object_base* object) const; + void process_model_instance(const render::context& ctx, render::queue& queue, const scene::model_instance* model_instance) const; + void process_billboard(const render::context& ctx, render::queue& queue, const scene::billboard* billboard) const; + void process_lod_group(const render::context& ctx, render::queue& queue, const scene::lod_group* lod_group) const; + void process_text(const render::context& ctx, render::queue& queue, const scene::text* text) const; - mutable render_operation billboard_op; + mutable render::operation billboard_op; }; #endif // ANTKEEPER_RENDERER_HPP - diff --git a/src/renderer/shader-template.hpp b/src/renderer/shader-template.hpp index 489cad0..295fd53 100644 --- a/src/renderer/shader-template.hpp +++ b/src/renderer/shader-template.hpp @@ -35,7 +35,7 @@ * * `#pragma vertex`: Replaced with `#define __VERTEX__` when generating vertex shader objects. * * `#pragma fragment`: Replaced with `#define __FRAGMENT__` when generating fragment shader objects. * * `#pragma geometry`: Replaced with `#define __GEOMETRY__` when generating geometry shader objects. - * * `#pragma define `: Will be replaced with `#define ` if its definition is passed to the shader template. + * * `#pragma define `: Will be replaced with `#define ` if its definition is passed to the shader template. * * @see gl::shader_stage * @see gl::shader_object diff --git a/src/renderer/simple-render-pass.cpp b/src/renderer/simple-render-pass.cpp index 57d53d8..a40b8b6 100644 --- a/src/renderer/simple-render-pass.cpp +++ b/src/renderer/simple-render-pass.cpp @@ -30,7 +30,7 @@ #include "gl/texture-wrapping.hpp" #include "gl/texture-filter.hpp" #include "renderer/vertex-attribute.hpp" -#include "renderer/render-context.hpp" +#include "renderer/context.hpp" #include "renderer/material.hpp" #include "renderer/material-property.hpp" #include "math/math.hpp" @@ -38,8 +38,7 @@ simple_render_pass::simple_render_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, gl::shader_program* shader_program): render_pass(rasterizer, framebuffer), - shader_program(shader_program), - time_tween(nullptr) + shader_program(shader_program) { // Create material material = new ::material(shader_program); @@ -84,7 +83,7 @@ simple_render_pass::~simple_render_pass() delete quad_vbo; } -void simple_render_pass::render(render_context* context) const +void simple_render_pass::render(const render::context& ctx, render::queue& queue) const { // Bind framebuffer rasterizer->use_framebuffer(*framebuffer); @@ -103,21 +102,13 @@ void simple_render_pass::render(render_context* context) const // Change shader program rasterizer->use_program(*shader_program); - // Get interpolated time - float time = (time_tween) ? time_tween->interpolate(context->alpha) : 0.0f; - // Update material properties - time_property->set_value(time); + time_property->set_value(ctx.t); resolution_property->set_value({static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}); // Upload material properties - material->upload(context->alpha); + material->upload(ctx.alpha); // Draw quad rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); } - -void simple_render_pass::set_time_tween(const tween* time) -{ - this->time_tween = time; -} diff --git a/src/renderer/simple-render-pass.hpp b/src/renderer/simple-render-pass.hpp index bbc38db..271828f 100644 --- a/src/renderer/simple-render-pass.hpp +++ b/src/renderer/simple-render-pass.hpp @@ -40,9 +40,7 @@ class simple_render_pass: public render_pass public: simple_render_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, gl::shader_program* shader_program); virtual ~simple_render_pass(); - virtual void render(render_context* context) const final; - - void set_time_tween(const tween* time); + virtual void render(const render::context& ctx, render::queue& queue) const final; const ::material* get_material() const; ::material* get_material(); @@ -53,8 +51,6 @@ private: material_property* time_property; material_property* resolution_property; - const tween* time_tween; - gl::vertex_buffer* quad_vbo; gl::vertex_array* quad_vao; }; diff --git a/src/scene/object.cpp b/src/scene/object.cpp index 6e95365..9777985 100644 --- a/src/scene/object.cpp +++ b/src/scene/object.cpp @@ -49,6 +49,9 @@ std::size_t object_base::next_object_type_id() return id++; } +void object_base::render(const render::context& ctx, render::queue& queue) const +{} + void object_base::update_tweens() { transform.update(); diff --git a/src/scene/object.hpp b/src/scene/object.hpp index 3e82f47..fd76931 100644 --- a/src/scene/object.hpp +++ b/src/scene/object.hpp @@ -25,6 +25,8 @@ #include "math/vector-type.hpp" #include "math/quaternion-type.hpp" #include "math/transform-type.hpp" +#include "renderer/context.hpp" +#include "renderer/queue.hpp" #include #include @@ -53,6 +55,17 @@ public: * Destroys a scene object base. */ virtual ~object_base() = default; + + /** + * Adds a render operation describing this object to a render queue. + * + * @param ctx Render context. + * @param queue Render queue. + * + * @see render::context + * @see render::operation + */ + virtual void render(const render::context& ctx, render::queue& queue) const; /** * Updates all tweens in the scene object. diff --git a/src/scene/text.cpp b/src/scene/text.cpp index 50d8efa..2b3b0dc 100644 --- a/src/scene/text.cpp +++ b/src/scene/text.cpp @@ -81,6 +81,15 @@ text::text(): vao->bind(render::vertex_attribute::position, position_attribute); vao->bind(render::vertex_attribute::uv, uv_attribute); vao->bind(render::vertex_attribute::color, color_attribute); + + // Init render operation + render_op.material = nullptr; + render_op.pose = nullptr; + render_op.vertex_array = vao; + render_op.drawing_mode = gl::drawing_mode::triangles; + render_op.start_index = 0; + render_op.index_count = 0; + render_op.instance_count = 0; } text::~text() @@ -90,9 +99,25 @@ text::~text() delete vbo; } +void text::render(const render::context& ctx, render::queue& queue) const +{ + if (!vertex_count) + return; + + render_op.transform = math::matrix_cast(get_transform_tween().interpolate(ctx.alpha)); + render_op.depth = ctx.clip_near.signed_distance(math::resize<3>(render_op.transform[3])); + queue.push_back(render_op); +} + +void text::refresh() +{ + update_content(); +} + void text::set_material(::material* material) { this->material = material; + render_op.material = material; } void text::set_font(const type::bitmap_font* font) @@ -162,6 +187,9 @@ void text::update_content() if (!font || content_u32.empty()) { vertex_count = 0; + render_op.index_count = vertex_count; + local_bounds = {{0, 0, 0}, {0, 0, 0}}; + transformed(); return; } @@ -287,6 +315,7 @@ void text::update_content() // Update vertex count this->vertex_count = vertex_count; + render_op.index_count = vertex_count; // Update world-space bounds transformed(); diff --git a/src/scene/text.hpp b/src/scene/text.hpp index 047fe5b..a56a258 100644 --- a/src/scene/text.hpp +++ b/src/scene/text.hpp @@ -45,6 +45,14 @@ public: /// Destructs a text object. ~text(); + /// @copydoc scene::object_base::render(const render::context&, render::queue&) const + virtual void render(const render::context& ctx, render::queue& queue) const; + + /** + * Manually updates the text object if its font has been updated or altered in any way. + */ + void refresh(); + /** * Sets the text material. * @@ -97,15 +105,6 @@ public: /// Returns the text color. const float4& get_color() const; - /// Returns the text vertex array. - const gl::vertex_array* get_vertex_array() const; - - /// Returns the text vertex buffer. - const gl::vertex_buffer* get_vertex_buffer() const; - - /// Returns the number of vertices. - std::size_t get_vertex_count() const; - /// @copydoc scene::object::get_bounds() const virtual const bounding_volume_type& get_bounds() const; @@ -118,6 +117,7 @@ private: virtual void transformed(); + mutable render::operation render_op; aabb_type local_bounds; aabb_type world_bounds; material* material; @@ -158,21 +158,6 @@ inline const float4& text::get_color() const return color; } -inline const gl::vertex_array* text::get_vertex_array() const -{ - return vao; -} - -inline const gl::vertex_buffer* text::get_vertex_buffer() const -{ - return vbo; -} - -inline std::size_t text::get_vertex_count() const -{ - return vertex_count; -} - inline const typename object_base::bounding_volume_type& text::get_bounds() const { return world_bounds;