Browse Source

Move some material flags to material parameters. Remove shadow mapping parameters from shadow map pass and add them to directional lights

master
C. J. Howard 1 year ago
parent
commit
407f62a6bc
32 changed files with 871 additions and 454 deletions
  1. +0
    -1
      CMakeLists.txt
  2. +2
    -1
      src/animation/screen-transition.cpp
  3. +0
    -1
      src/entity/commands.cpp
  4. +15
    -11
      src/game/ant/species.hpp
  5. +0
    -50
      src/game/component/genome.hpp
  6. +5
    -7
      src/game/component/portal.hpp
  7. +0
    -2
      src/game/context.hpp
  8. +1
    -1
      src/game/fonts.cpp
  9. +77
    -0
      src/game/spawn.cpp
  10. +65
    -0
      src/game/spawn.hpp
  11. +3
    -11
      src/game/state/boot.cpp
  12. +30
    -20
      src/game/state/nest-selection.cpp
  13. +0
    -3
      src/game/state/nest-selection.hpp
  14. +0
    -11
      src/game/state/nuptial-flight.cpp
  15. +1
    -1
      src/game/state/splash.cpp
  16. +50
    -0
      src/game/system/metamorphosis.cpp
  17. +10
    -17
      src/game/system/metamorphosis.hpp
  18. +1
    -17
      src/game/system/morphogenesis.hpp
  19. +0
    -82
      src/game/system/proteome.cpp
  20. +6
    -1
      src/game/world.cpp
  21. +42
    -0
      src/render/blend-mode.hpp
  22. +0
    -7
      src/render/material-flags.hpp
  23. +30
    -2
      src/render/material.cpp
  24. +72
    -6
      src/render/material.hpp
  25. +84
    -65
      src/render/passes/material-pass.cpp
  26. +0
    -5
      src/render/passes/material-pass.hpp
  27. +149
    -70
      src/render/passes/shadow-map-pass.cpp
  28. +22
    -32
      src/render/passes/shadow-map-pass.hpp
  29. +14
    -11
      src/render/shadow-mode.hpp
  30. +32
    -18
      src/resources/material-loader.cpp
  31. +42
    -1
      src/scene/directional-light.cpp
  32. +118
    -0
      src/scene/directional-light.hpp

+ 0
- 1
CMakeLists.txt View File

@ -1,6 +1,5 @@
cmake_minimum_required(VERSION 3.7)
option(VERSION_STRING "Project version string" "0.0.0")
project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX)

+ 2
- 1
src/animation/screen-transition.cpp View File

@ -24,7 +24,8 @@
screen_transition::screen_transition()
{
// Setup material
material.set_flags(MATERIAL_FLAG_TRANSLUCENT | MATERIAL_FLAG_X_RAY);
material.set_flags(MATERIAL_FLAG_X_RAY);
material.set_blend_mode(render::blend_mode::translucent);
progress = material.add_property<float>("progress");
// Setup billboard

+ 0
- 1
src/entity/commands.cpp View File

@ -20,7 +20,6 @@
#include "entity/commands.hpp"
#include "game/component/model.hpp"
#include "game/component/transform.hpp"
#include "game/component/parent.hpp"
#include "game/component/celestial-body.hpp"
#include "game/component/terrain.hpp"
#include "math/quaternion.hpp"

src/game/component/proteome.hpp → src/game/ant/species.hpp View File

@ -17,23 +17,27 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_COMPONENT_PROTEOME_HPP
#define ANTKEEPER_GAME_COMPONENT_PROTEOME_HPP
#ifndef ANTKEEPER_GAME_ANT_SPECIES_HPP
#define ANTKEEPER_GAME_ANT_SPECIES_HPP
#include <string>
#include <vector>
#include "game/ant/caste.hpp"
#include "game/ant/phenome.hpp"
#include "render/model.hpp"
#include <unordered_map>
namespace game {
namespace component {
namespace ant {
/// Set of all proteins that can be expressed by an organism.
struct proteome
struct species
{
/// Set of amino acid sequences of every protein in the proteome.
std::vector<std::string> proteins;
/// Caste-specific phenomes
std::unordered_map<caste, phenome> phenomes;
/// Caste-specific models
std::unordered_map<caste, render::model*> models;
};
} // namespace component
} // namespace ant
} // namespace game
#endif // ANTKEEPER_GAME_COMPONENT_PROTEOME_HPP
#endif // ANTKEEPER_GAME_ANT_SPECIES_HPP

+ 0
- 50
src/game/component/genome.hpp View File

@ -1,50 +0,0 @@
/*
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_COMPONENT_GENOME_HPP
#define ANTKEEPER_GAME_COMPONENT_GENOME_HPP
#include <string>
#include <vector>
namespace game {
namespace component {
/// Complete set of genetic material in an organism.
struct genome
{
/**
* Number of complete sets of chromosomes in a cell.
*
* A value of `1` indicates the organism is haploid, `2` is diploid, `3` is triploid, etc.
*/
unsigned int ploidy;
/**
* Set of DNA base sequences for every chromosomes in the genome.
*
* A DNA base sequence is a string of IUPAC DNA base symbols. Homologous chromosomes should be stored consecutively, such that in a diploid organism, a chromosome with an even index is homologous to the following chromosome.
*/
std::vector<std::string> chromosomes;
};
} // namespace component
} // namespace game
#endif // ANTKEEPER_GAME_COMPONENT_GENOME_HPP

src/game/component/parent.hpp → src/game/component/portal.hpp View File

@ -17,20 +17,18 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_COMPONENT_PARENT_HPP
#define ANTKEEPER_GAME_COMPONENT_PARENT_HPP
#include "entity/id.hpp"
#ifndef ANTKEEPER_GAME_COMPONENT_PORTAL_HPP
#define ANTKEEPER_GAME_COMPONENT_PORTAL_HPP
namespace game {
namespace component {
struct parent
struct portal
{
entity::id parent;
};
} // namespace component
} // namespace game
#endif // ANTKEEPER_GAME_COMPONENT_PARENT_HPP
#endif // ANTKEEPER_GAME_COMPONENT_PORTAL_HPP

+ 0
- 2
src/game/context.hpp View File

@ -97,7 +97,6 @@ namespace game
class camera;
class nest;
class render;
class proteome;
class steering;
class spring;
}
@ -295,7 +294,6 @@ struct context
game::system::atmosphere* atmosphere_system;
game::system::astronomy* astronomy_system;
game::system::orbit* orbit_system;
game::system::proteome* proteome_system;
double3 rgb_wavelengths;

+ 1
- 1
src/game/fonts.cpp View File

@ -61,7 +61,7 @@ static void build_bitmap_font(const type::typeface& typeface, float size, const
font_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear);
// Create font material
material.set_flags(MATERIAL_FLAG_TRANSLUCENT);
material.set_blend_mode(render::blend_mode::translucent);
material.add_property<const gl::texture_2d*>("font_bitmap")->set_value(font_texture);
material.set_shader_program(shader_program);
}

+ 77
- 0
src/game/spawn.cpp View File

@ -0,0 +1,77 @@
/*
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
*/
#include "game/spawn.hpp"
#include "game/component/transform.hpp"
#include "game/component/model.hpp"
namespace game {
entity::id spawn_ant_egg(game::context& ctx, const ant::genome& genome, bool fertilized, const float3& position)
{
// Create entity
entity::id egg_eid = ctx.entity_registry->create();
// Construct transform component
component::transform transform_component;
transform_component.local = math::transform<float>::identity;
transform_component.local.translation = position;
transform_component.world = transform_component.local;
transform_component.warp = true;
ctx.entity_registry->emplace<component::transform>(egg_eid, transform_component);
// Construct model component
component::model model_component;
model_component.render_model = genome.egg->phene.model;
model_component.instance_count = 0;
model_component.layers = ~0;
ctx.entity_registry->emplace<component::model>(egg_eid, model_component);
return egg_eid;
}
entity::id spawn_ant_larva(game::context& ctx, const ant::genome& genome, const float3& position)
{
// Create entity
entity::id larva_eid = ctx.entity_registry->create();
// Construct transform component
component::transform transform_component;
transform_component.local = math::transform<float>::identity;
transform_component.local.translation = position;
transform_component.world = transform_component.local;
transform_component.warp = true;
ctx.entity_registry->emplace<component::transform>(larva_eid, transform_component);
// Construct model component
component::model model_component;
model_component.render_model = genome.larva->phene.model;
model_component.instance_count = 0;
model_component.layers = ~0;
ctx.entity_registry->emplace<component::model>(larva_eid, model_component);
return larva_eid;
}
entity::id spawn_worker_ant(game::context& ctx, const ant::genome& genome, const float3& position)
{
return entt::null;
}
} // namespace game

+ 65
- 0
src/game/spawn.hpp View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_SPAWN_HPP
#define ANTKEEPER_GAME_SPAWN_HPP
#include "game/context.hpp"
#include "game/ant/genome.hpp"
#include "utility/fundamental-types.hpp"
namespace game {
/**
* Spawns an ant egg.
*
* @param ctx Game context.
* @param genome Ant genome.
* @param fertilized Whether the egg has been fertilized.
* @param position Position at which to spawn an egg.
*
* @return Entity ID of the spawned ant egg.
*/
entity::id spawn_ant_egg(game::context& ctx, const ant::genome& genome, bool fertilized, const float3& position);
/**
* Spawns an ant larva.
*
* @param ctx Game context.
* @param genome Ant genome.
* @param position Position at which to spawn an larva.
*
* @return Entity ID of the spawned ant larva.
*/
entity::id spawn_ant_larva(game::context& ctx, const ant::genome& genome, const float3& position);
/**
* Spawns a worker ant.
*
* @param ctx Game context.
* @param genome Ant genome.
* @param position Position at which to spawn a worker ant.
*
* @return Entity ID of the spawned worker ant.
*/
entity::id spawn_worker_ant(game::context& ctx, const ant::genome& genome, const float3& position);
} // namespace game
#endif // ANTKEEPER_GAME_SPAWN_HPP

+ 3
- 11
src/game/state/boot.cpp View File

@ -71,7 +71,6 @@
#include "game/system/blackbody.hpp"
#include "game/system/atmosphere.hpp"
#include "game/system/orbit.hpp"
#include "game/system/proteome.hpp"
#include "game/system/steering.hpp"
#include "game/system/spring.hpp"
#include "entity/commands.hpp"
@ -497,8 +496,7 @@ void boot::setup_rendering()
ctx.surface_shadow_map_clear_pass->set_cleared_buffers(false, true, false);
ctx.surface_shadow_map_clear_pass->set_clear_depth(1.0f);
ctx.surface_shadow_map_pass = new render::shadow_map_pass(ctx.rasterizer, ctx.shadow_map_framebuffer, ctx.resource_manager);
ctx.surface_shadow_map_pass->set_split_scheme_weight(0.75f);
ctx.surface_shadow_map_pass = new render::shadow_map_pass(ctx.rasterizer, ctx.resource_manager);
ctx.surface_clear_pass = new render::clear_pass(ctx.rasterizer, ctx.hdr_framebuffer);
ctx.surface_clear_pass->set_cleared_buffers(false, true, true);
@ -514,8 +512,6 @@ void boot::setup_rendering()
ctx.surface_material_pass = new render::material_pass(ctx.rasterizer, ctx.hdr_framebuffer, ctx.resource_manager);
ctx.surface_material_pass->set_fallback_material(ctx.fallback_material);
ctx.surface_material_pass->shadow_map_pass = ctx.surface_shadow_map_pass;
ctx.surface_material_pass->shadow_map = ctx.shadow_map_depth_texture;
ctx.app->get_event_dispatcher()->subscribe<mouse_moved_event>(ctx.surface_material_pass);
ctx.surface_outline_pass = new render::outline_pass(ctx.rasterizer, ctx.hdr_framebuffer, ctx.resource_manager);
@ -733,7 +729,7 @@ void boot::setup_scenes()
menu_bg_material->set_shader_program(ctx.resource_manager->load<gl::shader_program>("ui-element-untextured.glsl"));
auto menu_bg_tint = menu_bg_material->add_property<float4>("tint");
menu_bg_tint->set_value(float4{0.0f, 0.0f, 0.0f, 0.5f});
menu_bg_material->set_flags(MATERIAL_FLAG_TRANSLUCENT);
menu_bg_material->set_blend_mode(render::blend_mode::translucent);
menu_bg_material->update_tweens();
ctx.menu_bg_billboard = new scene::billboard();
ctx.menu_bg_billboard->set_active(false);
@ -750,7 +746,7 @@ void boot::setup_scenes()
flash_tint->set_value(float4{1, 1, 1, 1});
//flash_tint->set_tween_interpolator(ease<float4>::out_quad);
flash_material->set_flags(MATERIAL_FLAG_TRANSLUCENT);
flash_material->set_blend_mode(render::blend_mode::translucent);
flash_material->update_tweens();
ctx.camera_flash_billboard = new scene::billboard();
@ -966,9 +962,6 @@ void boot::setup_systems()
ctx.astronomy_system->set_transmittance_samples(16);
ctx.astronomy_system->set_sky_pass(ctx.sky_pass);
// Setup proteome system
ctx.proteome_system = new game::system::proteome(*ctx.entity_registry);
// Setup render system
ctx.render_system = new game::system::render(*ctx.entity_registry);
//ctx.render_system->add_layer(ctx.underground_scene);
@ -1203,7 +1196,6 @@ void boot::setup_loop()
ctx.spatial_system->update(t, dt);
ctx.constraint_system->update(t, dt);
ctx.painting_system->update(t, dt);
ctx.proteome_system->update(t, dt);
ctx.animator->animate(dt);
ctx.render_system->update(t, dt);
}

+ 30
- 20
src/game/state/nest-selection.cpp View File

@ -57,6 +57,7 @@
#include "game/ant/phenome.hpp"
#include "game/ant/genome.hpp"
#include "game/ant/cladogenesis.hpp"
#include "game/spawn.hpp"
using namespace game::ant;
@ -68,25 +69,9 @@ nest_selection::nest_selection(game::context& ctx):
{
ctx.logger->push_task("Entering nest selection state");
ctx.logger->push_task("Generating genome");
std::random_device rng;
ant::genome* genome = ant::cladogenesis(ctx.active_ecoregion->gene_pools[0], rng);
// genome.antennae = ctx.resource_manager->load<ant::gene::antennae>("pogonomyrmex-antennae.dna");
// genome.eyes = ctx.resource_manager->load<ant::gene::eyes>("pogonomyrmex-eyes.dna");
// genome.gaster = ctx.resource_manager->load<ant::gene::gaster>("pogonomyrmex-gaster.dna");
// genome.head = ctx.resource_manager->load<ant::gene::head>("pogonomyrmex-head.dna");
// genome.legs = ctx.resource_manager->load<ant::gene::legs>("pogonomyrmex-legs.dna");
// genome.mandibles = ctx.resource_manager->load<ant::gene::mandibles>("pogonomyrmex-mandibles.dna");
// genome.mesosoma = ctx.resource_manager->load<ant::gene::mesosoma>("pogonomyrmex-mesosoma.dna");
// genome.ocelli = ctx.resource_manager->load<ant::gene::ocelli>("ocelli-absent.dna");
// genome.pigmentation = ctx.resource_manager->load<ant::gene::pigmentation>("rust-pigmentation.dna");
// genome.sculpturing = ctx.resource_manager->load<ant::gene::sculpturing>("politus-sculpturing.dna");
// genome.sting = ctx.resource_manager->load<ant::gene::sting>("pogonomyrmex-sting.dna");
// genome.waist = ctx.resource_manager->load<ant::gene::waist>("pogonomyrmex-waist.dna");
// genome.wings = ctx.resource_manager->load<ant::gene::wings>("wings-absent.dna");
ctx.logger->pop_task(EXIT_SUCCESS);
ctx.logger->push_task("Building worker phenome");
@ -112,7 +97,6 @@ nest_selection::nest_selection(game::context& ctx):
worker_model_component.layers = ~0;
ctx.entity_registry->emplace<component::model>(worker_eid, worker_model_component);
// Disable UI color clear
ctx.ui_clear_pass->set_cleared_buffers(false, true, false);
@ -180,11 +164,37 @@ nest_selection::nest_selection(game::context& ctx):
// Satisfy first person camera rig constraints
satisfy_first_person_camera_rig_constraints();
auto color_checker_archetype = ctx.resource_manager->load<entity::archetype>("color-checker.ent");
color_checker_archetype->create(*ctx.entity_registry);
// auto color_checker_archetype = ctx.resource_manager->load<entity::archetype>("color-checker.ent");
// color_checker_archetype->create(*ctx.entity_registry);
// auto ruler_archetype = ctx.resource_manager->load<entity::archetype>("ruler-10cm.ent");
// ruler_archetype->create(*ctx.entity_registry);
auto yucca_archetype = ctx.resource_manager->load<entity::archetype>("yucca-plant-l.ent");
auto yucca_eid = yucca_archetype->create(*ctx.entity_registry);
entity::command::warp_to(*ctx.entity_registry, yucca_eid, {0, 4, 30});
yucca_archetype = ctx.resource_manager->load<entity::archetype>("yucca-plant-m.ent");
yucca_eid = yucca_archetype->create(*ctx.entity_registry);
entity::command::warp_to(*ctx.entity_registry, yucca_eid, {400, 0, 200});
yucca_archetype = ctx.resource_manager->load<entity::archetype>("yucca-plant-s.ent");
yucca_eid = yucca_archetype->create(*ctx.entity_registry);
entity::command::warp_to(*ctx.entity_registry, yucca_eid, {-300, 3, -300});
auto cactus_plant_archetype = ctx.resource_manager->load<entity::archetype>("barrel-cactus-plant-l.ent");
auto cactus_plant_eid = cactus_plant_archetype->create(*ctx.entity_registry);
entity::command::warp_to(*ctx.entity_registry, cactus_plant_eid, {-100, 0, -200});
cactus_plant_archetype = ctx.resource_manager->load<entity::archetype>("barrel-cactus-plant-m.ent");
cactus_plant_eid = cactus_plant_archetype->create(*ctx.entity_registry);
entity::command::warp_to(*ctx.entity_registry, cactus_plant_eid, {100, -2, -70});
cactus_plant_archetype = ctx.resource_manager->load<entity::archetype>("barrel-cactus-plant-s.ent");
cactus_plant_eid = cactus_plant_archetype->create(*ctx.entity_registry);
entity::command::warp_to(*ctx.entity_registry, cactus_plant_eid, {50, 2, 80});
auto cactus_seed_archetype = ctx.resource_manager->load<entity::archetype>("barrel-cactus-seed.ent");
auto cactus_seed_eid = cactus_seed_archetype->create(*ctx.entity_registry);
entity::command::warp_to(*ctx.entity_registry, cactus_seed_eid, {10, 5, 10});
// Queue control setup
ctx.function_queue.push(std::bind(&nest_selection::enable_controls, this));

+ 0
- 3
src/game/state/nest-selection.hpp View File

@ -36,11 +36,9 @@ public:
private:
void create_first_person_camera_rig();
void destroy_first_person_camera_rig();
void set_first_person_camera_rig_pedestal(float pedestal);
void move_first_person_camera_rig(const float2& direction, float factor);
void satisfy_first_person_camera_rig_constraints();
void enable_controls();
void disable_controls();
@ -59,7 +57,6 @@ private:
float first_person_camera_far_speed;
float first_person_camera_rig_pedestal_speed;
float first_person_camera_rig_pedestal;
bool mouse_look;
};

+ 0
- 11
src/game/state/nuptial-flight.cpp View File

@ -54,9 +54,6 @@
#include "color/color.hpp"
#include "application.hpp"
#include "input/mouse.hpp"
#include "geom/primitive/sphere.hpp"
#include "geom/primitive/rectangle.hpp"
#include "geom/primitive/hyperplane.hpp"
#include <iostream>
namespace game {
@ -65,11 +62,6 @@ namespace state {
nuptial_flight::nuptial_flight(game::context& ctx):
game::state::base(ctx)
{
geom::primitive::rectangle<float> rect;
rect.intersects(rect);
geom::primitive::hyperplane<float, 4> plane;
plane.distance({0, 0, 0, 0});
ctx.logger->push_task("Entering nuptial flight state");
// Init selected picking flag
@ -89,9 +81,6 @@ nuptial_flight::nuptial_flight(game::context& ctx):
game::world::create_observer(ctx);
}
// Load biome
//game::load::biome(ctx, "desert-scrub.bio");
// Set world time
game::world::set_time(ctx, 2022, 6, 21, 12, 0, 0.0);

+ 1
- 1
src/game/state/splash.cpp View File

@ -49,7 +49,7 @@ splash::splash(game::context& ctx):
auto splash_dimensions = splash_texture->get_dimensions();
// Construct splash billboard material
splash_billboard_material.set_flags(MATERIAL_FLAG_TRANSLUCENT);
splash_billboard_material.set_blend_mode(render::blend_mode::translucent);
splash_billboard_material.set_shader_program(ctx.resource_manager->load<gl::shader_program>("ui-element-textured.glsl"));
splash_billboard_material.add_property<const gl::texture_2d*>("background")->set_value(splash_texture);
render::material_property<float4>* splash_tint = splash_billboard_material.add_property<float4>("tint");

+ 50
- 0
src/game/system/metamorphosis.cpp View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
*/
#include "game/system/metamorphosis.hpp"
#include "entity/id.hpp"
namespace game {
namespace system {
metamorphosis::metamorphosis(entity::registry& registry):
updatable(registry),
time_scale(1.0f)
{}
void metamorphosis::update(double t, double dt)
{
// registry.view<component::egg>().each(
// [&](entity::id entity_id, auto& component)
// {
// });
// registry.view<component::larva>().each(
// [&](entity::id entity_id, auto& component)
// {
// });
}
void metamorphosis::set_time_scale(float scale)
{
time_scale = scale;
}
} // namespace system
} // namespace game

src/game/system/proteome.hpp → src/game/system/metamorphosis.hpp View File

@ -17,40 +17,33 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_SYSTEM_PROTEOME_HPP
#define ANTKEEPER_GAME_SYSTEM_PROTEOME_HPP
#ifndef ANTKEEPER_GAME_SYSTEM_METAMORPHOSIS_HPP
#define ANTKEEPER_GAME_SYSTEM_METAMORPHOSIS_HPP
#include "game/system/updatable.hpp"
#include "game/component/genome.hpp"
#include "entity/id.hpp"
namespace game {
namespace system {
/**
* Generates proteomes for every genome.
*/
class proteome:
class metamorphosis:
public updatable
{
public:
proteome(entity::registry& registry);
~proteome();
metamorphosis(entity::registry& registry);
virtual void update(double t, double dt);
/**
* Scales then adds the timestep `dt` to the current time, then recalculates the positions of proteomeing bodies.
* Sets the factor by which the timestep `dt` will be scaled.
*
* @param t Time, in seconds.
* @param dt Delta time, in seconds.
* @param scale Factor by which to scale the timestep.
*/
virtual void update(double t, double dt);
void set_time_scale(float scale);
private:
void on_genome_construct(entity::registry& registry, entity::id entity_id);
void on_genome_update(entity::registry& registry, entity::id entity_id);
float time_scale;
};
} // namespace system
} // namespace game
#endif // ANTKEEPER_GAME_SYSTEM_PROTEOME_HPP
#endif // ANTKEEPER_GAME_SYSTEM_METAMORPHOSIS_HPP

+ 1
- 17
src/game/system/morphogenesis.hpp View File

@ -26,29 +26,13 @@ namespace game {
namespace system {
/**
* Constructs the forms of organisms from their genetic codes.
*
* Algorithm:
*
* 1. Consider inital egg cell as a sphere at the origin.
* 2. Use the marching cubes algorithm to generate a mesh from the isosurface formed by the sum of all cells.
* 3. Generate evenly-distributed points across surface area of the mesh (Poisson sample?).
* 4. For each point, evaluate morphogenic genes to determine type, direction, and color of the next cell.
* 5. Create new cells given the output of step 4.
* 6. Go to step 2 and repeat.
* Generates 3D models from genomes.
*/
class morphogenesis:
public updatable
{
public:
morphogenesis(entity::registry& registry);
/**
* Scales then adds the timestep `dt` to the current time, then recalculates the positions of morphogenesising bodies.
*
* @param t Time, in seconds.
* @param dt Delta time, in seconds.
*/
virtual void update(double t, double dt);
private:

+ 0
- 82
src/game/system/proteome.cpp View File

@ -1,82 +0,0 @@
/*
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
*/
#include "game/system/proteome.hpp"
#include "game/component/proteome.hpp"
#include "genetics/sequence.hpp"
#include "genetics/standard-code.hpp"
namespace game {
namespace system {
proteome::proteome(entity::registry& registry):
updatable(registry)
{
registry.on_construct<game::component::genome>().connect<&proteome::on_genome_construct>(this);
registry.on_update<game::component::genome>().connect<&proteome::on_genome_update>(this);
}
proteome::~proteome()
{
registry.on_construct<game::component::genome>().disconnect<&proteome::on_genome_construct>(this);
registry.on_update<game::component::genome>().disconnect<&proteome::on_genome_update>(this);
}
void proteome::update(double t, double dt)
{}
void proteome::on_genome_construct(entity::registry& registry, entity::id entity_id)
{
on_genome_update(registry, entity_id);
}
void proteome::on_genome_update(entity::registry& registry, entity::id entity_id)
{
game::component::genome& genome = registry.get<game::component::genome>(entity_id);
// Allocate a proteome component
game::component::proteome proteome_component;
// For each chromosome in the genome
for (const std::string& chromosome: genome.chromosomes)
{
// Find the first ORF in the chromosome
auto orf = genetics::sequence::find_orf(chromosome.begin(), chromosome.end(), genetics::standard_code);
// While the ORF is valid
while (orf.start != chromosome.end())
{
// Translate the base sequence into an amino acid sequence (protein)
std::string protein;
genetics::sequence::translate(orf.start, orf.stop, std::back_inserter(protein), genetics::standard_code);
// Append protein to the proteome
proteome_component.proteins.push_back(protein);
// Find the next ORF
orf = genetics::sequence::find_orf(orf.stop, chromosome.end(), genetics::standard_code);
}
}
// Assign or replace the entity's proteome component
registry.emplace_or_replace<game::component::proteome>(entity_id, proteome_component);
}
} // namespace system
} // namespace game

+ 6
- 1
src/game/world.cpp View File

@ -418,6 +418,12 @@ void create_sun(game::context& ctx)
// Create sun directional light scene object
scene::directional_light* sun_light = new scene::directional_light();
sun_light->set_color({0, 0, 0});
sun_light->set_shadow_caster(true);
sun_light->set_shadow_framebuffer(ctx.shadow_map_framebuffer);
sun_light->set_shadow_bias(1.0f);
sun_light->set_shadow_cascade_count(4);
sun_light->set_shadow_cascade_coverage(0.1f);
sun_light->set_shadow_cascade_distribution(0.8f);
sun_light->update_tweens();
// Create sky ambient light scene object
@ -437,7 +443,6 @@ void create_sun(game::context& ctx)
//ctx.surface_scene->add_object(bounce_light);
// Pass direct sun light scene object to shadow map pass and astronomy system
ctx.surface_shadow_map_pass->set_light(sun_light);
ctx.astronomy_system->set_sun_light(sun_light);
ctx.astronomy_system->set_sky_light(sky_light);
ctx.astronomy_system->set_bounce_light(bounce_light);

+ 42
- 0
src/render/blend-mode.hpp View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_RENDER_BLEND_MODE_HPP
#define ANTKEEPER_RENDER_BLEND_MODE_HPP
namespace render {
/**
* Material blend modes.
*/
enum class blend_mode
{
/// Fully opaque.
opaque,
/// Binary masked opacity.
masked,
/// Translucent.
translucent
};
} // namespace render
#endif // ANTKEEPER_RENDER_BLEND_MODE_HPP

+ 0
- 7
src/render/material-flags.hpp View File

@ -20,13 +20,6 @@
#ifndef ANTKEEPER_MATERIAL_FLAGS_HPP
#define ANTKEEPER_MATERIAL_FLAGS_HPP
#define MATERIAL_FLAG_OPAQUE 0x00
#define MATERIAL_FLAG_TRANSLUCENT 0x01
#define MATERIAL_FLAG_FRONT_FACES 0x00
#define MATERIAL_FLAG_BACK_FACES 0x02
#define MATERIAL_FLAG_FRONT_AND_BACK_FACES 0x04
#define MATERIAL_FLAG_SHADOW_CASTER 0x00
#define MATERIAL_FLAG_NOT_SHADOW_CASTER 0x08
#define MATERIAL_FLAG_X_RAY 0x10
#define MATERIAL_FLAG_OUTLINE 0x20
#define MATERIAL_FLAG_VEGETATION 0x40

+ 30
- 2
src/render/material.cpp View File

@ -23,7 +23,11 @@ namespace render {
material::material(gl::shader_program* program):
program(program),
flags(0)
flags(0),
blend_mode(blend_mode::opaque),
opacity_threshold(0.5f),
two_sided(false),
shadow_mode(shadow_mode::opaque)
{}
material::material():
@ -55,6 +59,10 @@ material& material::operator=(const material& other)
this->program = other.program;
this->flags = other.flags;
this->blend_mode = other.blend_mode;
this->opacity_threshold = other.opacity_threshold;
this->two_sided = other.two_sided;
this->shadow_mode = other.shadow_mode;
for (auto it = other.property_map.begin(); it != other.property_map.end(); ++it)
{
material_property_base* property = it->second->clone();
@ -99,11 +107,31 @@ void material::set_shader_program(gl::shader_program* program)
reconnect_properties();
}
void material::set_flags(std::uint32_t flags)
void material::set_flags(std::uint32_t flags) noexcept
{
this->flags = flags;
}
void material::set_blend_mode(render::blend_mode mode) noexcept
{
blend_mode = mode;
}
void material::set_opacity_threshold(float threshold) noexcept
{
opacity_threshold = threshold;
}
void material::set_two_sided(bool two_sided) noexcept
{
this->two_sided = two_sided;
}
void material::set_shadow_mode(render::shadow_mode mode) noexcept
{
shadow_mode = mode;
}
std::size_t material::reconnect_properties()
{
std::size_t disconnected_property_count = properties.size();

+ 72
- 6
src/render/material.hpp View File

@ -20,7 +20,9 @@
#ifndef ANTKEEPER_RENDER_MATERIAL_HPP
#define ANTKEEPER_RENDER_MATERIAL_HPP
#include "render/blend-mode.hpp"
#include "render/material-property.hpp"
#include "render/shadow-mode.hpp"
#include "gl/shader-program.hpp"
#include <cstdint>
#include <cstddef>
@ -93,7 +95,37 @@ public:
*
* @param flags Material flags.
*/
void set_flags(std::uint32_t flags);
void set_flags(std::uint32_t flags) noexcept;
/**
* Sets the material blend mode.
*
* @param mode Blend mode.
*/
void set_blend_mode(blend_mode mode) noexcept;
/**
* Sets the opacity mask threshold value for masked blend mode.
*
* @param threshold Opacity mask threshold value, above which the surface is considered opaque.
*
* @see render::blend_mode::masked
*/
void set_opacity_threshold(float threshold) noexcept;
/**
* Enables or disables back-face culling of the material surface.
*
* @param two_sided `true` to disable back-face culling, or `false` to enable it.
*/
void set_two_sided(bool two_sided) noexcept;
/**
* Sets the material shadow mode.
*
* @param mode Shadow mode.
*/
void set_shadow_mode(shadow_mode mode) noexcept;
/**
* Adds a material array property to the material.
@ -110,10 +142,20 @@ public:
*/
gl::shader_program* get_shader_program() const;
/**
* Returns the material flags.
*/
std::uint32_t get_flags() const;
/// Returns the material flags.
std::uint32_t get_flags() const noexcept;
/// Returns the material blend mode.
blend_mode get_blend_mode() const noexcept;
/// Returns the opacity mask threshold value.
float get_opacity_threshold() const noexcept;
/// Returns `true` if the material surface is two-sided, and `false` otherwise.
bool is_two_sided() const noexcept;
/// Returns the material shadow mode.
shadow_mode get_shadow_mode() const noexcept;
/**
* Returns the material property with the specified name, or `nullptr` if the material could not be found.
@ -135,6 +177,10 @@ private:
gl::shader_program* program;
std::uint32_t flags;
blend_mode blend_mode;
float opacity_threshold;
bool two_sided;
shadow_mode shadow_mode;
std::list<material_property_base*> properties;
std::unordered_map<std::string, material_property_base*> property_map;
};
@ -163,11 +209,31 @@ inline gl::shader_program* material::get_shader_program() const
return program;
}
inline std::uint32_t material::get_flags() const
inline std::uint32_t material::get_flags() const noexcept
{
return flags;
}
inline blend_mode material::get_blend_mode() const noexcept
{
return blend_mode;
}
inline float material::get_opacity_threshold() const noexcept
{
return opacity_threshold;
}
inline bool material::is_two_sided() const noexcept
{
return two_sided;
}
inline shadow_mode material::get_shadow_mode() const noexcept
{
return shadow_mode;
}
inline material_property_base* material::get_property(const std::string& name) const
{
if (auto it = property_map.find(name); it != property_map.end())

+ 84
- 65
src/render/passes/material-pass.cpp View File

@ -47,8 +47,6 @@
#include <cmath>
#include <glad/glad.h>
#include "render/passes/shadow-map-pass.hpp"
namespace render {
static bool operation_compare(const render::operation& a, const render::operation& b);
@ -56,9 +54,7 @@ static bool operation_compare(const render::operation& a, const render::operatio
material_pass::material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager):
pass(rasterizer, framebuffer),
fallback_material(nullptr),
mouse_position({0.0f, 0.0f}),
shadow_map_pass(nullptr),
shadow_map(nullptr)
mouse_position({0.0f, 0.0f})
{
max_ambient_light_count = MATERIAL_PASS_MAX_AMBIENT_LIGHT_COUNT;
max_point_light_count = MATERIAL_PASS_MAX_POINT_LIGHT_COUNT;
@ -139,12 +135,18 @@ void material_pass::render(const render::context& ctx, render::queue& queue) con
const gl::shader_program* active_shader_program = nullptr;
const render::material* active_material = nullptr;
const parameter_set* parameters = nullptr;
blend_mode active_blend_mode = blend_mode::opaque;
bool active_two_sided = false;
// Reset light counts
ambient_light_count = 0;
point_light_count = 0;
directional_light_count = 0;
spot_light_count = 0;
const gl::texture_2d* shadow_map_texture = nullptr;
unsigned int shadow_cascade_count = 0;
const float* shadow_splits_directional = nullptr;
const float4x4* shadow_matrices_directional = nullptr;
// Collect lights
const std::list<scene::object_base*>* lights = ctx.collection->get_objects(scene::light::object_type_id);
@ -199,6 +201,14 @@ void material_pass::render(const render::context& ctx, render::queue& queue) con
float3 direction = static_cast<const scene::directional_light*>(light)->get_direction_tween().interpolate(ctx.alpha);
directional_light_directions[directional_light_count] = direction;
if (directional_light->is_shadow_caster())
{
if (directional_light->get_shadow_framebuffer())
shadow_map_texture = directional_light->get_shadow_framebuffer()->get_depth_attachment();
shadow_cascade_count = directional_light->get_shadow_cascade_count();
shadow_splits_directional = directional_light->get_shadow_cascade_distances();
shadow_matrices_directional = directional_light->get_shadow_cascade_matrices();
}
if (directional_light->get_light_texture())
{
@ -255,19 +265,6 @@ void material_pass::render(const render::context& ctx, render::queue& queue) con
}
}
float4x4 shadow_matrices_directional[4];
float4 shadow_splits_directional;
if (shadow_map_pass)
{
for (int i = 0; i < 4; ++i)
shadow_matrices_directional[i] = shadow_map_pass->get_shadow_matrices()[i];
// Calculate shadow map split distances
for (int i = 0; i < 4; ++i)
shadow_splits_directional[i] = shadow_map_pass->get_split_distances()[i + 1];
}
// Sort render queue
queue.sort(operation_compare);
@ -294,49 +291,43 @@ void material_pass::render(const render::context& ctx, render::queue& queue) con
{
active_material = material;
// Change rasterizer state according to material flags
std::uint32_t material_flags = active_material->get_flags();
if (active_material_flags != material_flags)
// Set blend mode
const blend_mode material_blend_mode = active_material->get_blend_mode();
if (material_blend_mode != active_blend_mode)
{
if ((material_flags & MATERIAL_FLAG_TRANSLUCENT) != (active_material_flags & MATERIAL_FLAG_TRANSLUCENT))
if (material_blend_mode == blend_mode::translucent)
{
if (material_flags & MATERIAL_FLAG_TRANSLUCENT)
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
else
{
glDisable(GL_BLEND);
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
if ((material_flags & MATERIAL_FLAG_BACK_FACES) != (active_material_flags & MATERIAL_FLAG_BACK_FACES))
else if (active_blend_mode == blend_mode::translucent && (material_blend_mode == blend_mode::opaque || material_blend_mode == blend_mode::masked))
{
if (material_flags & MATERIAL_FLAG_BACK_FACES)
{
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
}
else
{
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
}
glDisable(GL_BLEND);
}
else if ((material_flags & MATERIAL_FLAG_FRONT_AND_BACK_FACES) != (active_material_flags & MATERIAL_FLAG_FRONT_AND_BACK_FACES))
active_blend_mode = material_blend_mode;
}
// Set back-face culling mode
const bool material_two_sided = active_material->is_two_sided();
if (material_two_sided != active_two_sided)
{
if (material_two_sided)
{
if (material_flags & MATERIAL_FLAG_FRONT_AND_BACK_FACES)
{
glDisable(GL_CULL_FACE);
}
else
{
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
}
glDisable(GL_CULL_FACE);
}
else
{
glEnable(GL_CULL_FACE);
}
active_two_sided = material_two_sided;
}
// Change rasterizer state according to material flags
std::uint32_t material_flags = active_material->get_flags();
if (active_material_flags != material_flags)
{
if ((material_flags & MATERIAL_FLAG_X_RAY) != (active_material_flags & MATERIAL_FLAG_X_RAY))
{
if (material_flags & MATERIAL_FLAG_X_RAY)
@ -476,6 +467,13 @@ void material_pass::render(const render::context& ctx, render::queue& queue) con
if (parameters->directional_light_texture_opacities)
parameters->directional_light_texture_opacities->upload(0, directional_light_texture_opacities, directional_light_count);
if (parameters->shadow_map_directional && shadow_map_texture)
parameters->shadow_map_directional->upload(shadow_map_texture);
if (parameters->shadow_matrices_directional)
parameters->shadow_matrices_directional->upload(0, shadow_matrices_directional, shadow_cascade_count);
if (parameters->shadow_splits_directional)
parameters->shadow_splits_directional->upload(0, shadow_splits_directional, shadow_cascade_count);
if (parameters->spot_light_count)
parameters->spot_light_count->upload(spot_light_count);
if (parameters->spot_light_colors)
@ -488,13 +486,6 @@ void material_pass::render(const render::context& ctx, render::queue& queue) con
parameters->spot_light_attenuations->upload(0, spot_light_attenuations, spot_light_count);
if (parameters->spot_light_cutoffs)
parameters->spot_light_cutoffs->upload(0, spot_light_cutoffs, spot_light_count);
if (parameters->shadow_map_directional && shadow_map)
parameters->shadow_map_directional->upload(shadow_map);
if (parameters->shadow_matrices_directional)
parameters->shadow_matrices_directional->upload(0, shadow_matrices_directional, 4);
if (parameters->shadow_splits_directional)
parameters->shadow_splits_directional->upload(shadow_splits_directional);
}
// Upload material properties to shader
@ -608,6 +599,9 @@ bool operation_compare(const render::operation& a, const render::operation& b)
bool xray_a = a.material->get_flags() & MATERIAL_FLAG_X_RAY;
bool xray_b = b.material->get_flags() & MATERIAL_FLAG_X_RAY;
const bool two_sided_a = (a.material) ? a.material->is_two_sided() : false;
const bool two_sided_b = (b.material) ? b.material->is_two_sided() : false;
if (xray_a)
{
if (xray_b)
@ -631,8 +625,8 @@ bool operation_compare(const render::operation& a, const render::operation& b)
else
{
// Determine transparency
bool transparent_a = a.material->get_flags() & MATERIAL_FLAG_TRANSLUCENT;
bool transparent_b = b.material->get_flags() & MATERIAL_FLAG_TRANSLUCENT;
bool transparent_a = a.material->get_blend_mode() == blend_mode::translucent;
bool transparent_b = b.material->get_blend_mode() == blend_mode::translucent;
if (transparent_a)
{
@ -695,8 +689,33 @@ bool operation_compare(const render::operation& a, const render::operation& b)
}
else
{
// Sort by VAO
return (a.vertex_array < b.vertex_array);
// A and B have different VAOs, sort by two-sided
if (two_sided_a)
{
if (two_sided_b)
{
// A and B are both two-sided, sort by VAO
return (a.vertex_array < b.vertex_array);
}
else
{
// A is two-sided, B is one-sided. Render B first
return false;
}
}
else
{
if (two_sided_b)
{
// A is one-sided, B is two-sided. Render A first
return true;
}
else
{
// A and B are both one-sided, sort by VAO
return (a.vertex_array < b.vertex_array);
}
}
}
}
else

+ 0
- 5
src/render/passes/material-pass.hpp View File

@ -34,8 +34,6 @@ class resource_manager;
namespace render {
class shadow_map_pass;
/**
* Renders scene objects using their material-specified shaders and properties.
*/
@ -50,9 +48,6 @@ public:
/// 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);
const render::shadow_map_pass* shadow_map_pass;
const gl::texture_2d* shadow_map;
private:
virtual void handle_event(const mouse_moved_event& event);

+ 149
- 70
src/render/passes/shadow-map-pass.cpp View File

@ -26,12 +26,13 @@
#include "gl/drawing-mode.hpp"
#include "render/context.hpp"
#include "render/material.hpp"
#include "render/material-flags.hpp"
#include "scene/camera.hpp"
#include "scene/collection.hpp"
#include "scene/light.hpp"
#include "geom/view-frustum.hpp"
#include "geom/aabb.hpp"
#include "config.hpp"
#include "math/interpolation.hpp"
#include "math/vector.hpp"
#include "math/matrix.hpp"
#include "math/quaternion.hpp"
@ -43,28 +44,8 @@ namespace render {
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)
{
// Calculate split distances
for (std::size_t i = 0; i < split_count; ++i)
{
float part = static_cast<float>(i + 1) / static_cast<float>(split_count + 1);
// Calculate uniform split distance
float uniform_split_distance = near + (far - near) * part;
// Calculate logarithmic split distance
float log_split_distance = near * std::pow(far / near, part);
// Interpolate between uniform and logarithmic split distances
split_distances[i] = log_split_distance * split_scheme + uniform_split_distance * (1.0f - split_scheme);
}
}
shadow_map_pass::shadow_map_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager):
pass(rasterizer, framebuffer),
split_scheme_weight(0.5f),
light(nullptr)
shadow_map_pass::shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager):
pass(rasterizer, nullptr)
{
// Load skinned shader program
unskinned_shader_program = resource_manager->load<gl::shader_program>("depth-unskinned.glsl");
@ -91,13 +72,36 @@ shadow_map_pass::~shadow_map_pass()
void shadow_map_pass::render(const render::context& ctx, render::queue& queue) const
{
// Abort if no directional light was set
if (!light)
// Collect lights
const std::list<scene::object_base*>* lights = ctx.collection->get_objects(scene::light::object_type_id);
for (const scene::object_base* object: *lights)
{
return;
// Ignore inactive lights
if (!object->is_active())
continue;
// Ignore non-directional lights
const scene::light* light = static_cast<const scene::light*>(object);
if (light->get_light_type() != scene::light_type::directional)
continue;
// Ignore non-shadow casters
const scene::directional_light* directional_light = static_cast<const scene::directional_light*>(light);
if (!directional_light->is_shadow_caster())
continue;
// Ignore improperly-configured lights
if (!directional_light->get_shadow_cascade_count() || !directional_light->get_shadow_framebuffer())
continue;
// Render cascaded shadow maps for light
render_csm(*directional_light, ctx, queue);
}
rasterizer->use_framebuffer(*framebuffer);
}
void shadow_map_pass::render_csm(const scene::directional_light& light, const render::context& ctx, render::queue& queue) const
{
rasterizer->use_framebuffer(*light.get_shadow_framebuffer());
// Disable blending
glDisable(GL_BLEND);
@ -107,9 +111,10 @@ void shadow_map_pass::render(const render::context& ctx, render::queue& queue) c
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
// Disable face culling
// Enable back-face culling
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
glCullFace(GL_BACK);
bool two_sided = false;
// For half-z buffer
//glDepthRange(-1.0f, 1.0f);
@ -117,30 +122,48 @@ void shadow_map_pass::render(const render::context& ctx, render::queue& queue) c
// Get 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(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);
// Get distances to camera depth clipping planes
const float camera_clip_near = camera.get_clip_near_tween().interpolate(ctx.alpha);
const float camera_clip_far = camera.get_clip_far_tween().interpolate(ctx.alpha);
// Calculate distance to shadow cascade depth clipping planes
const float shadow_clip_far = math::lerp(camera_clip_near, camera_clip_far, light.get_shadow_cascade_coverage());
const unsigned int cascade_count = light.get_shadow_cascade_count();
float* cascade_distances = light.get_shadow_cascade_distances();
float4x4* cascade_matrices = light.get_shadow_cascade_matrices();
// Calculate cascade far clipping plane distances
cascade_distances[cascade_count - 1] = shadow_clip_far;
for (unsigned int i = 0; i < cascade_count - 1; ++i)
{
const float weight = static_cast<float>(i + 1) / static_cast<float>(cascade_count);
// Calculate linear and logarithmic distribution distances
const float linear_distance = math::lerp(camera_clip_near, shadow_clip_far, weight);
const float log_distance = math::log_lerp(camera_clip_near, shadow_clip_far, weight);
// Interpolate between linear and logarithmic distribution distances
cascade_distances[i] = math::lerp(linear_distance, log_distance, light.get_shadow_cascade_distribution());
}
// Calculate viewports for each shadow map
const int shadow_map_resolution = std::get<0>(framebuffer->get_dimensions()) / 2;
float4 shadow_map_viewports[4];
const int shadow_map_resolution = std::get<0>(light.get_shadow_framebuffer()->get_dimensions()) / 2;
int4 shadow_map_viewports[4];
for (int i = 0; i < 4; ++i)
{
int x = i % 2;
int y = i / 2;
float4& viewport = shadow_map_viewports[i];
viewport[0] = static_cast<float>(x * shadow_map_resolution);
viewport[1] = static_cast<float>(y * shadow_map_resolution);
viewport[2] = static_cast<float>(shadow_map_resolution);
viewport[3] = static_cast<float>(shadow_map_resolution);
int4& viewport = shadow_map_viewports[i];
viewport[0] = x * shadow_map_resolution;
viewport[1] = y * shadow_map_resolution;
viewport[2] = shadow_map_resolution;
viewport[3] = shadow_map_resolution;
}
// Calculate a view-projection matrix from the directional light's transform
math::transform<float> light_transform = light->get_transform_tween().interpolate(ctx.alpha);
math::transform<float> light_transform = light.get_transform_tween().interpolate(ctx.alpha);
float3 forward = light_transform.rotation * config::global_forward;
float3 up = light_transform.rotation * config::global_up;
float4x4 light_view = math::look_at(light_transform.translation, light_transform.translation + forward, up);
@ -159,15 +182,15 @@ void shadow_map_pass::render(const render::context& ctx, render::queue& queue) c
gl::shader_program* active_shader_program = nullptr;
for (int i = 0; i < 4; ++i)
for (unsigned int i = 0; i < cascade_count; ++i)
{
// Set viewport for this shadow map
const float4& viewport = shadow_map_viewports[i];
const int4& viewport = shadow_map_viewports[i];
rasterizer->set_viewport(viewport[0], viewport[1], viewport[2], viewport[3]);
// Calculate projection matrix for view camera subfrustum
const float subfrustum_near = split_distances[i];
const float subfrustum_far = split_distances[i + 1];
const float subfrustum_near = (i) ? cascade_distances[i - 1] : camera_clip_near;
const float subfrustum_far = cascade_distances[i];
float4x4 subfrustum_projection = math::perspective_half_z(camera.get_fov(), camera.get_aspect_ratio(), subfrustum_near, subfrustum_far);
// Calculate view camera subfrustum
@ -216,16 +239,31 @@ void shadow_map_pass::render(const render::context& ctx, render::queue& queue) c
crop_matrix = math::translate(math::matrix4<float>::identity(), offset) * math::scale(math::matrix4<float>::identity(), scale);
cropped_view_projection = crop_matrix * light_view_projection;
// Calculate shadow matrix
shadow_matrices[i] = bias_tile_matrices[i] * cropped_view_projection;
// Calculate world-space to cascade texture-space transformation matrix
cascade_matrices[i] = bias_tile_matrices[i] * cropped_view_projection;
for (const render::operation& operation: queue)
{
// Skip materials which don't cast shadows
const render::material* material = operation.material;
if (material && (material->get_flags() & MATERIAL_FLAG_NOT_SHADOW_CASTER))
if (material)
{
continue;
// Skip materials which don't cast shadows
if (material->get_shadow_mode() == shadow_mode::none)
continue;
if (material->is_two_sided() != two_sided)
{
if (material->is_two_sided())
{
glDisable(GL_CULL_FACE);
}
else
{
glEnable(GL_CULL_FACE);
}
two_sided = material->is_two_sided();
}
}
// Switch shader programs if necessary
@ -255,28 +293,44 @@ void shadow_map_pass::render(const render::context& ctx, render::queue& queue) c
}
}
void shadow_map_pass::set_split_scheme_weight(float weight)
{
split_scheme_weight = weight;
}
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)
{
// Determine transparency
bool skinned_a = (a.bone_count);
bool skinned_b = (b.bone_count);
const bool skinned_a = (a.bone_count);
const bool skinned_b = (b.bone_count);
const bool two_sided_a = (a.material) ? a.material->is_two_sided() : false;
const bool two_sided_b = (b.material) ? b.material->is_two_sided() : false;
if (skinned_a)
{
if (skinned_b)
{
// A and B are both skinned, sort by VAO
return (a.vertex_array < b.vertex_array);
// A and B are both skinned, sort by two-sided
if (two_sided_a)
{
if (two_sided_b)
{
// A and B are both two-sided, sort by VAO
return (a.vertex_array < b.vertex_array);
}
else
{
// A is two-sided, B is one-sided. Render B first
return false;
}
}
else
{
if (two_sided_b)
{
// A is one-sided, B is two-sided. Render A first
return true;
}
else
{
// A and B are both one-sided, sort by VAO
return (a.vertex_array < b.vertex_array);
}
}
}
else
{
@ -293,8 +347,33 @@ bool operation_compare(const render::operation& a, const render::operation& b)
}
else
{
// A and B are both unskinned, sort by VAO
return (a.vertex_array < b.vertex_array);
// A and B are both unskinned, sort by two-sided
if (two_sided_a)
{
if (two_sided_b)
{
// A and B are both two-sided, sort by VAO
return (a.vertex_array < b.vertex_array);
}
else
{
// A is two-sided, B is one-sided. Render B first
return false;
}
}
else
{
if (two_sided_b)
{
// A is one-sided, B is two-sided. Render A first
return true;
}
else
{
// A and B are both one-sided, sort by VAO
return (a.vertex_array < b.vertex_array);
}
}
}
}
}

+ 22
- 32
src/render/passes/shadow-map-pass.hpp View File

@ -31,38 +31,42 @@ class resource_manager;
namespace render {
/**
*
* Renders shadow maps.
*/
class shadow_map_pass: public pass
{
public:
shadow_map_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager);
virtual ~shadow_map_pass();
virtual void render(const render::context& ctx, render::queue& queue) const final;
/**
* Sets the linear interpolation weight between uniform and logarithmic frustum-splitting schemes.
* Constructs a shadow map pass.
*
* @param weight Linear interpolation weight between uniform and logarithmic frustum-splitting schemes. A value of `0.0` indicates a uniform split scheme, while `1.0` indicates a logarithmic split scheme.
* @param rasterizer Rasterizer.
* @param framebuffer Shadow map framebuffer.
* @param resource_manage Resource manager.
*/
void set_split_scheme_weight(float weight);
shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager);
void set_light(const scene::directional_light* light);
/**
* Destructs a shadow map pass.
*/
virtual ~shadow_map_pass();
const float4x4* get_shadow_matrices() const;
const float* get_split_distances() const;
/**
* Renders shadow maps for a single camera.
*
* @param ctx Render context.
* @param queue Render queue.
*/
virtual void render(const render::context& ctx, render::queue& queue) const final;
private:
/**
* Calculates the distances along the depth axis at which a view-frustum should be split, given a frustum-splitting scheme.
* Renders cascaded shadow maps for a single directional light.
*
* @param[out] split_distances Array containing the distances to each split.
* @param split_count Number of times the frustum should be split.
* @param split_scheme Linear interpolation weight between uniform and logarithmic frustum-splitting schemes. A value of `0.0` indicates a uniform split scheme, while `1.0` indicates a logarithmic split scheme.
* @param near Distance to the near clipping plane of the frustum to be split.
* @param far Distance to the far clipping plane of the frustum to be split.
* @param light Shadow-casting directional light.
* @param ctx Render context.
* @param queue Render queue.
*/
static void distribute_frustum_splits(float* split_distances, std::size_t split_count, float split_scheme, float near, float far);
void render_csm(const scene::directional_light& light, const render::context& ctx, render::queue& queue) const;
gl::shader_program* unskinned_shader_program;
const gl::shader_input* unskinned_model_view_projection_input;
@ -70,23 +74,9 @@ private:
gl::shader_program* skinned_shader_program;
const gl::shader_input* skinned_model_view_projection_input;
mutable float split_distances[5];
mutable float4x4 shadow_matrices[4];
float4x4 bias_tile_matrices[4];
float split_scheme_weight;
const scene::directional_light* light;
};
inline const float4x4* shadow_map_pass::get_shadow_matrices() const
{
return shadow_matrices;
}
inline const float* shadow_map_pass::get_split_distances() const
{
return split_distances;
}
} // namespace render
#endif // ANTKEEPER_RENDER_SHADOW_MAP_PASS_HPP

src/game/component/select.hpp → src/render/shadow-mode.hpp View File

@ -17,20 +17,23 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_COMPONENT_SELECT_HPP
#define ANTKEEPER_GAME_COMPONENT_SELECT_HPP
#ifndef ANTKEEPER_RENDER_SHADOW_MODE_HPP
#define ANTKEEPER_RENDER_SHADOW_MODE_HPP
namespace game {
namespace component {
namespace render {
/// Allows an entity to be selected.
struct select
/**
* Material shadow casting modes.
*/
enum class shadow_mode
{
/// Radius of selection bounds.
float radius;
/// Fully opaque shadow casting.
opaque,
/// No shadows cast.
none
};
} // namespace component
} // namespace game
} // namespace render
#endif // ANTKEEPER_GAME_COMPONENT_SELECT_HPP
#endif // ANTKEEPER_RENDER_SHADOW_MODE_HPP

+ 32
- 18
src/resources/material-loader.cpp View File

@ -286,28 +286,42 @@ render::material* resource_loader::load(resource_manager* reso
// Read blend mode
std::string blend_mode;
read_value(&blend_mode, json, "blend_mode");
if (blend_mode == "alpha_blend")
flags |= MATERIAL_FLAG_TRANSLUCENT;
else
flags |= MATERIAL_FLAG_OPAQUE;
if (blend_mode == "opaque")
{
material->set_blend_mode(render::blend_mode::opaque);
}
else if (blend_mode == "masked")
{
material->set_blend_mode(render::blend_mode::masked);
}
else if (blend_mode == "translucent")
{
material->set_blend_mode(render::blend_mode::translucent);
}
// Read opacity threshold
float opacity_threshold = 0.5f;
if (read_value(&opacity_threshold, json, "opacity_threshold"))
{
material->set_opacity_threshold(opacity_threshold);
}
// Read two sided
bool two_sided = false;
read_value(&two_sided, json, "two_sided");
material->set_two_sided(two_sided);
// Read shadow mode
std::string shadow_mode;
read_value(&shadow_mode, json, "shadow_mode");
if (shadow_mode == "none")
flags |= MATERIAL_FLAG_NOT_SHADOW_CASTER;
else
flags |= MATERIAL_FLAG_SHADOW_CASTER;
// Read cull mode
std::string cull_mode;
read_value(&cull_mode, json, "cull_mode");
if (cull_mode == "none")
flags |= MATERIAL_FLAG_FRONT_AND_BACK_FACES;
else if (cull_mode == "front")
flags |= MATERIAL_FLAG_BACK_FACES;
else
flags |= MATERIAL_FLAG_FRONT_FACES;
if (shadow_mode == "opaque")
{
material->set_shadow_mode(render::shadow_mode::opaque);
}
else if (shadow_mode == "none")
{
material->set_shadow_mode(render::shadow_mode::none);
}
// Read depth mode
std::string depth_mode;

+ 42
- 1
src/scene/directional-light.cpp View File

@ -33,10 +33,51 @@ static float3 interpolate_direction(const float3& x, const float3& y, float a)
directional_light::directional_light():
direction(config::global_forward, interpolate_direction),
shadow_caster(false),
shadow_framebuffer(nullptr),
shadow_bias(1.0f),
shadow_cascade_count(4),
shadow_cascade_coverage(1.0f),
shadow_cascade_distribution(0.8f),
light_texture(nullptr),
light_texture_opacity(1.0f, math::lerp<float, float>),
light_texture_scale({1.0f, 1.0f}, math::lerp<float2, float>)
{}
{
shadow_cascade_distances.resize(shadow_cascade_count);
shadow_cascade_matrices.resize(shadow_cascade_count);
}
void directional_light::set_shadow_caster(bool caster) noexcept
{
shadow_caster = caster;
}
void directional_light::set_shadow_framebuffer(const gl::framebuffer* framebuffer) noexcept
{
shadow_framebuffer = framebuffer;
}
void directional_light::set_shadow_bias(float bias) noexcept
{
shadow_bias = bias;
}
void directional_light::set_shadow_cascade_count(unsigned int count) noexcept
{
shadow_cascade_count = count;
shadow_cascade_distances.resize(shadow_cascade_count);
shadow_cascade_matrices.resize(shadow_cascade_count);
}
void directional_light::set_shadow_cascade_coverage(float factor) noexcept
{
shadow_cascade_coverage = factor;
}
void directional_light::set_shadow_cascade_distribution(float weight) noexcept
{
shadow_cascade_distribution = weight;
}
void directional_light::set_light_texture(const gl::texture_2d* texture)
{

+ 118
- 0
src/scene/directional-light.hpp View File

@ -23,6 +23,7 @@
#include "scene/light.hpp"
#include "gl/texture-2d.hpp"
#include "utility/fundamental-types.hpp"
#include <vector>
namespace scene {
@ -35,6 +36,48 @@ public:
/// Creates a directional light.
directional_light();
/**
* Enables or disables shadow casting.
*
* @param caster `true` if the light should cast shadows, `false` otherwise.
*/
void set_shadow_caster(bool caster) noexcept;
/**
* Sets the shadow map framebuffer.
*
* @param framebuffer Pointer to a shadow map framebuffer.
*/
void set_shadow_framebuffer(const gl::framebuffer* framebuffer) noexcept;
/**
* Sets the shadow bias factor for reducing self-shadowing.
*
* @param bias Shadow bias factor.
*/
void set_shadow_bias(float bias) noexcept;
/**
* Sets the number of shadow cascades.
*
* @param count Number of shadow cascades.
*/
void set_shadow_cascade_count(unsigned int count) noexcept;
/**
* Sets the shadow cascade coverage factor.
*
* @param factor Percentage of the view frustum clipping range covered by shadow cascades. A value of `1.0` results in full coverage of the view frustum clipping range, `0.5` results in coverage of half of the clipping range, etc.
*/
void set_shadow_cascade_coverage(float factor) noexcept;
/**
* Sets the shadow cascade distribution.
*
* @param weight Linear interpolation weight between uniform and logarithmic cascade distributions. A weight of `0.0` results in a uniform cascade distribution, while `1.0` results in a logarithmic distribution.
*/
void set_shadow_cascade_distribution(float weight) noexcept;
/**
* Sets the light texture, also known as a gobo, cucoloris, or cookie.
*
@ -62,6 +105,30 @@ public:
/// Returns the normalized direction vector of the light.
const float3& get_direction() const;
/// Returns `true` if the light casts shadows, `false` otherwise.
bool is_shadow_caster() const noexcept;
/// Returns the shadow map framebuffer, of `nullptr` if no shadow map framebuffer is set.
const gl::framebuffer* get_shadow_framebuffer() const noexcept;
/// Returns the shadow bias factor.
float get_shadow_bias() const noexcept;
/// Returns the number of shadow cascades.
unsigned int get_shadow_cascade_count() const noexcept;
/// Returns the shadow cascade coverage factor.
float get_shadow_cascade_coverage() const noexcept;
/// Returns the shadow cascade distribution weight.
float get_shadow_cascade_distribution() const noexcept;
/// Returns the array of shadow cascade far clipping plane distances.
float* get_shadow_cascade_distances() const noexcept;
/// Returns the array of world-space to cascade texture-space transformation matrices.
float4x4* get_shadow_cascade_matrices() const noexcept;
/// Returns the light texture for this light, or `nullptr` if no light texture is set.
const gl::texture_2d* get_light_texture() const;
@ -87,9 +154,20 @@ private:
virtual void transformed();
tween<float3> direction;
bool shadow_caster;
const gl::framebuffer* shadow_framebuffer;
float shadow_bias;
unsigned int shadow_cascade_count;
float shadow_cascade_coverage;
float shadow_cascade_distribution;
mutable std::vector<float> shadow_cascade_distances;
mutable std::vector<float4x4> shadow_cascade_matrices;
const gl::texture_2d* light_texture;
tween<float> light_texture_opacity;
tween<float2> light_texture_scale;
};
inline light_type directional_light::get_light_type() const
@ -102,6 +180,46 @@ inline const float3& directional_light::get_direction() const
return direction[1];
}
inline bool directional_light::is_shadow_caster() const noexcept
{
return shadow_caster;
}
inline const gl::framebuffer* directional_light::get_shadow_framebuffer() const noexcept
{
return shadow_framebuffer;
}
inline float directional_light::get_shadow_bias() const noexcept
{
return shadow_bias;
}
inline unsigned int directional_light::get_shadow_cascade_count() const noexcept
{
return shadow_cascade_count;
}
inline float directional_light::get_shadow_cascade_coverage() const noexcept
{
return shadow_cascade_coverage;
}
inline float directional_light::get_shadow_cascade_distribution() const noexcept
{
return shadow_cascade_distribution;
}
inline float* directional_light::get_shadow_cascade_distances() const noexcept
{
return shadow_cascade_distances.data();
}
inline float4x4* directional_light::get_shadow_cascade_matrices() const noexcept
{
return shadow_cascade_matrices.data();
}
inline const gl::texture_2d* directional_light::get_light_texture() const
{
return light_texture;

Loading…
Cancel
Save