From f1d46e10dae7ffe887af5981396ff2f6146a83e8 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Wed, 8 Mar 2023 13:18:32 +0800 Subject: [PATCH] Revise resource management and resource loading. RAII-ify entire codebase. Improve materials and shaders. Optimize and improve all render passes. Make material pass use shader templates to support arbitrary numbers of lights. Add fnv1a data types --- CMakeLists.txt | 1 + src/engine/animation/animation-channel.hpp | 1 + src/engine/animation/bone.hpp | 66 +- src/engine/animation/screen-transition.cpp | 18 +- src/engine/animation/screen-transition.hpp | 12 +- src/engine/animation/skeleton.hpp | 3 +- src/engine/app/input-manager.cpp | 4 +- src/engine/app/input-manager.hpp | 2 +- src/engine/app/sdl/sdl-input-manager.cpp | 8 +- src/engine/app/sdl/sdl-input-manager.hpp | 3 +- src/engine/app/sdl/sdl-window.cpp | 6 +- src/engine/app/sdl/sdl-window.hpp | 5 +- src/engine/app/window-manager.cpp | 4 +- src/engine/app/window-manager.hpp | 2 +- src/engine/config.hpp.in | 9 - src/engine/geom/csg.cpp | 116 - src/engine/geom/csg.hpp | 88 - src/engine/geom/marching-cubes.cpp | 32 +- src/engine/geom/marching-cubes.hpp | 2 +- src/engine/geom/mesh-functions.cpp | 14 +- src/engine/geom/meshes/grid.cpp | 6 +- src/engine/geom/meshes/grid.hpp | 3 +- src/engine/geom/rect-pack.hpp | 41 +- src/engine/gl/buffer-usage.hpp | 4 +- src/engine/gl/color-space.hpp | 12 +- src/engine/gl/drawing-mode.hpp | 5 +- src/engine/gl/element-array-type.hpp | 5 +- src/engine/gl/framebuffer.hpp | 7 +- src/engine/gl/gl.hpp | 21 - src/engine/gl/opengl/gl-shader-variables.cpp | 666 ++++++ src/engine/gl/opengl/gl-shader-variables.hpp | 526 +++++ src/engine/gl/pixel-format.hpp | 36 +- src/engine/gl/pixel-type.hpp | 5 +- src/engine/gl/rasterizer.cpp | 8 +- src/engine/gl/rasterizer.hpp | 18 +- src/engine/gl/shader-input.cpp | 773 ------- src/engine/gl/shader-input.hpp | 202 -- src/engine/gl/shader-object.cpp | 29 +- src/engine/gl/shader-object.hpp | 41 +- src/engine/gl/shader-program.cpp | 254 +-- src/engine/gl/shader-program.hpp | 110 +- src/engine/gl/shader-stage.hpp | 5 +- src/engine/gl/shader-template.cpp | 364 +++ src/engine/{render => gl}/shader-template.hpp | 68 +- src/engine/gl/shader-variable-type.hpp | 8 +- src/engine/gl/shader-variable.cpp | 396 ++++ src/engine/gl/shader-variable.hpp | 195 ++ src/engine/gl/texture-1d.cpp | 4 +- src/engine/gl/texture-1d.hpp | 8 +- src/engine/gl/texture-2d.cpp | 6 +- src/engine/gl/texture-2d.hpp | 10 +- src/engine/gl/texture-3d.cpp | 4 +- src/engine/gl/texture-3d.hpp | 8 +- src/engine/gl/texture-cube.hpp | 5 - src/engine/gl/texture-filter.hpp | 7 +- src/engine/gl/texture-wrapping.hpp | 5 +- src/engine/gl/texture.cpp | 225 +- src/engine/gl/texture.hpp | 23 +- src/engine/gl/vertex-array.cpp | 20 +- src/engine/gl/vertex-array.hpp | 13 +- src/engine/gl/vertex-attribute.hpp | 2 +- src/engine/gl/vertex-buffer.cpp | 93 +- src/engine/gl/vertex-buffer.hpp | 92 +- src/engine/i18n/string-map.cpp | 15 +- src/engine/i18n/string-map.hpp | 3 +- src/engine/i18n/string-table.cpp | 80 + src/engine/i18n/string-table.hpp | 13 +- .../orbit/ephemeris.cpp} | 105 +- src/engine/physics/orbit/ephemeris.hpp | 7 +- src/engine/physics/orbit/trajectory.hpp | 2 +- src/engine/render/compositor.cpp | 4 +- src/engine/render/compositor.hpp | 2 +- ...blend-mode.hpp => material-blend-mode.hpp} | 16 +- src/engine/render/material-flags.hpp | 1 - src/engine/render/material-property.hpp | 460 ---- ...adow-mode.hpp => material-shadow-mode.hpp} | 18 +- ...roperty.cpp => material-variable-type.hpp} | 55 +- src/engine/render/material-variable.hpp | 373 ++++ src/engine/render/material.cpp | 535 ++++- src/engine/render/material.hpp | 265 +-- src/engine/render/model.cpp | 371 +++- src/engine/render/model.hpp | 241 +- src/engine/render/pass.hpp | 2 +- src/engine/render/passes/bloom-pass.cpp | 249 ++- src/engine/render/passes/bloom-pass.hpp | 42 +- src/engine/render/passes/clear-pass.cpp | 98 +- src/engine/render/passes/clear-pass.hpp | 10 +- src/engine/render/passes/final-pass.cpp | 172 +- src/engine/render/passes/final-pass.hpp | 32 +- src/engine/render/passes/fxaa-pass.cpp | 119 +- src/engine/render/passes/fxaa-pass.hpp | 26 +- src/engine/render/passes/ground-pass.cpp | 67 +- src/engine/render/passes/ground-pass.hpp | 22 +- src/engine/render/passes/material-pass.cpp | 1360 +++++++----- src/engine/render/passes/material-pass.hpp | 161 +- src/engine/render/passes/outline-pass.cpp | 40 +- src/engine/render/passes/outline-pass.hpp | 19 +- src/engine/render/passes/resample-pass.cpp | 105 +- src/engine/render/passes/resample-pass.hpp | 26 +- src/engine/render/passes/shadow-map-pass.cpp | 69 +- src/engine/render/passes/shadow-map-pass.hpp | 18 +- src/engine/render/passes/sky-pass.cpp | 523 +++-- src/engine/render/passes/sky-pass.hpp | 126 +- src/engine/render/passes/ui-pass.cpp | 91 - src/engine/render/passes/ui-pass.hpp | 61 - src/engine/render/renderer.cpp | 54 +- src/engine/render/renderer.hpp | 12 +- src/engine/render/shader-template.cpp | 226 -- src/engine/render/stage.cpp | 7 +- src/engine/render/stage.hpp | 24 +- src/engine/render/stage/culling-stage.cpp | 10 +- src/engine/render/stage/culling-stage.hpp | 9 +- src/engine/render/vertex-attribute.hpp | 6 +- src/engine/resources/deserialize-context.cpp | 176 -- src/engine/resources/deserialize-context.hpp | 77 +- src/engine/resources/file-buffer-loader.cpp | 35 - src/engine/resources/image-loader.cpp | 169 -- src/engine/resources/image.cpp | 141 -- src/engine/resources/json-loader.cpp | 38 - src/engine/resources/material-loader.cpp | 457 ---- src/engine/resources/mesh-loader.cpp | 91 - src/engine/resources/model-loader.cpp | 334 --- .../physfs/physfs-deserialize-context.cpp | 270 +++ .../physfs/physfs-deserialize-context.hpp | 92 + .../physfs-serialize-context.cpp} | 99 +- .../physfs/physfs-serialize-context.hpp | 87 + src/engine/resources/resource-handle.hpp | 70 - src/engine/resources/resource-loader.cpp | 61 - src/engine/resources/resource-loader.hpp | 28 +- src/engine/resources/resource-manager.cpp | 113 +- src/engine/resources/resource-manager.hpp | 242 +- src/engine/resources/serialize-context.hpp | 42 +- src/engine/resources/shader-loader.cpp | 174 -- src/engine/resources/string-table-loader.cpp | 79 - src/engine/resources/text-file-loader.cpp | 37 - src/engine/resources/texture-loader.cpp | 259 --- src/engine/resources/typeface-loader.cpp | 56 - src/engine/scene/billboard.cpp | 4 +- src/engine/scene/billboard.hpp | 67 +- src/engine/scene/directional-light.cpp | 26 +- src/engine/scene/directional-light.hpp | 98 +- src/engine/scene/model-instance.cpp | 54 +- src/engine/scene/model-instance.hpp | 150 +- src/engine/scene/text.cpp | 39 +- src/engine/scene/text.hpp | 15 +- src/engine/type/bitmap-font.cpp | 40 +- src/engine/type/bitmap-font.hpp | 2 +- src/engine/type/bitmap-glyph.hpp | 2 +- .../{typeface.cpp => ft-typeface.cpp} | 47 +- .../{typeface.hpp => ft-typeface.hpp} | 29 +- src/engine/type/typeface.cpp | 31 + src/engine/type/typeface.hpp | 2 +- src/engine/utility/dict.cpp | 87 +- src/engine/utility/hash/combine.hpp | 51 + src/engine/utility/hash/fnv1a.hpp | 245 ++- src/engine/utility/hash/types.hpp | 32 + src/engine/utility/image.cpp | 277 +++ src/engine/{resources => utility}/image.hpp | 172 +- .../json.cpp} | 24 +- src/engine/{resources => utility}/json.hpp | 8 +- .../dict-loader.cpp => utility/text-file.cpp} | 49 +- .../{resources => utility}/text-file.hpp | 16 +- src/game/ant/{caste.hpp => ant-caste.hpp} | 16 +- ...{cladogenesis.cpp => ant-cladogenesis.cpp} | 10 +- ...{cladogenesis.hpp => ant-cladogenesis.hpp} | 11 +- ...table.hpp => ant-gene-frequency-table.hpp} | 16 +- src/game/ant/ant-gene-pool.hpp | 79 + .../ant/ant-genome.cpp} | 7 +- src/game/ant/ant-genome.hpp | 75 + ...orphogenesis.cpp => ant-morphogenesis.cpp} | 1944 ++++++++--------- ...orphogenesis.hpp => ant-morphogenesis.hpp} | 9 +- src/game/ant/{phenome.cpp => ant-phenome.cpp} | 34 +- src/game/ant/ant-phenome.hpp | 88 + src/game/ant/{species.hpp => ant-species.hpp} | 15 +- .../ant/{subcaste.hpp => ant-subcaste.hpp} | 8 +- src/game/ant/{swarm.cpp => ant-swarm.cpp} | 355 ++- src/game/ant/{swarm.hpp => ant-swarm.hpp} | 8 +- src/game/ant/gene-pool.hpp | 83 - src/game/ant/gene/antennae.hpp | 35 - src/game/ant/gene/body-size.hpp | 35 - src/game/ant/gene/cocoon.hpp | 35 - src/game/ant/gene/diet.hpp | 35 - src/game/ant/gene/egg.hpp | 35 - src/game/ant/gene/eyes.hpp | 35 - src/game/ant/gene/foraging-time.hpp | 35 - src/game/ant/gene/founding-mode.hpp | 35 - src/game/ant/gene/gaster.hpp | 35 - src/game/ant/gene/head.hpp | 35 - src/game/ant/gene/larva.hpp | 35 - src/game/ant/gene/legs.hpp | 35 - src/game/ant/gene/mandibles.hpp | 35 - src/game/ant/gene/mesosoma.hpp | 35 - src/game/ant/gene/nest-site.hpp | 35 - src/game/ant/gene/ocelli.hpp | 35 - src/game/ant/gene/pigmentation.hpp | 35 - src/game/ant/gene/pilosity.hpp | 35 - src/game/ant/gene/sculpturing.hpp | 35 - src/game/ant/gene/sting.hpp | 35 - src/game/ant/gene/waist.hpp | 35 - src/game/ant/gene/wings.hpp | 35 - .../ant-antennae-gene.cpp} | 31 +- .../ant-antennae-gene.hpp} | 26 +- .../ant-body-size-gene.cpp} | 27 +- .../ant-body-size-gene.hpp} | 17 +- .../ant-cocoon-gene.cpp} | 27 +- .../cocoon.hpp => genes/ant-cocoon-gene.hpp} | 19 +- .../ant-diet-gene.cpp} | 27 +- .../diet.hpp => genes/ant-diet-gene.hpp} | 15 +- .../egg-loader.cpp => genes/ant-egg-gene.cpp} | 27 +- .../{phene/egg.hpp => genes/ant-egg-gene.hpp} | 19 +- .../ant-eyes-gene.cpp} | 27 +- .../eyes.hpp => genes/ant-eyes-gene.hpp} | 22 +- .../ant-foraging-time-gene.cpp} | 27 +- .../ant-foraging-time-gene.hpp} | 17 +- .../ant-founding-mode-gene.cpp} | 29 +- .../ant-founding-mode-gene.hpp} | 16 +- .../ant-gaster-gene.cpp} | 27 +- .../gaster.hpp => genes/ant-gaster-gene.hpp} | 19 +- .../ant-gene-loader.hpp} | 62 +- .../ant-head-gene.cpp} | 27 +- .../head.hpp => genes/ant-head-gene.hpp} | 19 +- .../ant-larva-gene.cpp} | 27 +- .../larva.hpp => genes/ant-larva-gene.hpp} | 22 +- .../ant-legs-gene.cpp} | 27 +- .../legs.hpp => genes/ant-legs-gene.hpp} | 19 +- .../ant-mandibles-gene.cpp} | 27 +- .../ant-mandibles-gene.hpp} | 19 +- .../ant-mesosoma-gene.cpp} | 27 +- .../ant-mesosoma-gene.hpp} | 19 +- .../ant-nest-site-gene.cpp} | 29 +- .../ant-nest-site-gene.hpp} | 18 +- .../ant-ocelli-gene.cpp} | 29 +- .../ocelli.hpp => genes/ant-ocelli-gene.hpp} | 21 +- .../ant-pigmentation-gene.cpp} | 27 +- .../ant-pigmentation-gene.hpp} | 19 +- .../ant-pilosity-gene.cpp} | 25 +- .../ant-pilosity-gene.hpp} | 15 +- .../ant-sculpturing-gene.cpp} | 27 +- .../ant-sculpturing-gene.hpp} | 19 +- .../ant-sting-gene.cpp} | 27 +- .../sting.hpp => genes/ant-sting-gene.hpp} | 19 +- .../ant-waist-gene.cpp} | 27 +- .../waist.hpp => genes/ant-waist-gene.hpp} | 19 +- .../ant-wings-gene.cpp} | 29 +- .../wings.hpp => genes/ant-wings-gene.hpp} | 21 +- .../monophenic-ant-gene.hpp} | 18 +- .../polyphenic-ant-gene.hpp} | 22 +- src/game/ant/genome.cpp | 49 - src/game/ant/genome.hpp | 82 - src/game/ant/phenome.hpp | 92 - ...-component.hpp => ant-caste-component.hpp} | 16 +- src/game/components/model-component.hpp | 6 +- .../components/name-component.hpp} | 18 +- src/game/control-profile.cpp | 17 +- src/game/control-profile.hpp | 6 +- src/game/controls.cpp | 207 +- src/game/controls.hpp | 2 +- src/game/ecoregion-loader.cpp | 110 +- src/game/ecoregion.hpp | 10 +- src/game/fonts.cpp | 33 +- src/game/game.cpp | 193 +- src/game/game.hpp | 49 +- src/game/graphics.cpp | 69 +- src/game/load.cpp | 24 +- src/game/loaders/control-profile-loader.cpp | 41 - src/game/loaders/entity-archetype-loader.cpp | 25 +- src/game/menu.cpp | 10 +- src/game/settings.hpp | 6 +- src/game/spawn.cpp | 6 +- src/game/spawn.hpp | 8 +- src/game/states/collection-menu-state.cpp | 22 +- src/game/states/collection-menu-state.hpp | 4 +- src/game/states/controls-menu-state.cpp | 18 +- src/game/states/controls-menu-state.hpp | 8 +- src/game/states/credits-state.cpp | 5 +- src/game/states/extras-menu-state.cpp | 12 +- src/game/states/extras-menu-state.hpp | 7 +- src/game/states/gamepad-config-menu-state.cpp | 97 +- src/game/states/gamepad-config-menu-state.hpp | 7 +- src/game/states/graphics-menu-state.cpp | 80 +- src/game/states/graphics-menu-state.hpp | 17 +- .../states/keyboard-config-menu-state.cpp | 61 +- .../states/keyboard-config-menu-state.hpp | 8 +- src/game/states/language-menu-state.cpp | 18 +- src/game/states/language-menu-state.hpp | 6 + src/game/states/main-menu-state.cpp | 56 +- src/game/states/main-menu-state.hpp | 7 +- src/game/states/nest-selection-state.cpp | 19 +- src/game/states/nuptial-flight-state.cpp | 161 +- src/game/states/nuptial-flight-state.hpp | 6 +- src/game/states/options-menu-state.cpp | 30 +- src/game/states/options-menu-state.hpp | 9 + src/game/states/pause-menu-state.cpp | 28 +- src/game/states/pause-menu-state.hpp | 9 +- src/game/states/sound-menu-state.cpp | 58 +- src/game/states/sound-menu-state.hpp | 17 +- src/game/states/splash-state.cpp | 23 +- src/game/states/splash-state.hpp | 2 +- src/game/strings.cpp | 6 +- src/game/strings.hpp | 12 +- src/game/systems/orbit-system.cpp | 6 +- src/game/systems/orbit-system.hpp | 5 +- src/game/systems/render-system.cpp | 71 +- src/game/systems/render-system.hpp | 6 +- src/game/systems/subterrain-system.cpp | 16 +- src/game/systems/terrain-system.cpp | 23 +- src/game/systems/terrain-system.hpp | 10 +- src/game/world.cpp | 127 +- 308 files changed, 11227 insertions(+), 11457 deletions(-) delete mode 100644 src/engine/geom/csg.cpp delete mode 100644 src/engine/geom/csg.hpp create mode 100644 src/engine/gl/opengl/gl-shader-variables.cpp create mode 100644 src/engine/gl/opengl/gl-shader-variables.hpp delete mode 100644 src/engine/gl/shader-input.cpp delete mode 100644 src/engine/gl/shader-input.hpp create mode 100644 src/engine/gl/shader-template.cpp rename src/engine/{render => gl}/shader-template.hpp (73%) create mode 100644 src/engine/gl/shader-variable.cpp create mode 100644 src/engine/gl/shader-variable.hpp create mode 100644 src/engine/i18n/string-table.cpp rename src/engine/{resources/ephemeris-loader.cpp => physics/orbit/ephemeris.cpp} (66%) rename src/engine/render/{blend-mode.hpp => material-blend-mode.hpp} (73%) delete mode 100644 src/engine/render/material-property.hpp rename src/engine/render/{shadow-mode.hpp => material-shadow-mode.hpp} (73%) rename src/engine/render/{material-property.cpp => material-variable-type.hpp} (63%) create mode 100644 src/engine/render/material-variable.hpp delete mode 100644 src/engine/render/passes/ui-pass.cpp delete mode 100644 src/engine/render/passes/ui-pass.hpp delete mode 100644 src/engine/render/shader-template.cpp delete mode 100644 src/engine/resources/deserialize-context.cpp delete mode 100644 src/engine/resources/file-buffer-loader.cpp delete mode 100644 src/engine/resources/image-loader.cpp delete mode 100644 src/engine/resources/image.cpp delete mode 100644 src/engine/resources/json-loader.cpp delete mode 100644 src/engine/resources/material-loader.cpp delete mode 100644 src/engine/resources/mesh-loader.cpp delete mode 100644 src/engine/resources/model-loader.cpp create mode 100644 src/engine/resources/physfs/physfs-deserialize-context.cpp create mode 100644 src/engine/resources/physfs/physfs-deserialize-context.hpp rename src/engine/resources/{serialize-context.cpp => physfs/physfs-serialize-context.cpp} (59%) create mode 100644 src/engine/resources/physfs/physfs-serialize-context.hpp delete mode 100644 src/engine/resources/resource-handle.hpp delete mode 100644 src/engine/resources/resource-loader.cpp delete mode 100644 src/engine/resources/shader-loader.cpp delete mode 100644 src/engine/resources/string-table-loader.cpp delete mode 100644 src/engine/resources/text-file-loader.cpp delete mode 100644 src/engine/resources/texture-loader.cpp delete mode 100644 src/engine/resources/typeface-loader.cpp rename src/engine/type/freetype/{typeface.cpp => ft-typeface.cpp} (74%) rename src/engine/type/freetype/{typeface.hpp => ft-typeface.hpp} (68%) create mode 100644 src/engine/utility/hash/combine.hpp create mode 100644 src/engine/utility/hash/types.hpp create mode 100644 src/engine/utility/image.cpp rename src/engine/{resources => utility}/image.hpp (62%) rename src/engine/{resources/string-map-loader.cpp => utility/json.cpp} (59%) rename src/engine/{resources => utility}/json.hpp (86%) rename src/engine/{resources/dict-loader.cpp => utility/text-file.cpp} (50%) rename src/engine/{resources => utility}/text-file.hpp (78%) rename src/game/ant/{caste.hpp => ant-caste.hpp} (84%) rename src/game/ant/{cladogenesis.cpp => ant-cladogenesis.cpp} (89%) rename src/game/ant/{cladogenesis.hpp => ant-cladogenesis.hpp} (85%) rename src/game/ant/{gene-frequency-table.hpp => ant-gene-frequency-table.hpp} (88%) create mode 100644 src/game/ant/ant-gene-pool.hpp rename src/{engine/resources/resource-handle.cpp => game/ant/ant-genome.cpp} (86%) create mode 100644 src/game/ant/ant-genome.hpp rename src/game/ant/{morphogenesis.cpp => ant-morphogenesis.cpp} (67%) rename src/game/ant/{morphogenesis.hpp => ant-morphogenesis.hpp} (89%) rename src/game/ant/{phenome.cpp => ant-phenome.cpp} (84%) create mode 100644 src/game/ant/ant-phenome.hpp rename src/game/ant/{species.hpp => ant-species.hpp} (81%) rename src/game/ant/{subcaste.hpp => ant-subcaste.hpp} (93%) rename src/game/ant/{swarm.cpp => ant-swarm.cpp} (89%) rename src/game/ant/{swarm.hpp => ant-swarm.hpp} (87%) delete mode 100644 src/game/ant/gene-pool.hpp delete mode 100644 src/game/ant/gene/antennae.hpp delete mode 100644 src/game/ant/gene/body-size.hpp delete mode 100644 src/game/ant/gene/cocoon.hpp delete mode 100644 src/game/ant/gene/diet.hpp delete mode 100644 src/game/ant/gene/egg.hpp delete mode 100644 src/game/ant/gene/eyes.hpp delete mode 100644 src/game/ant/gene/foraging-time.hpp delete mode 100644 src/game/ant/gene/founding-mode.hpp delete mode 100644 src/game/ant/gene/gaster.hpp delete mode 100644 src/game/ant/gene/head.hpp delete mode 100644 src/game/ant/gene/larva.hpp delete mode 100644 src/game/ant/gene/legs.hpp delete mode 100644 src/game/ant/gene/mandibles.hpp delete mode 100644 src/game/ant/gene/mesosoma.hpp delete mode 100644 src/game/ant/gene/nest-site.hpp delete mode 100644 src/game/ant/gene/ocelli.hpp delete mode 100644 src/game/ant/gene/pigmentation.hpp delete mode 100644 src/game/ant/gene/pilosity.hpp delete mode 100644 src/game/ant/gene/sculpturing.hpp delete mode 100644 src/game/ant/gene/sting.hpp delete mode 100644 src/game/ant/gene/waist.hpp delete mode 100644 src/game/ant/gene/wings.hpp rename src/game/ant/{gene/loader/antennae-loader.cpp => genes/ant-antennae-gene.cpp} (61%) rename src/game/ant/{phene/antennae.hpp => genes/ant-antennae-gene.hpp} (68%) rename src/game/ant/{gene/loader/body-size-loader.cpp => genes/ant-body-size-gene.cpp} (66%) rename src/game/ant/{phene/body-size.hpp => genes/ant-body-size-gene.hpp} (78%) rename src/game/ant/{gene/loader/cocoon-loader.cpp => genes/ant-cocoon-gene.cpp} (63%) rename src/game/ant/{phene/cocoon.hpp => genes/ant-cocoon-gene.hpp} (73%) rename src/game/ant/{gene/loader/diet-loader.cpp => genes/ant-diet-gene.cpp} (58%) rename src/game/ant/{phene/diet.hpp => genes/ant-diet-gene.hpp} (80%) rename src/game/ant/{gene/loader/egg-loader.cpp => genes/ant-egg-gene.cpp} (61%) rename src/game/ant/{phene/egg.hpp => genes/ant-egg-gene.hpp} (72%) rename src/game/ant/{gene/loader/eyes-loader.cpp => genes/ant-eyes-gene.cpp} (72%) rename src/game/ant/{phene/eyes.hpp => genes/ant-eyes-gene.hpp} (75%) rename src/game/ant/{gene/loader/foraging-time-loader.cpp => genes/ant-foraging-time-gene.cpp} (63%) rename src/game/ant/{phene/foraging-time.hpp => genes/ant-foraging-time-gene.hpp} (74%) rename src/game/ant/{gene/loader/founding-mode-loader.cpp => genes/ant-founding-mode-gene.cpp} (51%) rename src/game/ant/{phene/founding-mode.hpp => genes/ant-founding-mode-gene.hpp} (74%) rename src/game/ant/{gene/loader/gaster-loader.cpp => genes/ant-gaster-gene.cpp} (63%) rename src/game/ant/{phene/gaster.hpp => genes/ant-gaster-gene.hpp} (73%) rename src/game/ant/{gene/loader/gene-loader.hpp => genes/ant-gene-loader.hpp} (55%) rename src/game/ant/{gene/loader/head-loader.cpp => genes/ant-head-gene.cpp} (68%) rename src/game/ant/{phene/head.hpp => genes/ant-head-gene.hpp} (77%) rename src/game/ant/{gene/loader/larva-loader.cpp => genes/ant-larva-gene.cpp} (63%) rename src/game/ant/{phene/larva.hpp => genes/ant-larva-gene.hpp} (70%) rename src/game/ant/{gene/loader/legs-loader.cpp => genes/ant-legs-gene.cpp} (66%) rename src/game/ant/{phene/legs.hpp => genes/ant-legs-gene.hpp} (75%) rename src/game/ant/{gene/loader/mandibles-loader.cpp => genes/ant-mandibles-gene.cpp} (67%) rename src/game/ant/{phene/mandibles.hpp => genes/ant-mandibles-gene.hpp} (79%) rename src/game/ant/{gene/loader/mesosoma-loader.cpp => genes/ant-mesosoma-gene.cpp} (71%) rename src/game/ant/{phene/mesosoma.hpp => genes/ant-mesosoma-gene.hpp} (77%) rename src/game/ant/{gene/loader/nest-site-loader.cpp => genes/ant-nest-site-gene.cpp} (53%) rename src/game/ant/{phene/nest-site.hpp => genes/ant-nest-site-gene.hpp} (68%) rename src/game/ant/{gene/loader/ocelli-loader.cpp => genes/ant-ocelli-gene.cpp} (71%) rename src/game/ant/{phene/ocelli.hpp => genes/ant-ocelli-gene.hpp} (73%) rename src/game/ant/{gene/loader/pigmentation-loader.cpp => genes/ant-pigmentation-gene.cpp} (58%) rename src/game/ant/{phene/pigmentation.hpp => genes/ant-pigmentation-gene.hpp} (69%) rename src/game/ant/{gene/loader/pilosity-loader.cpp => genes/ant-pilosity-gene.cpp} (61%) rename src/game/ant/{phene/pilosity.hpp => genes/ant-pilosity-gene.hpp} (73%) rename src/game/ant/{gene/loader/sculpturing-loader.cpp => genes/ant-sculpturing-gene.cpp} (61%) rename src/game/ant/{phene/sculpturing.hpp => genes/ant-sculpturing-gene.hpp} (70%) rename src/game/ant/{gene/loader/sting-loader.cpp => genes/ant-sting-gene.cpp} (63%) rename src/game/ant/{phene/sting.hpp => genes/ant-sting-gene.hpp} (73%) rename src/game/ant/{gene/loader/waist-loader.cpp => genes/ant-waist-gene.cpp} (80%) rename src/game/ant/{phene/waist.hpp => genes/ant-waist-gene.hpp} (82%) rename src/game/ant/{gene/loader/wings-loader.cpp => genes/ant-wings-gene.cpp} (75%) rename src/game/ant/{phene/wings.hpp => genes/ant-wings-gene.hpp} (79%) rename src/game/ant/{gene/monophenic-gene.hpp => genes/monophenic-ant-gene.hpp} (74%) rename src/game/ant/{gene/polyphenic-gene.hpp => genes/polyphenic-ant-gene.hpp} (72%) delete mode 100644 src/game/ant/genome.cpp delete mode 100644 src/game/ant/genome.hpp delete mode 100644 src/game/ant/phenome.hpp rename src/game/components/{caste-component.hpp => ant-caste-component.hpp} (77%) rename src/{engine/resources/file-buffer.hpp => game/components/name-component.hpp} (76%) delete mode 100644 src/game/loaders/control-profile-loader.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c2fc691..912a62c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required(VERSION 3.25) + option(APPLICATION_NAME "Application name" "Antkeeper") option(APPLICATION_VERSION "Application version string" "0.0.0") option(APPLICATION_AUTHOR "Application author" "C. J. Howard") diff --git a/src/engine/animation/animation-channel.hpp b/src/engine/animation/animation-channel.hpp index 974c1cf..539614d 100644 --- a/src/engine/animation/animation-channel.hpp +++ b/src/engine/animation/animation-channel.hpp @@ -128,6 +128,7 @@ animation_channel& animation_channel::operator=(const animation_channel& o { id = other.id; keyframes = other.keyframes; + return *this; } template diff --git a/src/engine/animation/bone.hpp b/src/engine/animation/bone.hpp index a99c9e1..22fadae 100644 --- a/src/engine/animation/bone.hpp +++ b/src/engine/animation/bone.hpp @@ -22,15 +22,13 @@ #include - - /** * Skeletal animation bone identifier, consisting of a bone index in the lower half, and a parent bone index in the upper half. */ -typedef std::uint32_t bone; +using bone = std::uint32_t; /// Mask to extract the index of a bone. -constexpr bone bone_index_mask = 0xffff; +inline constexpr bone bone_index_mask = 0xffff; /** * Bone index comparison function object. @@ -42,9 +40,13 @@ struct bone_index_compare * * @param lhs First bone. * @param rhs Second bone. + * * @return Comparison result. */ - bool operator()(const bone& lhs, const bone& rhs) const; + [[nodiscard]] inline bool operator()(const bone& lhs, const bone& rhs) const noexcept + { + return (lhs & bone_index_mask) < (rhs & bone_index_mask); + } }; /** @@ -52,68 +54,58 @@ struct bone_index_compare * * @param index Index of the bone. * @param parent_index Index of the parent bone. + * * @return Bone identifier. */ -bone make_bone(std::uint16_t index, std::uint16_t parent_index); +[[nodiscard]] inline bone make_bone(std::uint16_t index, std::uint16_t parent_index) noexcept +{ + return (static_cast(parent_index) << 16) | index; +} /** * Constructs an orphan bone identifier. * * @param index Index of the orphan bone. + * * @return Orphan bone identifier. */ -bone make_bone(std::uint16_t index); +[[nodiscard]] inline bone make_bone(std::uint16_t index) noexcept +{ + return make_bone(index, index); +} /** * Returns the index of a bone. * * @param x Bone identifier. + * * @return Index of the bone. */ -std::uint16_t bone_index(bone x); +[[nodiscard]] inline std::uint16_t bone_index(bone x) noexcept +{ + return static_cast(x & bone_index_mask); +} /** * Returns the parent index of a bone. * * @param x Bone identifier. + * * @return Index of the parent bone. */ -std::uint16_t bone_parent_index(bone x); +[[nodiscard]] inline std::uint16_t bone_parent_index(bone x) noexcept +{ + return static_cast(x >> 16); +} /** * Returns `true` if a bone has a parent, `false` otherwise. * * @param x Bone identifier. + * * @return Bone parent status. */ -bool bone_has_parent(bone x); - -inline bool bone_index_compare::operator()(const bone& lhs, const bone& rhs) const -{ - return (lhs & bone_index_mask) < (rhs & bone_index_mask); -} - -inline bone make_bone(std::uint16_t index, std::uint16_t parent_index) -{ - return (static_cast(parent_index) << 16) | index; -} - -inline bone make_bone(std::uint16_t index) -{ - return make_bone(index, index); -} - -inline std::uint16_t bone_index(bone x) -{ - return static_cast(x & bone_index_mask); -} - -inline std::uint16_t bone_parent_index(bone x) -{ - return static_cast(x >> 16); -} - -inline bool bone_has_parent(bone x) +[[nodiscard]] inline bool bone_has_parent(bone x) noexcept { return (x & bone_index_mask) != (x >> 16); } diff --git a/src/engine/animation/screen-transition.cpp b/src/engine/animation/screen-transition.cpp index 72ee1dd..dcc678c 100644 --- a/src/engine/animation/screen-transition.cpp +++ b/src/engine/animation/screen-transition.cpp @@ -23,13 +23,15 @@ screen_transition::screen_transition() { + progress = std::make_shared(1, 0.0f); + // Setup material - //material.set_flags(MATERIAL_FLAG_X_RAY); - material.set_blend_mode(render::blend_mode::translucent); - progress = material.add_property("progress"); + material = std::make_shared(); + material->set_blend_mode(render::material_blend_mode::translucent); + material->set_variable("progress", progress); // Setup billboard - billboard.set_material(&material); + billboard.set_material(material); billboard.set_active(false); // Add single channel to transition animation @@ -52,7 +54,7 @@ screen_transition::screen_transition() ( [this](int channel, float progress) { - this->progress->set_value(progress); + this->progress->set(progress); } ); @@ -61,7 +63,7 @@ screen_transition::screen_transition() ( [this](int channel, float progress) { - this->progress->set_value(progress); + this->progress->set(progress); } ); } @@ -104,8 +106,8 @@ void screen_transition::transition(float duration, bool reverse, ::animationset_value(initial_state); - material.update_tweens(); + progress->set(initial_state); + //material.update_tweens(); // Reset and play transition animation animation.stop(); diff --git a/src/engine/animation/screen-transition.hpp b/src/engine/animation/screen-transition.hpp index 2aa647c..3a6f139 100644 --- a/src/engine/animation/screen-transition.hpp +++ b/src/engine/animation/screen-transition.hpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include /** @@ -38,13 +38,13 @@ public: void transition(float duration, bool reverse, animation::interpolator_type interpolator, bool hide = true, const std::function& callback = nullptr); scene::billboard* get_billboard(); - render::material* get_material(); + std::shared_ptr get_material(); ::animation* get_animation(); private: scene::billboard billboard; - render::material material; - render::material_property* progress; + std::shared_ptr material; + std::shared_ptr progress; ::animation animation; ::animation::channel* channel; std::function callback; @@ -55,9 +55,9 @@ inline scene::billboard* screen_transition::get_billboard() return &billboard; } -inline render::material* screen_transition::get_material() +inline std::shared_ptr screen_transition::get_material() { - return &material; + return material; } inline animation* screen_transition::get_animation() diff --git a/src/engine/animation/skeleton.hpp b/src/engine/animation/skeleton.hpp index 27cfc62..ca9e2b3 100644 --- a/src/engine/animation/skeleton.hpp +++ b/src/engine/animation/skeleton.hpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -37,7 +38,7 @@ struct skeleton pose inverse_bind_pose; /// Maps bone names to bone identifiers. - std::unordered_map bone_map; + std::unordered_map bone_map; }; #endif // ANTKEEPER_ANIMATION_SKELETON_HPP diff --git a/src/engine/app/input-manager.cpp b/src/engine/app/input-manager.cpp index e41a835..394871b 100644 --- a/src/engine/app/input-manager.cpp +++ b/src/engine/app/input-manager.cpp @@ -22,9 +22,9 @@ namespace app { -input_manager* input_manager::instance() +std::unique_ptr input_manager::instance() { - return new sdl_input_manager(); + return std::make_unique(); } void input_manager::register_device(input::device& device) diff --git a/src/engine/app/input-manager.hpp b/src/engine/app/input-manager.hpp index 6a7ea3f..9beb243 100644 --- a/src/engine/app/input-manager.hpp +++ b/src/engine/app/input-manager.hpp @@ -40,7 +40,7 @@ public: /** * Allocates and returns an input manager. */ - static input_manager* instance(); + static std::unique_ptr instance(); /// Destructs an input manager. virtual ~input_manager() = default; diff --git a/src/engine/app/sdl/sdl-input-manager.cpp b/src/engine/app/sdl/sdl-input-manager.cpp index 22fab13..bdbee4e 100644 --- a/src/engine/app/sdl/sdl-input-manager.cpp +++ b/src/engine/app/sdl/sdl-input-manager.cpp @@ -268,13 +268,11 @@ void sdl_input_manager::update() debug::log::info("Connected gamepad {}; name: \"{}\"; UUID: {}", event.cdevice.which, controller_name, gamepad_uuid.string()); - // Create new gamepad - input::gamepad* gamepad = new input::gamepad(); + // Allocate gamepad + auto& gamepad = gamepad_map[event.cdevice.which]; + gamepad = std::make_unique(); gamepad->set_uuid(gamepad_uuid); - // Add gamepad to gamepad map - gamepad_map[event.cdevice.which] = gamepad; - // Register gamepad register_device(*gamepad); diff --git a/src/engine/app/sdl/sdl-input-manager.hpp b/src/engine/app/sdl/sdl-input-manager.hpp index 1942fcf..b1445e6 100644 --- a/src/engine/app/sdl/sdl-input-manager.hpp +++ b/src/engine/app/sdl/sdl-input-manager.hpp @@ -21,6 +21,7 @@ #define ANTKEEPER_APP_SDL_INPUT_MANAGER_HPP #include +#include namespace app { @@ -49,7 +50,7 @@ public: private: input::keyboard keyboard; input::mouse mouse; - std::unordered_map gamepad_map; + std::unordered_map> gamepad_map; }; } // namespace app diff --git a/src/engine/app/sdl/sdl-window.cpp b/src/engine/app/sdl/sdl-window.cpp index 57e4cbe..3c08ca0 100644 --- a/src/engine/app/sdl/sdl-window.cpp +++ b/src/engine/app/sdl/sdl-window.cpp @@ -179,13 +179,13 @@ sdl_window::sdl_window SDL_GL_GetDrawableSize(internal_window, &this->viewport_size.x(), &this->viewport_size.y()); // Allocate rasterizer - this->rasterizer = new gl::rasterizer(); + this->rasterizer = std::make_unique(); } sdl_window::~sdl_window() { - // Destruct rasterizer - delete rasterizer; + // Deallocate rasterizer + rasterizer.reset(); // Destruct the OpenGL context SDL_GL_DeleteContext(internal_context); diff --git a/src/engine/app/sdl/sdl-window.hpp b/src/engine/app/sdl/sdl-window.hpp index 9d69623..1c1ba88 100644 --- a/src/engine/app/sdl/sdl-window.hpp +++ b/src/engine/app/sdl/sdl-window.hpp @@ -22,6 +22,7 @@ #include #include +#include namespace app { @@ -60,7 +61,7 @@ public: [[nodiscard]] inline virtual gl::rasterizer* get_rasterizer() noexcept { - return rasterizer; + return rasterizer.get(); } private: @@ -68,7 +69,7 @@ private: SDL_Window* internal_window; SDL_GLContext internal_context; - gl::rasterizer* rasterizer; + std::unique_ptr rasterizer; }; } // namespace app diff --git a/src/engine/app/window-manager.cpp b/src/engine/app/window-manager.cpp index 694dc36..23901b4 100644 --- a/src/engine/app/window-manager.cpp +++ b/src/engine/app/window-manager.cpp @@ -22,9 +22,9 @@ namespace app { -window_manager* window_manager::instance() +std::unique_ptr window_manager::instance() { - return new sdl_window_manager(); + return std::make_unique(); } } // namespace app diff --git a/src/engine/app/window-manager.hpp b/src/engine/app/window-manager.hpp index 9e319a4..3e80b59 100644 --- a/src/engine/app/window-manager.hpp +++ b/src/engine/app/window-manager.hpp @@ -37,7 +37,7 @@ public: /** * Allocates and returns a window manager. */ - static window_manager* instance(); + static std::unique_ptr instance(); /// Destructs a window manager. virtual ~window_manager() = default; diff --git a/src/engine/config.hpp.in b/src/engine/config.hpp.in index ebbcc1e..e426f7e 100644 --- a/src/engine/config.hpp.in +++ b/src/engine/config.hpp.in @@ -126,15 +126,6 @@ inline constexpr float new_colony_fade_out_duration = 1.0f; /// Duration of the nuptial flight fade in, in seconds. inline constexpr float nuptial_flight_fade_in_duration = 5.0f; -#define MATERIAL_PASS_MAX_AMBIENT_LIGHT_COUNT 1 -#define MATERIAL_PASS_MAX_POINT_LIGHT_COUNT 1 -#define MATERIAL_PASS_MAX_DIRECTIONAL_LIGHT_COUNT 3 -#define MATERIAL_PASS_MAX_SPOTLIGHT_COUNT 1 -#define MATERIAL_PASS_MAX_BONE_COUNT 64 -#define TERRAIN_PATCH_SIZE 200.0f -#define TERRAIN_PATCH_RESOLUTION 4 -#define VEGETATION_PATCH_RESOLUTION 1 - } // namespace config #endif // ANTKEEPER_CONFIG_HPP diff --git a/src/engine/geom/csg.cpp b/src/engine/geom/csg.cpp deleted file mode 100644 index b7263ea..0000000 --- a/src/engine/geom/csg.cpp +++ /dev/null @@ -1,116 +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 . - */ - -#include -#include - -namespace geom { -namespace csg { - -enum class polygon_classification -{ - coplanar, - front, - back, - spanning -}; - -/** - * Classifies a polygon relative to a partitioning plane. - * - * @param partition Partitioning plane relative to which the polygon should be classified. - * @param poly Polygon to be classified. - * @return Classification of the polygon, relative to the plane. - */ -static polygon_classification classify_polygon(const plane& partition, const polygon& poly) -{ - for (const float3& vertex: poly.vertices) - { - - } - - return polygon_classification::coplanar; -} - -/** - * Splits a polygon along a partitioning plane. - * - * @param poly Polygon to split. - * @param partition Partitioning plane along which the polygon should be split. - * @return List of polygons which were formed by splitting the specified polygon along the partitioning plane, along with their respective classifications relative to the partition. - */ -std::list> split_polygon(const polygon& poly, const plane& partition) -{ - return {}; -} - - -bsp_tree::bsp_tree(const std::list& polygons): - front(nullptr), - back(nullptr) -{ - //partition = polygons.front(); - - std::list front_polygons; - std::list back_polygons; - - // Classify all polygons relative to this node's partitioning plane - for (const polygon& p: polygons) - { - polygon_classification classification = classify_polygon(partition, p); - switch (classification) - { - case polygon_classification::coplanar: - coplanar_polygons.push_back(p); - break; - - case polygon_classification::front: - front_polygons.push_back(p); - break; - - case polygon_classification::back: - back_polygons.push_back(p); - break; - - case polygon_classification::spanning: - break; - } - } - - if (!front_polygons.empty()) - { - // Make subtree containing all polygons in front of this node's plane - front = new bsp_tree(front_polygons); - } - - if (!back_polygons.empty()) - { - // Make subtree containing all polygons behind this node's plane - back = new bsp_tree(back_polygons); - } -} - -bsp_tree::~bsp_tree() -{ - delete front; - delete back; -} - -} // namespace csg -} // namespace geom diff --git a/src/engine/geom/csg.hpp b/src/engine/geom/csg.hpp deleted file mode 100644 index d53f5da..0000000 --- a/src/engine/geom/csg.hpp +++ /dev/null @@ -1,88 +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 . - */ - -#ifndef ANTKEEPER_GEOM_CSG_HPP -#define ANTKEEPER_GEOM_CSG_HPP - -#include -#include - -namespace geom { - -/// Constructive solid geometry (CSG) -namespace csg { - -struct plane -{ - float3 normal; - float distance; -}; - -struct polygon -{ - std::list vertices; - void* shared; -}; - -/** - * 3D solid represented by a collection of polygons. - */ -typedef std::list solid; - -/** - * BSP tree node. - */ -class bsp_tree -{ -public: - /** - * Recursively constructs a BSP tree from a collection of polygons. - * - * @param polygons Collection of polygons from which to create the BSP tree. - */ - explicit bsp_tree(const std::list& polygons); - - /** - * Destroys a BSP tree. - */ - ~bsp_tree(); - -private: - /// Partition which separates the front and back polygons. - plane partition; - - /// Set of polygons which are coplanar with the partition. - std::list coplanar_polygons; - - /// Subtree containing all polygons in front of the partition. - bsp_tree* front; - - /// Subtree containing all polygons behind the partition. - bsp_tree* back; -}; - -solid op_union(const solid& a, const solid& b); -solid op_difference(const solid& a, const solid& b); -solid op_intersect(const solid& a, const solid& b); - -} // namespace csg -} // namespace geom - -#endif // ANTKEEPER_GEOM_CSG_HPP - diff --git a/src/engine/geom/marching-cubes.cpp b/src/engine/geom/marching-cubes.cpp index ea3fdaf..4d9162e 100644 --- a/src/engine/geom/marching-cubes.cpp +++ b/src/engine/geom/marching-cubes.cpp @@ -23,7 +23,7 @@ namespace geom { namespace mc { -static constexpr std::uint_fast8_t vertex_table[12][2] = +static constexpr std::uint8_t vertex_table[12][2] = { {0, 1}, {1, 2}, @@ -39,7 +39,7 @@ static constexpr std::uint_fast8_t vertex_table[12][2] = {3, 7} }; -static constexpr std::uint_fast16_t edge_table[256] = +static constexpr std::uint16_t edge_table[256] = { 0x000, 0x109, 0x203, 0x30A, 0x406, 0x50F, 0x605, 0x70C, 0x80C, 0x905, 0xA0F, 0xB06, 0xC0A, 0xD03, 0xE09, 0xF00, @@ -75,7 +75,7 @@ static constexpr std::uint_fast16_t edge_table[256] = 0x70C, 0x605, 0x50F, 0x406, 0x30A, 0x203, 0x109, 0x000 }; -static constexpr std::int_fast8_t triangle_table[256][16] = +static constexpr std::int8_t triangle_table[256][16] = { {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, @@ -335,34 +335,34 @@ static constexpr std::int_fast8_t triangle_table[256][16] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} }; -void polygonize(float* vertices, std::uint_fast8_t* vertex_count, std::int_fast8_t* triangles, std::uint_fast8_t* triangle_count, const float* corners, const float* distances) +void polygonize(float* vertices, std::uint8_t* vertex_count, std::int8_t* triangles, std::uint8_t* triangle_count, const float* corners, const float* distances) { *vertex_count = 0; *triangle_count = 0; // Calculate signed distances for each cube corner and form an edge table index. - std::uint_fast8_t edge_index = 0; - for (std::uint_fast8_t i = 0; i < 8; ++i) + std::uint8_t edge_index = 0; + for (std::uint8_t i = 0; i < 8; ++i) edge_index |= (distances[i] < 0.0f) ? (1 << i) : 0; // Get edge flags from edge table - const std::uint_fast16_t edge_flags = edge_table[edge_index]; + const std::uint16_t edge_flags = edge_table[edge_index]; if (!edge_flags) return; // Get vertex indices of the case - const std::int_fast8_t* indices = triangle_table[edge_index]; + const std::int8_t* indices = triangle_table[edge_index]; // Calculate vertices and store in vertex buffer float vertex_buffer[12 * 3]; - for (std::uint_fast16_t i = 0; i < 12; ++i) + for (std::uint16_t i = 0; i < 12; ++i) { // If this edge is intersected if (edge_flags & (1 << i)) { // Find the two vertices which make up edge ab - std::uint_fast8_t a = vertex_table[i][0]; - std::uint_fast8_t b = vertex_table[i][1]; + std::uint8_t a = vertex_table[i][0]; + std::uint8_t b = vertex_table[i][1]; const float* v_a = corners + a * 3; const float* v_b = corners + b * 3; float f_a = distances[a]; @@ -389,14 +389,14 @@ void polygonize(float* vertices, std::uint_fast8_t* vertex_count, std::int_fast8 } // Remap vertex buffer to be stored contiguously - std::int_fast8_t vertex_remap[12]; - for (std::uint_fast8_t i = 0; i < 12; ++i) + std::int8_t vertex_remap[12]; + for (std::uint8_t i = 0; i < 12; ++i) vertex_remap[i] = -1; - for (std::uint_fast8_t i = 0; indices[i] != -1; ++i) + for (std::uint8_t i = 0; indices[i] != -1; ++i) { if (vertex_remap[indices[i]] == -1) { - std::int_fast8_t index = indices[i] * 3; + std::int8_t index = indices[i] * 3; *(vertices++) = vertex_buffer[ index]; *(vertices++) = vertex_buffer[++index]; *(vertices++) = vertex_buffer[++index]; @@ -405,7 +405,7 @@ void polygonize(float* vertices, std::uint_fast8_t* vertex_count, std::int_fast8 } // Form triangles - for (std::uint_fast8_t i = 0; indices[i] != -1;) + for (std::uint8_t i = 0; indices[i] != -1;) { *(triangles++) = vertex_remap[indices[i++]]; *(triangles++) = vertex_remap[indices[i++]]; diff --git a/src/engine/geom/marching-cubes.hpp b/src/engine/geom/marching-cubes.hpp index 44bcea3..de25903 100644 --- a/src/engine/geom/marching-cubes.hpp +++ b/src/engine/geom/marching-cubes.hpp @@ -35,7 +35,7 @@ namespace mc { * @param[out] triangles Array which can hold 5 at least triangles (15 ints). * @param[out] triangle_count Number of generated triangles. The maximum number triangles generated for a single cell is 5. */ -void polygonize(float* vertices, std::uint_fast8_t* vertex_count, std::int_fast8_t* triangles, std::uint_fast8_t* triangle_count, const float* corners, const float* distances); +void polygonize(float* vertices, std::uint8_t* vertex_count, std::int8_t* triangles, std::uint8_t* triangle_count, const float* corners, const float* distances); /** * Vertices of a unit cube. diff --git a/src/engine/geom/mesh-functions.cpp b/src/engine/geom/mesh-functions.cpp index baefaef..50c5921 100644 --- a/src/engine/geom/mesh-functions.cpp +++ b/src/engine/geom/mesh-functions.cpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace geom { @@ -101,13 +102,8 @@ void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const const std::vector& vertices = mesh.get_vertices(); // Allocate tangent and bitangent buffers - float3* tangent_buffer = new float3[vertices.size()]; - float3* bitangent_buffer = new float3[vertices.size()]; - for (std::size_t i = 0; i < vertices.size(); ++i) - { - tangent_buffer[i] = {0.0f, 0.0f, 0.0f}; - bitangent_buffer[i] = {0.0f, 0.0f, 0.0f}; - } + std::vector tangent_buffer(vertices.size(), float3{0.0f, 0.0f, 0.0f}); + std::vector bitangent_buffer(vertices.size(), float3{0.0f, 0.0f, 0.0f}); // Accumulate tangents and bitangents for (std::size_t i = 0; i < faces.size(); ++i) @@ -155,10 +151,6 @@ void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const tangents[i] = {tangent.x(), tangent.y(), tangent.z(), bitangent_sign}; } - - // Free faceted tangents and bitangents - delete[] tangent_buffer; - delete[] bitangent_buffer; } aabb calculate_bounds(const mesh& mesh) diff --git a/src/engine/geom/meshes/grid.cpp b/src/engine/geom/meshes/grid.cpp index 425f3f5..b39c185 100644 --- a/src/engine/geom/meshes/grid.cpp +++ b/src/engine/geom/meshes/grid.cpp @@ -25,10 +25,10 @@ namespace geom { namespace meshes { -geom::mesh* grid_xy(float length, std::size_t subdivisions_x, std::size_t subdivisions_y) +std::unique_ptr grid_xy(float length, std::size_t subdivisions_x, std::size_t subdivisions_y) { // Allocate new mesh - geom::mesh* mesh = new geom::mesh(); + std::unique_ptr mesh = std::make_unique(); // Determine vertex count and placement std::size_t columns = subdivisions_x + 1; @@ -94,7 +94,7 @@ geom::mesh* grid_xy(float length, std::size_t subdivisions_x, std::size_t subdiv mesh->add_face({ab, bd, dc, ca}); } } - + return mesh; } diff --git a/src/engine/geom/meshes/grid.hpp b/src/engine/geom/meshes/grid.hpp index 7fba2a6..59a92c6 100644 --- a/src/engine/geom/meshes/grid.hpp +++ b/src/engine/geom/meshes/grid.hpp @@ -21,6 +21,7 @@ #define ANTKEEPER_GEOM_MESHES_GRID_HPP #include +#include namespace geom { namespace meshes { @@ -33,7 +34,7 @@ namespace meshes { * @param subdivisions_y Number of subdivisions on the y-axis. * @return Grid mesh on the XY plane. */ -geom::mesh* grid_xy(float length, std::size_t subdivisions_x, std::size_t subdivisions_y); +[[nodiscard]] std::unique_ptr grid_xy(float length, std::size_t subdivisions_x, std::size_t subdivisions_y); } // namespace meshes } // namespace geom diff --git a/src/engine/geom/rect-pack.hpp b/src/engine/geom/rect-pack.hpp index 0fb59cd..90c6a9a 100644 --- a/src/engine/geom/rect-pack.hpp +++ b/src/engine/geom/rect-pack.hpp @@ -21,6 +21,7 @@ #define ANTKEEPER_GEOM_RECT_PACK_HPP #include +#include namespace geom { @@ -38,38 +39,16 @@ struct rect_pack_node /// Rect type. typedef rect rect_type; - /// Creates a rect pack node. - rect_pack_node(); - - /// Destroys a rect pack node and its children. - ~rect_pack_node(); - /// Pointers to the two children of the node, if any. - rect_pack_node* children[2]; + std::unique_ptr children[2]; /// Bounds of the node. - rect_type bounds; + rect_type bounds{T{0}, T{0}, T{0}, T{0}}; /// `true` if the node is occupied, `false` otherwise. - bool occupied; + bool occupied{false}; }; -template -rect_pack_node::rect_pack_node(): - bounds{T(0), T(0), T(0), T(0)}, - occupied(false) -{ - children[0] = nullptr; - children[1] = nullptr; -} - -template -rect_pack_node::~rect_pack_node() -{ - delete children[0]; - delete children[1]; -} - /** * Packs 2D rectangles. * @@ -136,7 +115,7 @@ private: template rect_pack::rect_pack(scalar_type w, scalar_type h) { - root.bounds = {T(0), T(0), w, h}; + root.bounds = {T{0}, T{0}, w, h}; } template @@ -154,10 +133,8 @@ void rect_pack::resize(scalar_type w, scalar_type h) template void rect_pack::clear() { - delete root.children[0]; - delete root.children[1]; - root.children[0] = nullptr; - root.children[1] = nullptr; + root.children[0].reset(); + root.children[1].reset(); root.occupied = false; } @@ -209,8 +186,8 @@ typename rect_pack::node_type* rect_pack::insert(node_type& node, scalar_t } // Split the node - node.children[0] = new node_type(); - node.children[1] = new node_type(); + node.children[0] = std::make_unique(); + node.children[1] = std::make_unique(); // Determine split direction scalar_type dw = node_w - w; diff --git a/src/engine/gl/buffer-usage.hpp b/src/engine/gl/buffer-usage.hpp index 1bb0ac2..f76c63d 100644 --- a/src/engine/gl/buffer-usage.hpp +++ b/src/engine/gl/buffer-usage.hpp @@ -20,6 +20,8 @@ #ifndef ANTKEEPER_GL_BUFFER_USAGE_HPP #define ANTKEEPER_GL_BUFFER_USAGE_HPP +#include + namespace gl { /** @@ -27,7 +29,7 @@ namespace gl { * * @see gl::vertex_buffer */ -enum class buffer_usage +enum class buffer_usage: std::uint8_t { /// Data will be modified once, by the application, and used at most a few times, for drawing commands. stream_draw, diff --git a/src/engine/gl/color-space.hpp b/src/engine/gl/color-space.hpp index 1be6569..5d2272d 100644 --- a/src/engine/gl/color-space.hpp +++ b/src/engine/gl/color-space.hpp @@ -20,15 +20,19 @@ #ifndef ANTKEEPER_GL_COLOR_SPACE_HPP #define ANTKEEPER_GL_COLOR_SPACE_HPP +#include + namespace gl { -enum class color_space +enum class color_space: std::uint8_t { - linear, ///< Linear color space - srgb ///< sRGB color space + /// Linear color space. + linear, + + /// sRGB color space. + srgb }; } // namespace gl #endif // ANTKEEPER_GL_COLOR_SPACE_HPP - diff --git a/src/engine/gl/drawing-mode.hpp b/src/engine/gl/drawing-mode.hpp index 699810f..0c0ec7b 100644 --- a/src/engine/gl/drawing-mode.hpp +++ b/src/engine/gl/drawing-mode.hpp @@ -20,9 +20,11 @@ #ifndef ANTKEEPER_GL_DRAWING_MODE_HPP #define ANTKEEPER_GL_DRAWING_MODE_HPP +#include + namespace gl { -enum class drawing_mode +enum class drawing_mode: std::uint8_t { points, line_strip, @@ -40,4 +42,3 @@ enum class drawing_mode } // namespace gl #endif // ANTKEEPER_GL_DRAWING_MODE_HPP - diff --git a/src/engine/gl/element-array-type.hpp b/src/engine/gl/element-array-type.hpp index 9ed2137..fd77208 100644 --- a/src/engine/gl/element-array-type.hpp +++ b/src/engine/gl/element-array-type.hpp @@ -20,9 +20,11 @@ #ifndef ANTKEEPER_GL_ELEMENT_ARRAY_TYPE_HPP #define ANTKEEPER_GL_ELEMENT_ARRAY_TYPE_HPP +#include + namespace gl { -enum class element_array_type +enum class element_array_type: std::uint8_t { uint_8, uint_16, @@ -32,4 +34,3 @@ enum class element_array_type } // namespace gl #endif // ANTKEEPER_GL_ELEMENT_ARRAY_TYPE_HPP - diff --git a/src/engine/gl/framebuffer.hpp b/src/engine/gl/framebuffer.hpp index b9f486b..39ee243 100644 --- a/src/engine/gl/framebuffer.hpp +++ b/src/engine/gl/framebuffer.hpp @@ -21,13 +21,14 @@ #define ANTKEEPER_GL_FRAMEBUFFER_HPP #include +#include namespace gl { class rasterizer; class texture_2d; -enum class framebuffer_attachment_type +enum class framebuffer_attachment_type: std::uint8_t { color, depth, @@ -41,6 +42,7 @@ public: * Creates a framebuffer. */ framebuffer(int width, int height); + framebuffer(); /// Destroys a framebuffer. ~framebuffer(); @@ -73,8 +75,6 @@ public: private: friend class rasterizer; - framebuffer(); - unsigned int gl_framebuffer_id; std::array dimensions; texture_2d* color_attachment; @@ -120,4 +120,3 @@ inline texture_2d* framebuffer::get_stencil_attachment() } // namespace gl #endif // ANTKEEPER_GL_FRAMEBUFFER_HPP - diff --git a/src/engine/gl/gl.hpp b/src/engine/gl/gl.hpp index 6cdf2ce..be2e549 100644 --- a/src/engine/gl/gl.hpp +++ b/src/engine/gl/gl.hpp @@ -23,25 +23,4 @@ /// Graphics library interface. namespace gl {} -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #endif // ANTKEEPER_GL_HPP diff --git a/src/engine/gl/opengl/gl-shader-variables.cpp b/src/engine/gl/opengl/gl-shader-variables.cpp new file mode 100644 index 0000000..a91fbe4 --- /dev/null +++ b/src/engine/gl/opengl/gl-shader-variables.cpp @@ -0,0 +1,666 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +namespace gl { + +gl_shader_bool::gl_shader_bool(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location}, + ivalues(size) +{} + +void gl_shader_bool::update(bool value) const noexcept +{ + glUniform1i(gl_uniform_location, static_cast(value)); +} + +void gl_shader_bool::update(bool value, std::size_t index) const +{ + glUniform1i(gl_uniform_location + static_cast(index), static_cast(value)); +} + +void gl_shader_bool::update(std::span values, std::size_t index) const +{ + for (std::size_t i = 0; i < values.size(); ++i) + { + ivalues[i] = static_cast(values[i]); + } + + glUniform1iv(gl_uniform_location + static_cast(index), static_cast(values.size()), ivalues.data()); +} + +gl_shader_bool2::gl_shader_bool2(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location}, + ivalues(size * 2) +{} + +void gl_shader_bool2::update(const bool2& value) const noexcept +{ + glUniform2i(gl_uniform_location, static_cast(value[0]), static_cast(value[1])); +} + +void gl_shader_bool2::update(const bool2& value, std::size_t index) const +{ + glUniform2i(gl_uniform_location + static_cast(index), static_cast(value[0]), static_cast(value[1])); +} + +void gl_shader_bool2::update(std::span values, std::size_t index) const +{ + GLint* ivalue = ivalues.data(); + for (const auto& value: values) + { + *(++ivalue) = static_cast(value[0]); + *(++ivalue) = static_cast(value[1]); + } + + glUniform2iv(gl_uniform_location + static_cast(index), static_cast(values.size()), ivalues.data()); +} + +gl_shader_bool3::gl_shader_bool3(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location}, + ivalues(size * 3) +{} + +void gl_shader_bool3::update(const bool3& value) const noexcept +{ + glUniform3i(gl_uniform_location, static_cast(value[0]), static_cast(value[1]), static_cast(value[2])); +} + +void gl_shader_bool3::update(const bool3& value, std::size_t index) const +{ + glUniform3i(gl_uniform_location + static_cast(index), static_cast(value[0]), static_cast(value[1]), static_cast(value[2])); +} + +void gl_shader_bool3::update(std::span values, std::size_t index) const +{ + GLint* ivalue = ivalues.data(); + for (const auto& value: values) + { + *(++ivalue) = static_cast(value[0]); + *(++ivalue) = static_cast(value[1]); + *(++ivalue) = static_cast(value[2]); + } + + glUniform3iv(gl_uniform_location + static_cast(index), static_cast(values.size()), ivalues.data()); +} + +gl_shader_bool4::gl_shader_bool4(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location}, + ivalues(size * 4) +{} + +void gl_shader_bool4::update(const bool4& value) const noexcept +{ + glUniform4i(gl_uniform_location, static_cast(value[0]), static_cast(value[1]), static_cast(value[2]), static_cast(value[3])); +} + +void gl_shader_bool4::update(const bool4& value, std::size_t index) const +{ + glUniform4i(gl_uniform_location + static_cast(index), static_cast(value[0]), static_cast(value[1]), static_cast(value[2]), static_cast(value[3])); +} + +void gl_shader_bool4::update(std::span values, std::size_t index) const +{ + GLint* ivalue = ivalues.data(); + for (const auto& value: values) + { + *(++ivalue) = static_cast(value[0]); + *(++ivalue) = static_cast(value[1]); + *(++ivalue) = static_cast(value[2]); + *(++ivalue) = static_cast(value[3]); + } + + glUniform4iv(gl_uniform_location + static_cast(index), static_cast(values.size()), ivalues.data()); +} + +gl_shader_int::gl_shader_int(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_int::update(int value) const noexcept +{ + glUniform1i(gl_uniform_location, value); +} + +void gl_shader_int::update(int value, std::size_t index) const +{ + glUniform1i(gl_uniform_location + static_cast(index), value); +} + +void gl_shader_int::update(std::span values, std::size_t index) const +{ + glUniform1iv(gl_uniform_location + static_cast(index), static_cast(values.size()), values.data()); +} + +gl_shader_int2::gl_shader_int2(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_int2::update(const int2& value) const noexcept +{ + glUniform2iv(gl_uniform_location, 1, value.data()); +} + +void gl_shader_int2::update(const int2& value, std::size_t index) const +{ + glUniform2iv(gl_uniform_location + static_cast(index), 1, value.data()); +} + +void gl_shader_int2::update(std::span values, std::size_t index) const +{ + glUniform2iv(gl_uniform_location + static_cast(index), static_cast(values.size()), values.data()->data()); +} + +gl_shader_int3::gl_shader_int3(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_int3::update(const int3& value) const noexcept +{ + glUniform3iv(gl_uniform_location, 1, value.data()); +} + +void gl_shader_int3::update(const int3& value, std::size_t index) const +{ + glUniform3iv(gl_uniform_location + static_cast(index), 1, value.data()); +} + +void gl_shader_int3::update(std::span values, std::size_t index) const +{ + glUniform3iv(gl_uniform_location + static_cast(index), static_cast(values.size()), values.data()->data()); +} + +gl_shader_int4::gl_shader_int4(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_int4::update(const int4& value) const noexcept +{ + glUniform4iv(gl_uniform_location, 1, value.data()); +} + +void gl_shader_int4::update(const int4& value, std::size_t index) const +{ + glUniform4iv(gl_uniform_location + static_cast(index), 1, value.data()); +} + +void gl_shader_int4::update(std::span values, std::size_t index) const +{ + glUniform4iv(gl_uniform_location + static_cast(index), static_cast(values.size()), values.data()->data()); +} + +gl_shader_uint::gl_shader_uint(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_uint::update(unsigned int value) const noexcept +{ + glUniform1ui(gl_uniform_location, value); +} + +void gl_shader_uint::update(unsigned int value, std::size_t index) const +{ + glUniform1ui(gl_uniform_location + static_cast(index), value); +} + +void gl_shader_uint::update(std::span values, std::size_t index) const +{ + glUniform1uiv(gl_uniform_location + static_cast(index), static_cast(values.size()), values.data()); +} + +gl_shader_uint2::gl_shader_uint2(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_uint2::update(const uint2& value) const noexcept +{ + glUniform2uiv(gl_uniform_location, 1, value.data()); +} + +void gl_shader_uint2::update(const uint2& value, std::size_t index) const +{ + glUniform2uiv(gl_uniform_location + static_cast(index), 1, value.data()); +} + +void gl_shader_uint2::update(std::span values, std::size_t index) const +{ + glUniform2uiv(gl_uniform_location + static_cast(index), static_cast(values.size()), values.data()->data()); +} + +gl_shader_uint3::gl_shader_uint3(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_uint3::update(const uint3& value) const noexcept +{ + glUniform3uiv(gl_uniform_location, 1, value.data()); +} + +void gl_shader_uint3::update(const uint3& value, std::size_t index) const +{ + glUniform3uiv(gl_uniform_location + static_cast(index), 1, value.data()); +} + +void gl_shader_uint3::update(std::span values, std::size_t index) const +{ + glUniform3uiv(gl_uniform_location + static_cast(index), static_cast(values.size()), values.data()->data()); +} + +gl_shader_uint4::gl_shader_uint4(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_uint4::update(const uint4& value) const noexcept +{ + glUniform4uiv(gl_uniform_location, 1, value.data()); +} + +void gl_shader_uint4::update(const uint4& value, std::size_t index) const +{ + glUniform4uiv(gl_uniform_location + static_cast(index), 1, value.data()); +} + +void gl_shader_uint4::update(std::span values, std::size_t index) const +{ + glUniform4uiv(gl_uniform_location + static_cast(index), static_cast(values.size()), values.data()->data()); +} + +gl_shader_float::gl_shader_float(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_float::update(float value) const noexcept +{ + glUniform1f(gl_uniform_location, value); +} + +void gl_shader_float::update(float value, std::size_t index) const +{ + glUniform1f(gl_uniform_location + static_cast(index), value); +} + +void gl_shader_float::update(std::span values, std::size_t index) const +{ + glUniform1fv(gl_uniform_location + static_cast(index), static_cast(values.size()), values.data()); +} + +gl_shader_float2::gl_shader_float2(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_float2::update(const float2& value) const noexcept +{ + glUniform2fv(gl_uniform_location, 1, value.data()); +} + +void gl_shader_float2::update(const float2& value, std::size_t index) const +{ + glUniform2fv(gl_uniform_location + static_cast(index), 1, value.data()); +} + +void gl_shader_float2::update(std::span values, std::size_t index) const +{ + glUniform2fv(gl_uniform_location + static_cast(index), static_cast(values.size()), values.data()->data()); +} + +gl_shader_float3::gl_shader_float3(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_float3::update(const float3& value) const noexcept +{ + glUniform3fv(gl_uniform_location, 1, value.data()); +} + +void gl_shader_float3::update(const float3& value, std::size_t index) const +{ + glUniform3fv(gl_uniform_location + static_cast(index), 1, value.data()); +} + +void gl_shader_float3::update(std::span values, std::size_t index) const +{ + glUniform3fv(gl_uniform_location + static_cast(index), static_cast(values.size()), values.data()->data()); +} + +gl_shader_float4::gl_shader_float4(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_float4::update(const float4& value) const noexcept +{ + glUniform4fv(gl_uniform_location, 1, value.data()); +} + +void gl_shader_float4::update(const float4& value, std::size_t index) const +{ + glUniform4fv(gl_uniform_location + static_cast(index), 1, value.data()); +} + +void gl_shader_float4::update(std::span values, std::size_t index) const +{ + glUniform4fv(gl_uniform_location + static_cast(index), static_cast(values.size()), values.data()->data()); +} + +gl_shader_float2x2::gl_shader_float2x2(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_float2x2::update(const float2x2& value) const noexcept +{ + glUniformMatrix2fv(gl_uniform_location, 1, GL_FALSE, value.data()); +} + +void gl_shader_float2x2::update(const float2x2& value, std::size_t index) const +{ + glUniformMatrix2fv(gl_uniform_location + static_cast(index) * 2, 1, GL_FALSE, value.data()); +} + +void gl_shader_float2x2::update(std::span values, std::size_t index) const +{ + glUniformMatrix2fv(gl_uniform_location + static_cast(index) * 2, static_cast(values.size()), GL_FALSE, values.data()->data()); +} + +gl_shader_float3x3::gl_shader_float3x3(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_float3x3::update(const float3x3& value) const noexcept +{ + glUniformMatrix3fv(gl_uniform_location, 1, GL_FALSE, value.data()); +} + +void gl_shader_float3x3::update(const float3x3& value, std::size_t index) const +{ + glUniformMatrix3fv(gl_uniform_location + static_cast(index) * 3, 1, GL_FALSE, value.data()); +} + +void gl_shader_float3x3::update(std::span values, std::size_t index) const +{ + glUniformMatrix3fv(gl_uniform_location + static_cast(index) * 3, static_cast(values.size()), GL_FALSE, values.data()->data()); +} + +gl_shader_float4x4::gl_shader_float4x4(std::size_t size, GLint gl_uniform_location): + shader_variable(size), + gl_uniform_location{gl_uniform_location} +{} + +void gl_shader_float4x4::update(const float4x4& value) const noexcept +{ + glUniformMatrix4fv(gl_uniform_location, 1, GL_FALSE, value.data()); +} + +void gl_shader_float4x4::update(const float4x4& value, std::size_t index) const +{ + glUniformMatrix4fv(gl_uniform_location + static_cast(index) * 4, 1, GL_FALSE, value.data()); +} + +void gl_shader_float4x4::update(std::span values, std::size_t index) const +{ + glUniformMatrix4fv(gl_uniform_location + static_cast(index) * 4, static_cast(values.size()), GL_FALSE, values.data()->data()); +} + +gl_shader_texture_1d::gl_shader_texture_1d(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_unit_index): + shader_variable(size), + gl_uniform_location{gl_uniform_location}, + gl_texture_unit_indices(size) +{ + std::iota(gl_texture_unit_indices.begin(), gl_texture_unit_indices.end(), gl_first_texture_unit_index); +} + +void gl_shader_texture_1d::update(const texture_1d& value) const noexcept +{ + // Bind texture to texture unit + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices.front())); + glBindTexture(GL_TEXTURE_1D, value.gl_texture_id); + + // Pass texture unit index to shader + glUniform1i(gl_uniform_location, gl_texture_unit_indices.front()); +} + +void gl_shader_texture_1d::update(const texture_1d& value, std::size_t index) const +{ + const GLint gl_texture_index = gl_texture_unit_indices[index]; + + // Bind texture to texture unit + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_index)); + glBindTexture(GL_TEXTURE_1D, value.gl_texture_id); + + // Pass texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index), gl_texture_index); +} + +void gl_shader_texture_1d::update(std::span values, std::size_t index) const +{ + // Bind textures + for (std::size_t i = 0; i < values.size(); ++i) + { + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); + glBindTexture(GL_TEXTURE_1D, values[i]->gl_texture_id); + } + + // Pass texture unit indices to shader + glUniform1iv(gl_uniform_location + static_cast(index), static_cast(values.size()), &gl_texture_unit_indices[index]); +} + +void gl_shader_texture_1d::update(std::span> values, std::size_t index) const +{ + // Bind textures + for (std::size_t i = 0; i < values.size(); ++i) + { + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); + glBindTexture(GL_TEXTURE_1D, values[i]->gl_texture_id); + } + + // Pass texture unit indices to shader + glUniform1iv(gl_uniform_location + static_cast(index), static_cast(values.size()), &gl_texture_unit_indices[index]); +} + +gl_shader_texture_2d::gl_shader_texture_2d(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_unit_index): + shader_variable(size), + gl_uniform_location{gl_uniform_location}, + gl_texture_unit_indices(size) +{ + std::iota(gl_texture_unit_indices.begin(), gl_texture_unit_indices.end(), gl_first_texture_unit_index); +} + +void gl_shader_texture_2d::update(const texture_2d& value) const noexcept +{ + // Bind texture to texture unit + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices.front())); + glBindTexture(GL_TEXTURE_2D, value.gl_texture_id); + + // Pass texture unit index to shader + glUniform1i(gl_uniform_location, gl_texture_unit_indices.front()); +} + +void gl_shader_texture_2d::update(const texture_2d& value, std::size_t index) const +{ + const GLint gl_texture_index = gl_texture_unit_indices[index]; + + // Bind texture to texture unit + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_index)); + glBindTexture(GL_TEXTURE_2D, value.gl_texture_id); + + // Pass texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index), gl_texture_index); +} + +void gl_shader_texture_2d::update(std::span values, std::size_t index) const +{ + // Bind textures + for (std::size_t i = 0; i < values.size(); ++i) + { + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); + glBindTexture(GL_TEXTURE_2D, values[i]->gl_texture_id); + } + + // Pass texture unit indices to shader + glUniform1iv(gl_uniform_location + static_cast(index), static_cast(values.size()), &gl_texture_unit_indices[index]); +} + +void gl_shader_texture_2d::update(std::span> values, std::size_t index) const +{ + // Bind textures + for (std::size_t i = 0; i < values.size(); ++i) + { + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); + glBindTexture(GL_TEXTURE_2D, values[i]->gl_texture_id); + } + + // Pass texture unit indices to shader + glUniform1iv(gl_uniform_location + static_cast(index), static_cast(values.size()), &gl_texture_unit_indices[index]); +} + +gl_shader_texture_3d::gl_shader_texture_3d(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_unit_index): + shader_variable(size), + gl_uniform_location{gl_uniform_location}, + gl_texture_unit_indices(size) +{ + std::iota(gl_texture_unit_indices.begin(), gl_texture_unit_indices.end(), gl_first_texture_unit_index); +} + +void gl_shader_texture_3d::update(const texture_3d& value) const noexcept +{ + // Bind texture to texture unit + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices.front())); + glBindTexture(GL_TEXTURE_3D, value.gl_texture_id); + + // Pass texture unit index to shader + glUniform1i(gl_uniform_location, gl_texture_unit_indices.front()); +} + +void gl_shader_texture_3d::update(const texture_3d& value, std::size_t index) const +{ + const GLint gl_texture_index = gl_texture_unit_indices[index]; + + // Bind texture to texture unit + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_index)); + glBindTexture(GL_TEXTURE_3D, value.gl_texture_id); + + // Pass texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index), gl_texture_index); +} + +void gl_shader_texture_3d::update(std::span values, std::size_t index) const +{ + // Bind textures + for (std::size_t i = 0; i < values.size(); ++i) + { + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); + glBindTexture(GL_TEXTURE_3D, values[i]->gl_texture_id); + } + + // Pass texture unit indices to shader + glUniform1iv(gl_uniform_location + static_cast(index), static_cast(values.size()), &gl_texture_unit_indices[index]); +} + +void gl_shader_texture_3d::update(std::span> values, std::size_t index) const +{ + // Bind textures + for (std::size_t i = 0; i < values.size(); ++i) + { + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); + glBindTexture(GL_TEXTURE_3D, values[i]->gl_texture_id); + } + + // Pass texture unit indices to shader + glUniform1iv(gl_uniform_location + static_cast(index), static_cast(values.size()), &gl_texture_unit_indices[index]); +} + +gl_shader_texture_cube::gl_shader_texture_cube(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_unit_index): + shader_variable(size), + gl_uniform_location{gl_uniform_location}, + gl_texture_unit_indices(size) +{ + std::iota(gl_texture_unit_indices.begin(), gl_texture_unit_indices.end(), gl_first_texture_unit_index); +} + +void gl_shader_texture_cube::update(const texture_cube& value) const noexcept +{ + // Bind texture to texture unit + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices.front())); + glBindTexture(GL_TEXTURE_CUBE_MAP, value.gl_texture_id); + + // Pass texture unit index to shader + glUniform1i(gl_uniform_location, gl_texture_unit_indices.front()); +} + +void gl_shader_texture_cube::update(const texture_cube& value, std::size_t index) const +{ + const GLint gl_texture_index = gl_texture_unit_indices[index]; + + // Bind texture to texture unit + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_index)); + glBindTexture(GL_TEXTURE_CUBE_MAP, value.gl_texture_id); + + // Pass texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index), gl_texture_index); +} + +void gl_shader_texture_cube::update(std::span values, std::size_t index) const +{ + // Bind textures + for (std::size_t i = 0; i < values.size(); ++i) + { + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); + glBindTexture(GL_TEXTURE_CUBE_MAP, values[i]->gl_texture_id); + } + + // Pass texture unit indices to shader + glUniform1iv(gl_uniform_location + static_cast(index), static_cast(values.size()), &gl_texture_unit_indices[index]); +} + +void gl_shader_texture_cube::update(std::span> values, std::size_t index) const +{ + // Bind textures + for (std::size_t i = 0; i < values.size(); ++i) + { + glActiveTexture(GL_TEXTURE0 + static_cast(gl_texture_unit_indices[index + i])); + glBindTexture(GL_TEXTURE_CUBE_MAP, values[i]->gl_texture_id); + } + + // Pass texture unit indices to shader + glUniform1iv(gl_uniform_location + static_cast(index), static_cast(values.size()), &gl_texture_unit_indices[index]); +} + +} // namespace gl diff --git a/src/engine/gl/opengl/gl-shader-variables.hpp b/src/engine/gl/opengl/gl-shader-variables.hpp new file mode 100644 index 0000000..b1f3a80 --- /dev/null +++ b/src/engine/gl/opengl/gl-shader-variables.hpp @@ -0,0 +1,526 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_GL_GL_SHADER_VARIABLES_HPP +#define ANTKEEPER_GL_GL_SHADER_VARIABLES_HPP + +#include +#include +#include + +namespace gl { + +/** + * Boolean shader variable implementation using OpenGL. + */ +class gl_shader_bool: public shader_variable +{ +public: + gl_shader_bool(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::bool1; + } + + void update(bool value) const noexcept override; + void update(bool value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; + mutable std::vector ivalues; +}; + +/** + * 2-dimensional boolean vector shader variable implementation using OpenGL. + */ +class gl_shader_bool2: public shader_variable +{ +public: + gl_shader_bool2(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::bool2; + } + + void update(const bool2& value) const noexcept override; + void update(const bool2& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; + mutable std::vector ivalues; +}; + +/** + * 3-dimensional boolean vector shader variable implementation using OpenGL. + */ +class gl_shader_bool3: public shader_variable +{ +public: + gl_shader_bool3(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::bool3; + } + + void update(const bool3& value) const noexcept override; + void update(const bool3& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; + mutable std::vector ivalues; +}; + +/** + * 4-dimensional boolean vector shader variable implementation using OpenGL. + */ +class gl_shader_bool4: public shader_variable +{ +public: + gl_shader_bool4(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::bool4; + } + + void update(const bool4& value) const noexcept override; + void update(const bool4& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; + mutable std::vector ivalues; +}; + +/** + * Signed integer shader variable implementation using OpenGL. + */ +class gl_shader_int: public shader_variable +{ +public: + gl_shader_int(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::int1; + } + + void update(int value) const noexcept override; + void update(int value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * 2-dimensional signed integer vector shader variable implementation using OpenGL. + */ +class gl_shader_int2: public shader_variable +{ +public: + gl_shader_int2(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::int2; + } + + void update(const int2& value) const noexcept override; + void update(const int2& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * 3-dimensional signed integer vector shader variable implementation using OpenGL. + */ +class gl_shader_int3: public shader_variable +{ +public: + gl_shader_int3(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::int3; + } + + void update(const int3& value) const noexcept override; + void update(const int3& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * 4-dimensional signed integer vector shader variable implementation using OpenGL. + */ +class gl_shader_int4: public shader_variable +{ +public: + gl_shader_int4(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::int4; + } + + void update(const int4& value) const noexcept override; + void update(const int4& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * Unsigned integer shader variable implementation using OpenGL. + */ +class gl_shader_uint: public shader_variable +{ +public: + gl_shader_uint(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::uint1; + } + + void update(unsigned int value) const noexcept override; + void update(unsigned int value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * 2-dimensional unsigned integer vector shader variable implementation using OpenGL. + */ +class gl_shader_uint2: public shader_variable +{ +public: + gl_shader_uint2(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::uint2; + } + + void update(const uint2& value) const noexcept override; + void update(const uint2& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * 3-dimensional unsigned integer vector shader variable implementation using OpenGL. + */ +class gl_shader_uint3: public shader_variable +{ +public: + gl_shader_uint3(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::uint3; + } + + void update(const uint3& value) const noexcept override; + void update(const uint3& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * 4-dimensional unsigned integer vector shader variable implementation using OpenGL. + */ +class gl_shader_uint4: public shader_variable +{ +public: + gl_shader_uint4(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::uint4; + } + + void update(const uint4& value) const noexcept override; + void update(const uint4& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * Floating-point shader variable implementation using OpenGL. + */ +class gl_shader_float: public shader_variable +{ +public: + gl_shader_float(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::float1; + } + + void update(float value) const noexcept override; + void update(float value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * 2-dimensional floating-point vector shader variable implementation using OpenGL. + */ +class gl_shader_float2: public shader_variable +{ +public: + gl_shader_float2(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::float2; + } + + void update(const float2& value) const noexcept override; + void update(const float2& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * 3-dimensional floating-point vector shader variable implementation using OpenGL. + */ +class gl_shader_float3: public shader_variable +{ +public: + gl_shader_float3(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::float3; + } + + void update(const float3& value) const noexcept override; + void update(const float3& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * 4-dimensional floating-point vector shader variable implementation using OpenGL. + */ +class gl_shader_float4: public shader_variable +{ +public: + gl_shader_float4(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::float4; + } + + void update(const float4& value) const noexcept override; + void update(const float4& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * 2x2 floating-point matrix shader variable implementation using OpenGL. + */ +class gl_shader_float2x2: public shader_variable +{ +public: + gl_shader_float2x2(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::float2x2; + } + + void update(const float2x2& value) const noexcept override; + void update(const float2x2& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * 3x3 floating-point matrix shader variable implementation using OpenGL. + */ +class gl_shader_float3x3: public shader_variable +{ +public: + gl_shader_float3x3(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::float3x3; + } + + void update(const float3x3& value) const noexcept override; + void update(const float3x3& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * 4x4 floating-point matrix shader variable implementation using OpenGL. + */ +class gl_shader_float4x4: public shader_variable +{ +public: + gl_shader_float4x4(std::size_t size, GLint gl_uniform_location); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::float4x4; + } + + void update(const float4x4& value) const noexcept override; + void update(const float4x4& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; +}; + +/** + * 1-dimensional texture shader variable implementation using OpenGL. + */ +class gl_shader_texture_1d: public shader_variable +{ +public: + gl_shader_texture_1d(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_index); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::texture_1d; + } + + void update(const texture_1d& value) const noexcept override; + void update(const texture_1d& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + void update(std::span> values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; + std::vector gl_texture_unit_indices; +}; + +/** + * 2-dimensional texture shader variable implementation using OpenGL. + */ +class gl_shader_texture_2d: public shader_variable +{ +public: + gl_shader_texture_2d(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_unit_index); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::texture_2d; + } + + void update(const texture_2d& value) const noexcept override; + void update(const texture_2d& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + void update(std::span> values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; + std::vector gl_texture_unit_indices; +}; + +/** + * 3-dimensional texture shader variable implementation using OpenGL. + */ +class gl_shader_texture_3d: public shader_variable +{ +public: + gl_shader_texture_3d(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_index); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::texture_3d; + } + + void update(const texture_3d& value) const noexcept override; + void update(const texture_3d& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + void update(std::span> values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; + std::vector gl_texture_unit_indices; +}; + +/** + * Cube texture shader variable implementation using OpenGL. + */ +class gl_shader_texture_cube: public shader_variable +{ +public: + gl_shader_texture_cube(std::size_t size, GLint gl_uniform_location, GLint gl_first_texture_index); + + [[nodiscard]] inline constexpr shader_variable_type type() const noexcept override + { + return shader_variable_type::texture_cube; + } + + void update(const texture_cube& value) const noexcept override; + void update(const texture_cube& value, std::size_t index) const override; + void update(std::span values, std::size_t index = 0) const override; + void update(std::span> values, std::size_t index = 0) const override; + +private: + GLint gl_uniform_location; + std::vector gl_texture_unit_indices; +}; + +} // namespace gl + +#endif // ANTKEEPER_GL_GL_SHADER_VARIABLES_HPP diff --git a/src/engine/gl/pixel-format.hpp b/src/engine/gl/pixel-format.hpp index 2bee239..f89fbeb 100644 --- a/src/engine/gl/pixel-format.hpp +++ b/src/engine/gl/pixel-format.hpp @@ -20,21 +20,37 @@ #ifndef ANTKEEPER_GL_PIXEL_FORMAT_HPP #define ANTKEEPER_GL_PIXEL_FORMAT_HPP +#include + namespace gl { -enum class pixel_format +enum class pixel_format: std::uint8_t { - d, ///< Depth - ds, ///< Depth, stencil - r, ///< Red - rg, ///< Red, green - rgb, ///< Red, green, blue - bgr, ///< Blue, green, red - rgba, ///< Red, green, blue, alpha - bgra ///< Blue, green, red, alpha + /// Depth + d, + + /// Depth, stencil + ds, + + /// Red + r, + + /// Red, green + rg, + + /// Red, green, blue + rgb, + + /// Blue, green, red + bgr, + + /// Red, green, blue, alpha + rgba, + + ///< Blue, green, red, alpha + bgra }; } // namespace gl #endif // ANTKEEPER_GL_PIXEL_FORMAT_HPP - diff --git a/src/engine/gl/pixel-type.hpp b/src/engine/gl/pixel-type.hpp index 9a4f7ba..4fb1a9a 100644 --- a/src/engine/gl/pixel-type.hpp +++ b/src/engine/gl/pixel-type.hpp @@ -20,9 +20,11 @@ #ifndef ANTKEEPER_GL_PIXEL_TYPE_HPP #define ANTKEEPER_GL_PIXEL_TYPE_HPP +#include + namespace gl { -enum class pixel_type +enum class pixel_type: std::uint8_t { int_8, uint_8, @@ -37,4 +39,3 @@ enum class pixel_type } // namespace gl #endif // ANTKEEPER_GL_PIXEL_TYPE_HPP - diff --git a/src/engine/gl/rasterizer.cpp b/src/engine/gl/rasterizer.cpp index 1601b50..bdd7859 100644 --- a/src/engine/gl/rasterizer.cpp +++ b/src/engine/gl/rasterizer.cpp @@ -56,18 +56,16 @@ rasterizer::rasterizer(): glGetIntegerv(GL_SCISSOR_BOX, scissor_box); // Setup default framebuffer - default_framebuffer = new framebuffer(); + default_framebuffer = std::make_unique(); default_framebuffer->gl_framebuffer_id = 0; default_framebuffer->dimensions = {scissor_box[2], scissor_box[3]}; // Bind default framebuffer - bound_framebuffer = default_framebuffer; + bound_framebuffer = default_framebuffer.get(); } rasterizer::~rasterizer() -{ - delete default_framebuffer; -} +{} void rasterizer::context_resized(int width, int height) { diff --git a/src/engine/gl/rasterizer.hpp b/src/engine/gl/rasterizer.hpp index 2f43e3c..6372a3d 100644 --- a/src/engine/gl/rasterizer.hpp +++ b/src/engine/gl/rasterizer.hpp @@ -20,15 +20,16 @@ #ifndef ANTKEEPER_GL_RASTERIZER_HPP #define ANTKEEPER_GL_RASTERIZER_HPP +#include +#include #include +#include namespace gl { class framebuffer; class vertex_array; class shader_program; -enum class drawing_mode; -enum class element_array_type; /** * Interface to the OpenGL state and drawing functions. @@ -121,21 +122,18 @@ public: /** * Returns the default framebuffer associated with the OpenGL context of a window. */ - const framebuffer& get_default_framebuffer() const; + [[nodiscard]] inline const framebuffer& get_default_framebuffer() const noexcept + { + return *default_framebuffer; + } private: - framebuffer* default_framebuffer; + std::unique_ptr default_framebuffer; const framebuffer* bound_framebuffer; const vertex_array* bound_vao; const shader_program* bound_shader_program; }; -inline const framebuffer& rasterizer::get_default_framebuffer() const -{ - return *default_framebuffer; -} - } // namespace gl #endif // ANTKEEPER_GL_RASTERIZER_HPP - diff --git a/src/engine/gl/shader-input.cpp b/src/engine/gl/shader-input.cpp deleted file mode 100644 index 000bdd0..0000000 --- a/src/engine/gl/shader-input.cpp +++ /dev/null @@ -1,773 +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 . - */ - -#include -#include -#include -#include -#include -#include - -namespace gl { - -shader_input::shader_input(shader_program* program, std::size_t input_index, int gl_uniform_location, const std::string& name, shader_variable_type data_type, std::size_t element_count, int texture_unit): - program(program), - input_index(input_index), - gl_uniform_location(gl_uniform_location), - name(name), - data_type(data_type), - element_count(element_count), - texture_unit(texture_unit) -{} - -shader_input::~shader_input() -{} - -bool shader_input::upload(const bool& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1i(gl_uniform_location, static_cast(value)); - return true; -} - -bool shader_input::upload(const bool2& value) const -{ - if (gl_uniform_location == -1) - return false; - - const GLint values[] = {value[0], value[1]}; - glUniform2iv(gl_uniform_location, 1, values); - return true; -} - -bool shader_input::upload(const bool3& value) const -{ - if (gl_uniform_location == -1) - return false; - - const GLint values[] = {value[0], value[1], value[2]}; - glUniform3iv(gl_uniform_location, 1, values); - return true; -} - -bool shader_input::upload(const bool4& value) const -{ - if (gl_uniform_location == -1) - return false; - - const GLint values[] = {value[0], value[1], value[2], value[3]}; - glUniform4iv(gl_uniform_location, 1, values); - return true; -} - -bool shader_input::upload(const int& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1i(gl_uniform_location, value); - return true; -} - -bool shader_input::upload(const int2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2iv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const int3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3iv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const int4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4iv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const unsigned int& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1ui(gl_uniform_location, value); - return true; -} - -bool shader_input::upload(const uint2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2uiv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const uint3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3uiv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const uint4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4uiv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const float& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1f(gl_uniform_location, value); - return true; -} - -bool shader_input::upload(const float2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2fv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const float3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3fv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const float4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4fv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const float2x2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix2fv(gl_uniform_location, 1, GL_FALSE, value[0].data()); - return true; -} - -bool shader_input::upload(const float3x3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix3fv(gl_uniform_location, 1, GL_FALSE, value[0].data()); - return true; -} - -bool shader_input::upload(const float4x4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix4fv(gl_uniform_location, 1, GL_FALSE, value[0].data()); - return true; -} - -bool shader_input::upload(const texture_1d* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit); - glBindTexture(GL_TEXTURE_1D, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location, texture_unit); - - return true; -} - -bool shader_input::upload(const texture_2d* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit); - glBindTexture(GL_TEXTURE_2D, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location, texture_unit); - - return true; -} - -bool shader_input::upload(const texture_3d* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit); - glBindTexture(GL_TEXTURE_3D, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location, texture_unit); - - return true; -} - -bool shader_input::upload(const texture_cube* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit); - glBindTexture(GL_TEXTURE_CUBE_MAP, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location, texture_unit); - - return true; -} - -bool shader_input::upload(std::size_t index, const bool& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1i(gl_uniform_location + static_cast(index), static_cast(value)); - return true; -} - -bool shader_input::upload(std::size_t index, const bool2& value) const -{ - if (gl_uniform_location == -1) - return false; - - const GLint values[] = {value[0], value[1]}; - glUniform2iv(gl_uniform_location + static_cast(index), 1, values); - return true; -} - -bool shader_input::upload(std::size_t index, const bool3& value) const -{ - if (gl_uniform_location == -1) - return false; - - const GLint values[] = {value[0], value[1], value[3]}; - glUniform3iv(gl_uniform_location + static_cast(index), 1, values); - return true; -} - -bool shader_input::upload(std::size_t index, const bool4& value) const -{ - if (gl_uniform_location == -1) - return false; - - const GLint values[] = {value[0], value[1], value[3], value[4]}; - glUniform4iv(gl_uniform_location + static_cast(index), 1, values); - return true; -} - -bool shader_input::upload(std::size_t index, const int& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1i(gl_uniform_location + static_cast(index), value); - return true; -} - -bool shader_input::upload(std::size_t index, const int2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2iv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const int3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3iv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const int4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4iv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const unsigned int& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1ui(gl_uniform_location + static_cast(index), value); - return true; -} - -bool shader_input::upload(std::size_t index, const uint2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2uiv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const uint3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3uiv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const uint4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4uiv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1f(gl_uniform_location + static_cast(index), value); - return true; -} - -bool shader_input::upload(std::size_t index, const float2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2fv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3fv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4fv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float2x2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix2fv(gl_uniform_location + static_cast(index) * 2, 1, GL_FALSE, value[0].data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float3x3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix3fv(gl_uniform_location + static_cast(index) * 3, 1, GL_FALSE, value[0].data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float4x4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix4fv(gl_uniform_location + static_cast(index) * 4, 1, GL_FALSE, value[0].data()); - return true; -} - -bool shader_input::upload(std::size_t index, const texture_1d* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); - glBindTexture(GL_TEXTURE_1D, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); - - return true; -} - -bool shader_input::upload(std::size_t index, const texture_2d* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); - glBindTexture(GL_TEXTURE_2D, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); - - return true; -} - -bool shader_input::upload(std::size_t index, const texture_3d* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); - glBindTexture(GL_TEXTURE_3D, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); - - return true; -} - -bool shader_input::upload(std::size_t index, const texture_cube* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); - glBindTexture(GL_TEXTURE_CUBE_MAP, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); - - return true; -} - -bool shader_input::upload(std::size_t index, const bool* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - int* int_values = new int[count]; - for (std::size_t i = 0; i < count; ++i) - int_values[i] = values[i]; - glUniform1iv(gl_uniform_location + static_cast(index), static_cast(count), &(*int_values)); - delete[] int_values; - - return true; -} - -bool shader_input::upload(std::size_t index, const bool2* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - int2* int2_values = new int2[count]; - for (std::size_t i = 0; i < count; ++i) - int2_values[i] = {values[i][0], values[i][1]}; - glUniform2iv(gl_uniform_location + static_cast(index), static_cast(count), &((*int2_values)[0])); - delete[] int2_values; - - return true; -} - -bool shader_input::upload(std::size_t index, const bool3* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - int3* int3_values = new int3[count]; - for (std::size_t i = 0; i < count; ++i) - int3_values[i] = {values[i][0], values[i][1], values[i][2]}; - glUniform3iv(gl_uniform_location + static_cast(index), static_cast(count), &((*int3_values)[0])); - delete[] int3_values; - - return true; -} - -bool shader_input::upload(std::size_t index, const bool4* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - int4* int4_values = new int4[count]; - for (std::size_t i = 0; i < count; ++i) - int4_values[i] = {values[i][0], values[i][1], values[i][2], values[i][3]}; - glUniform4iv(gl_uniform_location + static_cast(index), static_cast(count), &((*int4_values)[0])); - delete[] int4_values; - - return true; -} - -bool shader_input::upload(std::size_t index, const int* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1iv(gl_uniform_location + static_cast(index), static_cast(count), &(*values)); - return true; -} - -bool shader_input::upload(std::size_t index, const int2* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2iv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const int3* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3iv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const int4* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4iv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const unsigned int* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1uiv(gl_uniform_location + static_cast(index), static_cast(count), &(*values)); - return true; -} - -bool shader_input::upload(std::size_t index, const uint2* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2uiv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const uint3* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3uiv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const uint4* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4uiv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1fv(gl_uniform_location + static_cast(index), static_cast(count), &(*values)); - return true; -} - -bool shader_input::upload(std::size_t index, const float2* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2fv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float3* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3fv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float4* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4fv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float2x2* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix2fv(gl_uniform_location + static_cast(index) * 2, static_cast(count), GL_FALSE, (*values)[0].data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float3x3* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix3fv(gl_uniform_location + static_cast(index) * 3, static_cast(count), GL_FALSE, (*values)[0].data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float4x4* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix4fv(gl_uniform_location + static_cast(index) * 4, static_cast(count), GL_FALSE, (*values)[0].data()); - return true; -} - -bool shader_input::upload(std::size_t index, const texture_1d** values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - for (std::size_t i = 0; i < count; ++i) - { - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); - glBindTexture(GL_TEXTURE_1D, values[i]->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); - } - - return true; -} - -bool shader_input::upload(std::size_t index, const texture_2d** values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - for (std::size_t i = 0; i < count; ++i) - { - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); - glBindTexture(GL_TEXTURE_2D, values[i]->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); - } - - return true; -} - -bool shader_input::upload(std::size_t index, const texture_3d** values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - for (std::size_t i = 0; i < count; ++i) - { - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); - glBindTexture(GL_TEXTURE_3D, values[i]->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); - } - - return true; -} - -bool shader_input::upload(std::size_t index, const texture_cube** values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - for (std::size_t i = 0; i < count; ++i) - { - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); - glBindTexture(GL_TEXTURE_CUBE_MAP, values[i]->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); - } - - return true; -} - -} // namespace gl diff --git a/src/engine/gl/shader-input.hpp b/src/engine/gl/shader-input.hpp deleted file mode 100644 index 002c6a2..0000000 --- a/src/engine/gl/shader-input.hpp +++ /dev/null @@ -1,202 +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 . - */ - -#ifndef ANTKEEPER_GL_SHADER_INPUT_HPP -#define ANTKEEPER_GL_SHADER_INPUT_HPP - -#include -#include - -namespace gl { - -class shader_program; -class texture_1d; -class texture_2d; -class texture_3d; -class texture_cube; -enum class shader_variable_type; - -/** - * Port through which data can be uploaded to shader variables. - */ -class shader_input -{ -public: - /** - * Returns the type of data which can be passed through this input. - */ - shader_variable_type get_data_type() const; - - /** - * Returns `true` if the input data is stored in an array. - */ - bool is_array() const; - - /** - * Returns the number of elements the array can contain, or `1` if the data is not stored in an array. - */ - std::size_t get_element_count() const; - - /** - * Uploads a value to the shader. - * - * @param value Value to upload. - * @return `true` if the value was uploaded successfully, `false` otherwise. - */ - ///@{ - bool upload(const bool& value) const; - bool upload(const bool2& value) const; - bool upload(const bool3& value) const; - bool upload(const bool4& value) const; - bool upload(const int& value) const; - bool upload(const int2& value) const; - bool upload(const int3& value) const; - bool upload(const int4& value) const; - bool upload(const unsigned int& value) const; - bool upload(const uint2& value) const; - bool upload(const uint3& value) const; - bool upload(const uint4& value) const; - bool upload(const float& value) const; - bool upload(const float2& value) const; - bool upload(const float3& value) const; - bool upload(const float4& value) const; - bool upload(const float2x2& value) const; - bool upload(const float3x3& value) const; - bool upload(const float4x4& value) const; - bool upload(const texture_1d* value) const; - bool upload(const texture_2d* value) const; - bool upload(const texture_3d* value) const; - bool upload(const texture_cube* value) const; - ///@} - - /** - * Uploads a single array element to the shader. - * - * @param index Index of an array element. - * @param values Value to upload. - * @return `true` if the value was uploaded successfully, `false` otherwise. - */ - ///@{ - bool upload(std::size_t index, const bool& value) const; - bool upload(std::size_t index, const bool2& value) const; - bool upload(std::size_t index, const bool3& value) const; - bool upload(std::size_t index, const bool4& value) const; - bool upload(std::size_t index, const int& value) const; - bool upload(std::size_t index, const int2& value) const; - bool upload(std::size_t index, const int3& value) const; - bool upload(std::size_t index, const int4& value) const; - bool upload(std::size_t index, const unsigned int& value) const; - bool upload(std::size_t index, const uint2& value) const; - bool upload(std::size_t index, const uint3& value) const; - bool upload(std::size_t index, const uint4& value) const; - bool upload(std::size_t index, const float& value) const; - bool upload(std::size_t index, const float2& value) const; - bool upload(std::size_t index, const float3& value) const; - bool upload(std::size_t index, const float4& value) const; - bool upload(std::size_t index, const float2x2& value) const; - bool upload(std::size_t index, const float3x3& value) const; - bool upload(std::size_t index, const float4x4& value) const; - bool upload(std::size_t index, const texture_1d* value) const; - bool upload(std::size_t index, const texture_2d* value) const; - bool upload(std::size_t index, const texture_3d* value) const; - bool upload(std::size_t index, const texture_cube* value) const; - ///@} - - /** - * Uploads a range of array elements to the shader. - * - * @param index Index of the first array element. - * @param values Pointer to an array of values. - * @param count Number of elements to upload. - * @return `true` if the value was fed successfully, `false` otherwise. - */ - ///@{ - bool upload(std::size_t index, const bool* values, std::size_t count) const; - bool upload(std::size_t index, const bool2* values, std::size_t count) const; - bool upload(std::size_t index, const bool3* values, std::size_t count) const; - bool upload(std::size_t index, const bool4* values, std::size_t count) const; - bool upload(std::size_t index, const int* values, std::size_t count) const; - bool upload(std::size_t index, const int2* values, std::size_t count) const; - bool upload(std::size_t index, const int3* values, std::size_t count) const; - bool upload(std::size_t index, const int4* values, std::size_t count) const; - bool upload(std::size_t index, const unsigned int* values, std::size_t count) const; - bool upload(std::size_t index, const uint2* values, std::size_t count) const; - bool upload(std::size_t index, const uint3* values, std::size_t count) const; - bool upload(std::size_t index, const uint4* values, std::size_t count) const; - bool upload(std::size_t index, const float* values, std::size_t count) const; - bool upload(std::size_t index, const float2* values, std::size_t count) const; - bool upload(std::size_t index, const float3* values, std::size_t count) const; - bool upload(std::size_t index, const float4* values, std::size_t count) const; - bool upload(std::size_t index, const float2x2* values, std::size_t count) const; - bool upload(std::size_t index, const float3x3* values, std::size_t count) const; - bool upload(std::size_t index, const float4x4* values, std::size_t count) const; - bool upload(std::size_t index, const texture_1d** values, std::size_t count) const; - bool upload(std::size_t index, const texture_2d** values, std::size_t count) const; - bool upload(std::size_t index, const texture_3d** values, std::size_t count) const; - bool upload(std::size_t index, const texture_cube** values, std::size_t count) const; - ///@} - -private: - friend class shader_program; - - /** - * Creates a shader input. - * - * @param program Shader program with which this input is associated. - * @param gl_uniform_location Location of the shader uniform with which this shader input is associated. - * @param name Name of the input. - * @param data_type Type of data which can be passed through this input. - * @param element_count Number of elements which the array can contain, or `0` if input data is not stored in an array. - * @param texture_unit Texture unit to which texture shader variables can be bound, or `-1` if the data type is not a texture type. - */ - shader_input(shader_program* program, std::size_t input_index, int gl_uniform_location, const std::string& name, shader_variable_type data_type, std::size_t element_count, int texture_unit); - - /** - * Destroys a shader input. - */ - ~shader_input(); - - shader_program* program; - std::size_t input_index; - int gl_uniform_location; - std::string name; - shader_variable_type data_type; - std::size_t element_count; - int texture_unit; -}; - -inline shader_variable_type shader_input::get_data_type() const -{ - return data_type; -} - -inline bool shader_input::is_array() const -{ - return (element_count > 1); -} - -inline std::size_t shader_input::get_element_count() const -{ - return element_count; -} - -} // namespace gl - -#endif // ANTKEEPER_GL_SHADER_INPUT_HPP - diff --git a/src/engine/gl/shader-object.cpp b/src/engine/gl/shader-object.cpp index 58f2f6a..81c01c9 100644 --- a/src/engine/gl/shader-object.cpp +++ b/src/engine/gl/shader-object.cpp @@ -31,34 +31,29 @@ static constexpr GLenum gl_shader_type_lut[] = }; shader_object::shader_object(shader_stage stage): - gl_shader_id(0), - stage(stage), - compiled(false) + m_stage{stage} { // Look up OpenGL shader type enumeration that corresponds to the given stage - GLenum gl_shader_type = gl_shader_type_lut[static_cast(stage)]; + const GLenum gl_shader_type = gl_shader_type_lut[static_cast(m_stage)]; // Create an OpenGL shader object gl_shader_id = glCreateShader(gl_shader_type); - - // Handle OpenGL errors if (!gl_shader_id) { - throw std::runtime_error("An error occurred while creating an OpenGL shader object."); + throw std::runtime_error("Unable to create OpenGL shader object"); } } shader_object::~shader_object() { - // Flag the OpenGL shader object for deletion glDeleteShader(gl_shader_id); } -void shader_object::source(const std::string& source_code) +void shader_object::source(std::string_view source_code) { // Replace OpenGL shader object source code - GLint gl_length = static_cast(source_code.length()); - const GLchar* gl_string = source_code.c_str(); + const GLint gl_length = static_cast(source_code.length()); + const GLchar* gl_string = source_code.data(); glShaderSource(gl_shader_id, 1, &gl_string, &gl_length); // Handle OpenGL errors @@ -76,6 +71,9 @@ void shader_object::source(const std::string& source_code) bool shader_object::compile() { + m_compiled = false; + info_log.clear(); + // Compile OpenGL shader object glCompileShader(gl_shader_id); @@ -94,7 +92,7 @@ bool shader_object::compile() // Get OpenGL shader object compilation status GLint gl_compile_status; glGetShaderiv(gl_shader_id, GL_COMPILE_STATUS, &gl_compile_status); - compiled = (gl_compile_status == GL_TRUE); + m_compiled = (gl_compile_status == GL_TRUE); // Get OpenGL shader object info log length GLint gl_info_log_length; @@ -111,14 +109,9 @@ bool shader_object::compile() // Remove redundant null terminator from string info_log.pop_back(); } - else - { - // Empty info log - info_log.clear(); - } // Return compilation status - return compiled; + return m_compiled; } } // namespace gl diff --git a/src/engine/gl/shader-object.hpp b/src/engine/gl/shader-object.hpp index bba5339..8144861 100644 --- a/src/engine/gl/shader-object.hpp +++ b/src/engine/gl/shader-object.hpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace gl { @@ -44,7 +45,7 @@ public: * * @exception std::runtime_error An error occurred while creating an OpenGL shader object. */ - shader_object(shader_stage stage); + explicit shader_object(shader_stage stage); /** * Destroys a shader object. @@ -59,7 +60,7 @@ public: * @exception std::runtime_error Shader object handle is not a value generated by OpenGL. * @exception std::runtime_error Shader object handle is not a shader object. */ - void source(const std::string& source_code); + void source(std::string_view source_code); /** * Compiles the shader object. @@ -74,13 +75,22 @@ public: bool compile(); /// Returns the shader stage of this shader object. - shader_stage get_stage() const; + [[nodiscard]] inline shader_stage stage() const noexcept + { + return m_stage; + } /// Returns the shader object info log, which is updated when the shader object is compiled. - const std::string& get_info_log() const; + [[nodiscard]] inline const std::string& info() const noexcept + { + return info_log; + } /// Returns `true` if the shader object has been successfully compiled, `false` otherwise. - bool was_compiled() const; + [[nodiscard]] inline bool compiled() const noexcept + { + return m_compiled; + } shader_object(const shader_object&) = delete; shader_object& operator=(const shader_object&) = delete; @@ -88,27 +98,12 @@ public: private: friend class shader_program; - unsigned int gl_shader_id; - shader_stage stage; + unsigned int gl_shader_id{0}; + shader_stage m_stage; + bool m_compiled{false}; std::string info_log; - bool compiled; }; -inline shader_stage shader_object::get_stage() const -{ - return stage; -} - -inline const std::string& shader_object::get_info_log() const -{ - return info_log; -} - -inline bool shader_object::was_compiled() const -{ - return compiled; -} - } // namespace gl #endif // ANTKEEPER_GL_SHADER_OBJECT_HPP diff --git a/src/engine/gl/shader-program.cpp b/src/engine/gl/shader-program.cpp index 151aff3..d61c807 100644 --- a/src/engine/gl/shader-program.cpp +++ b/src/engine/gl/shader-program.cpp @@ -19,16 +19,14 @@ #include #include -#include -#include +#include #include #include +#include namespace gl { -shader_program::shader_program(): - gl_program_id(0), - linked(false) +shader_program::shader_program() { // Create an OpenGL shader program gl_program_id = glCreateProgram(); @@ -36,15 +34,12 @@ shader_program::shader_program(): // Handle OpenGL errors if (!gl_program_id) { - throw std::runtime_error("An error occurred while creating an OpenGL shader program."); + throw std::runtime_error("Unable to create OpenGL shader program"); } } shader_program::~shader_program() { - // Delete shader inputs - free_inputs(); - // Detach all shader objects detach_all(); @@ -52,22 +47,26 @@ shader_program::~shader_program() glDeleteProgram(gl_program_id); } -void shader_program::attach(const shader_object* object) +void shader_program::attach(const shader_object& object) { - if (attached_objects.find(object) != attached_objects.end()) + if (attached_objects.find(&object) != attached_objects.end()) { - throw std::runtime_error("Shader object is already attached to the shader program."); + throw std::runtime_error("OpenGL shader object already attached to the shader program"); } else { // Check that both the OpenGL shader program and OpenGL shader object are valid if (glIsProgram(gl_program_id) != GL_TRUE) - throw std::runtime_error("OpenGL shader program is not a valid program object."); - if (glIsShader(object->gl_shader_id) != GL_TRUE) - throw std::runtime_error("OpenGL shader object is not a valid shader object."); + { + throw std::runtime_error("Invalid OpenGL shader program"); + } + if (glIsShader(object.gl_shader_id) != GL_TRUE) + { + throw std::runtime_error("Invalid OpenGL shader object"); + } // Attach the OpenGL shader object to the OpenGL shader program - glAttachShader(gl_program_id, object->gl_shader_id); + glAttachShader(gl_program_id, object.gl_shader_id); // Handle OpenGL errors switch (glGetError()) @@ -78,13 +77,13 @@ void shader_program::attach(const shader_object* object) } // Add shader object to set of attached objects - attached_objects.insert(object); + attached_objects.insert(&object); } } -void shader_program::detach(const shader_object* object) +void shader_program::detach(const shader_object& object) { - if (attached_objects.find(object) == attached_objects.end()) + if (attached_objects.find(&object) == attached_objects.end()) { throw std::runtime_error("Shader object is not attached to the shader program."); } @@ -92,12 +91,16 @@ void shader_program::detach(const shader_object* object) { // Check that both the OpenGL shader program and OpenGL shader object are valid if (glIsProgram(gl_program_id) != GL_TRUE) - throw std::runtime_error("OpenGL shader program is not a valid program object."); - if (glIsShader(object->gl_shader_id) != GL_TRUE) - throw std::runtime_error("OpenGL shader object is not a valid shader object."); + { + throw std::runtime_error("Invalid OpenGL shader program"); + } + if (glIsShader(object.gl_shader_id) != GL_TRUE) + { + throw std::runtime_error("Invalid OpenGL shader object"); + } // Detach the OpenGL shader object from the OpenGL shader program - glDetachShader(gl_program_id, object->gl_shader_id); + glDetachShader(gl_program_id, object.gl_shader_id); // Handle OpenGL errors switch (glGetError()) @@ -108,21 +111,29 @@ void shader_program::detach(const shader_object* object) } // Remove shader object from set of attached objects - attached_objects.erase(object); + attached_objects.erase(&object); } } void shader_program::detach_all() { while (!attached_objects.empty()) - detach(*attached_objects.begin()); + { + detach(**attached_objects.begin()); + } } bool shader_program::link() { + m_linked = false; + info_log.clear(); + variable_map.clear(); + // Check that the OpenGL shader program is valid if (glIsProgram(gl_program_id) != GL_TRUE) - throw std::runtime_error("OpenGL shader program is not a valid program object."); + { + throw std::runtime_error("Invalid OpenGL shader program"); + } // Link OpenGL shader program glLinkProgram(gl_program_id); @@ -138,202 +149,179 @@ bool shader_program::link() // Get OpenGL shader program linking status GLint gl_link_status; glGetProgramiv(gl_program_id, GL_LINK_STATUS, &gl_link_status); - linked = (gl_link_status == GL_TRUE); + m_linked = (gl_link_status == GL_TRUE); - // Get OpenGL shader program info log length - GLint gl_info_log_length; - glGetProgramiv(gl_program_id, GL_INFO_LOG_LENGTH, &gl_info_log_length); - - if (gl_info_log_length > 0) + // Populate info log string + if (m_linked) { - // Resize string to accommodate OpenGL shader program info log - info_log.resize(gl_info_log_length); - - // Read OpenGL shader program info log into string - glGetProgramInfoLog(gl_program_id, gl_info_log_length, &gl_info_log_length, info_log.data()); - - // Remove redundant null terminator from string - info_log.pop_back(); + // Load shader variables + load_variables(); } else { - // Empty info log - info_log.clear(); + // Get OpenGL shader program info log length + GLint gl_info_log_length; + glGetProgramiv(gl_program_id, GL_INFO_LOG_LENGTH, &gl_info_log_length); + + if (gl_info_log_length > 0) + { + // Resize string to accommodate OpenGL shader program info log + info_log.resize(gl_info_log_length); + + // Read OpenGL shader program info log into string + glGetProgramInfoLog(gl_program_id, gl_info_log_length, &gl_info_log_length, info_log.data()); + + // Remove redundant null terminator from string + info_log.pop_back(); + } } - // Find shader inputs - find_inputs(); - - return linked; + return m_linked; } -void shader_program::find_inputs() +void shader_program::load_variables() { + variable_map.clear(); + // Get maximum uniform name length GLint max_uniform_name_length = 0; glGetProgramiv(gl_program_id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_length); // Allocate uniform name buffer - GLchar* uniform_name = new GLchar[max_uniform_name_length]; + std::string uniform_name(static_cast(max_uniform_name_length), '\0'); // Get number of active uniforms in the shader GLint active_uniform_count = 0; glGetProgramiv(gl_program_id, GL_ACTIVE_UNIFORMS, &active_uniform_count); - // Set first available texture unit - int available_texture_unit = 0; + // Init texture unit index + GLint texture_index = 0; // For each active uniform - for (GLuint uniform_index = 0; uniform_index < static_cast(active_uniform_count); ++uniform_index) + for (GLint uniform_index = 0; uniform_index < active_uniform_count; ++uniform_index) { - // Get information about uniform + // Get uniform info GLsizei uniform_name_length; GLint uniform_size; GLenum uniform_type; - glGetActiveUniform(gl_program_id, uniform_index, static_cast(max_uniform_name_length), &uniform_name_length, &uniform_size, &uniform_type, &uniform_name[0]); + glGetActiveUniform(gl_program_id, static_cast(uniform_index), static_cast(max_uniform_name_length), &uniform_name_length, &uniform_size, &uniform_type, uniform_name.data()); - // Get name without array symbols - std::string input_name = uniform_name; - std::size_t bracketPos = input_name.find_first_of("["); - if (bracketPos != std::string::npos) + // Get uniform location + const GLint uniform_location = glGetUniformLocation(gl_program_id, uniform_name.c_str()); + if (uniform_location == -1) { - input_name = input_name.substr(0, bracketPos); + throw std::runtime_error("Unable to get shader uniform location"); } - // Determine corresponding shader variable data type - shader_variable_type variable_type; - int texture_unit = -1; - bool unsupported = false; + // Get length of variable name by stripping array notation from uniform name + std::size_t uniform_base_name_length = static_cast(uniform_name_length); + if (std::size_t i = std::string_view(uniform_name.c_str(), uniform_name_length).find_first_of("["); i != std::string::npos) + { + uniform_base_name_length = i; + } + + // Hash variable name to get variable key + const hash::fnv1a32_t variable_key = hash::fnv1a32({uniform_name.c_str(), uniform_base_name_length}); + + // Get size of variable + const std::size_t variable_size = static_cast(uniform_size); + + // Construct shader variable + std::unique_ptr variable; switch (uniform_type) { case GL_BOOL: - variable_type = shader_variable_type::bool1; + variable = std::make_unique(variable_size, uniform_location); break; case GL_BOOL_VEC2: - variable_type = shader_variable_type::bool2; + variable = std::make_unique(variable_size, uniform_location); break; case GL_BOOL_VEC3: - variable_type = shader_variable_type::bool3; + variable = std::make_unique(variable_size, uniform_location); break; case GL_BOOL_VEC4: - variable_type = shader_variable_type::bool4; + variable = std::make_unique(variable_size, uniform_location); break; - + case GL_INT: - variable_type = shader_variable_type::int1; + variable = std::make_unique(variable_size, uniform_location); break; case GL_INT_VEC2: - variable_type = shader_variable_type::int2; + variable = std::make_unique(variable_size, uniform_location); break; case GL_INT_VEC3: - variable_type = shader_variable_type::int3; + variable = std::make_unique(variable_size, uniform_location); break; case GL_INT_VEC4: - variable_type = shader_variable_type::int4; + variable = std::make_unique(variable_size, uniform_location); break; - + case GL_UNSIGNED_INT: - variable_type = shader_variable_type::uint1; + variable = std::make_unique(variable_size, uniform_location); break; case GL_UNSIGNED_INT_VEC2: - variable_type = shader_variable_type::uint2; + variable = std::make_unique(variable_size, uniform_location); break; case GL_UNSIGNED_INT_VEC3: - variable_type = shader_variable_type::uint3; + variable = std::make_unique(variable_size, uniform_location); break; case GL_UNSIGNED_INT_VEC4: - variable_type = shader_variable_type::uint4; + variable = std::make_unique(variable_size, uniform_location); break; - + case GL_FLOAT: - variable_type = shader_variable_type::float1; + variable = std::make_unique(variable_size, uniform_location); break; case GL_FLOAT_VEC2: - variable_type = shader_variable_type::float2; + variable = std::make_unique(variable_size, uniform_location); break; case GL_FLOAT_VEC3: - variable_type = shader_variable_type::float3; + variable = std::make_unique(variable_size, uniform_location); break; case GL_FLOAT_VEC4: - variable_type = shader_variable_type::float4; + variable = std::make_unique(variable_size, uniform_location); break; - + case GL_FLOAT_MAT2: - variable_type = shader_variable_type::float2x2; + variable = std::make_unique(variable_size, uniform_location); break; case GL_FLOAT_MAT3: - variable_type = shader_variable_type::float3x3; + variable = std::make_unique(variable_size, uniform_location); break; case GL_FLOAT_MAT4: - variable_type = shader_variable_type::float4x4; + variable = std::make_unique(variable_size, uniform_location); break; case GL_SAMPLER_1D: case GL_SAMPLER_1D_SHADOW: - variable_type = shader_variable_type::texture_1d; - texture_unit = available_texture_unit; - available_texture_unit += uniform_size; + variable = std::make_unique(variable_size, uniform_location, texture_index); + texture_index += uniform_size; break; - + case GL_SAMPLER_2D: case GL_SAMPLER_2D_SHADOW: - variable_type = shader_variable_type::texture_2d; - texture_unit = available_texture_unit; - available_texture_unit += uniform_size; + variable = std::make_unique(variable_size, uniform_location, texture_index); + texture_index += uniform_size; break; case GL_SAMPLER_3D: - variable_type = shader_variable_type::texture_3d; - texture_unit = available_texture_unit; - available_texture_unit += uniform_size; + variable = std::make_unique(variable_size, uniform_location, texture_index); + texture_index += uniform_size; break; case GL_SAMPLER_CUBE: - variable_type = shader_variable_type::texture_cube; - texture_unit = available_texture_unit; - available_texture_unit += uniform_size; + variable = std::make_unique(variable_size, uniform_location, texture_index); + texture_index += uniform_size; break; default: - unsupported = true; + throw std::runtime_error("Unsupported to shader uniform type"); break; } - // Check if data type is supported - if (unsupported) - { - std::string message = std::string("Shader uniform \"") + std::string(uniform_name) + std::string("\" has unsupported data type."); - throw std::runtime_error(message.c_str()); - } - - // Get uniform location - GLint uniform_location = glGetUniformLocation(gl_program_id, uniform_name); - if (uniform_location == -1) - { - //std::string message = std::string("Unable to get location for uniform \"") + std::string(uniform_name) + std::string("\""); - //throw std::runtime_error(message.c_str()); - } - else - { - // Create new shader input - shader_input* input = new shader_input(this, inputs.size(), uniform_location, input_name, variable_type, uniform_size, texture_unit); - input_map[input_name] = input; - inputs.push_back(input); - } + // Map variable to variable key + variable_map.emplace(variable_key, std::move(variable)); } - - // Free uniform name buffer - delete[] uniform_name; -} - -void shader_program::free_inputs() -{ - for (shader_input* input: inputs) - { - delete input; - } - - inputs.clear(); } } // namespace gl diff --git a/src/engine/gl/shader-program.hpp b/src/engine/gl/shader-program.hpp index 4ebaf91..662d68c 100644 --- a/src/engine/gl/shader-program.hpp +++ b/src/engine/gl/shader-program.hpp @@ -20,16 +20,18 @@ #ifndef ANTKEEPER_GL_SHADER_PROGRAM_HPP #define ANTKEEPER_GL_SHADER_PROGRAM_HPP -#include #include #include #include +#include +#include +#include namespace gl { class shader_object; class rasterizer; -class shader_input; +class shader_variable; /** * Shader program which can be linked to shader objects and executed. @@ -51,6 +53,11 @@ public: */ ~shader_program(); + shader_program(const shader_program&) = delete; + shader_program(shader_program&&) = delete; + shader_program& operator=(const shader_program&) = delete; + shader_program& operator=(shader_program&&) = delete; + /** * Attaches a shader object to the shader program. Attaching a shader object has no effect on a shader program until shader_program::link() is called. * @@ -63,7 +70,7 @@ public: * * @see shader_program::link() */ - void attach(const shader_object* object); + void attach(const shader_object& object); /** * Detaches a shader object from the shader program. Detaching a shader object has no effect on a shader program until shader_program::link() is called. @@ -77,7 +84,7 @@ public: * * @see shader_program::link() */ - void detach(const shader_object* object); + void detach(const shader_object& object); /** * Detaches all shader objects from the shader program. @@ -94,65 +101,64 @@ public: /** * Links all attached shader objects to create an executable shader program. * - * @warning All existing pointers to a shader program's shader inputs will be invalidated if the program is re-linked. + * @return `true` if the attached shader objects were successfully linked into the shader program, `false` otherwise. * - * @return `true` if the attached shader objects were successfully linked into the shader program, `false` otherwise. If linking fails, check the info log via shader_program::get_info_log() for more information. + * @warning All existing of the shader program's variables will be invalidated if the program is re-linked. */ bool link(); - /// Returns the shader program info log, which is updated when the shader program is linked. - const std::string& get_info_log() const; - /// Returns `true` if the shader program has been successfully linked, `false` otherwise. - bool was_linked() const; - - shader_program(const shader_program&) = delete; - shader_program& operator=(const shader_program&) = delete; - - const std::list* get_inputs() const; - const shader_input* get_input(const std::string& name) const; - -private: - friend class rasterizer; - - unsigned int gl_program_id; - std::string info_log; - bool linked; - std::unordered_set attached_objects; - - void find_inputs(); - void free_inputs(); + [[nodiscard]] inline bool linked() const noexcept + { + return m_linked; + } - std::list inputs; - std::unordered_map input_map; + /** + * Returns all active shader variables in the shader program. + * + * @return Map of 32-bit FNV-1a hash values of shader variable names to shader variables. + */ + [[nodiscard]] inline const std::unordered_map>& variables() const noexcept + { + return variable_map; + } -}; - -inline const std::string& shader_program::get_info_log() const -{ - return info_log; -} - -inline bool shader_program::was_linked() const -{ - return linked; -} - -inline const std::list* shader_program::get_inputs() const -{ - return &inputs; -} - -inline const shader_input* shader_program::get_input(const std::string& name) const -{ - auto it = input_map.find(name); - if (it == input_map.end()) + /** + * Returns a pointer to an active shader variable with the given name, or `nullptr` if not found. + * + * @param key 32-bit FNV-1a hash value of a shader variable name. + * + * @return Pointer to the active shader variable with the given name, or `nullptr` if not found. + */ + [[nodiscard]] inline const shader_variable* variable(hash::fnv1a32_t key) const { + if (auto i = variable_map.find(key); i != variable_map.end()) + { + return i->second.get(); + } + return nullptr; } + + /** + * Returns the info log that contains debug information when linking fails. + */ + [[nodiscard]] inline const std::string& info() const noexcept + { + return info_log; + } - return it->second; -} +private: + friend class rasterizer; + + void load_variables(); + + unsigned int gl_program_id{0}; + bool m_linked{false}; + std::unordered_set attached_objects; + std::unordered_map> variable_map; + std::string info_log; +}; } // namespace gl diff --git a/src/engine/gl/shader-stage.hpp b/src/engine/gl/shader-stage.hpp index 975394d..36d4e31 100644 --- a/src/engine/gl/shader-stage.hpp +++ b/src/engine/gl/shader-stage.hpp @@ -20,12 +20,14 @@ #ifndef ANTKEEPER_GL_SHADER_STAGE_HPP #define ANTKEEPER_GL_SHADER_STAGE_HPP +#include + namespace gl { /** * Enumerates all supported shader stages. */ -enum class shader_stage +enum class shader_stage: std::uint8_t { /// Vertex shader stage. vertex, @@ -40,4 +42,3 @@ enum class shader_stage } // namespace gl #endif // ANTKEEPER_GL_SHADER_STAGE_HPP - diff --git a/src/engine/gl/shader-template.cpp b/src/engine/gl/shader-template.cpp new file mode 100644 index 0000000..3e9c217 --- /dev/null +++ b/src/engine/gl/shader-template.cpp @@ -0,0 +1,364 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace gl { + +shader_template::shader_template(const text_file& source_code): + template_source{source_code} +{ + find_directives(); + rehash(); +} + +shader_template::shader_template(text_file&& source_code): + template_source{source_code} +{ + find_directives(); + rehash(); +} + +void shader_template::source(const text_file& source_code) +{ + template_source = source_code; + find_directives(); + rehash(); +} + +void shader_template::source(text_file&& source_code) +{ + template_source = source_code; + find_directives(); + rehash(); +} + +std::string shader_template::configure(gl::shader_stage stage, const dictionary_type& definitions) const +{ + replace_stage_directives(stage); + replace_define_directives(definitions); + + // Join vector of source lines into single string + std::string string; + for (const auto& line: template_source.lines) + { + string += line; + string += '\n'; + } + + return string; +} + +std::unique_ptr shader_template::compile(gl::shader_stage stage, const dictionary_type& definitions) const +{ + // Generate shader object source + const std::string object_source = configure(stage, definitions); + + // Create shader object + std::unique_ptr object = std::make_unique(stage); + + // Set shader object source + object->source(object_source); + + // Compile shader object + object->compile(); + + return object; +} + +std::unique_ptr shader_template::build(const dictionary_type& definitions) const +{ + std::unique_ptr vertex_object; + std::unique_ptr fragment_object; + std::unique_ptr geometry_object; + + // Create shader program + std::unique_ptr program = std::make_unique(); + + if (has_vertex_directive()) + { + // Compile vertex shader object and attach to shader program + vertex_object = compile(gl::shader_stage::vertex, definitions); + program->attach(*vertex_object); + } + + if (has_fragment_directive()) + { + // Compile fragment shader object and attach to shader program + fragment_object = compile(gl::shader_stage::fragment, definitions); + program->attach(*fragment_object); + } + + if (has_geometry_directive()) + { + // Compile fragment shader object and attach to shader program + geometry_object = compile(gl::shader_stage::geometry, definitions); + program->attach(*geometry_object); + } + + // Link attached shader objects into shader program + program->link(); + + // Detach all attached shader objects + program->detach_all(); + + return program; +} + +void shader_template::find_directives() +{ + // Reset directives + vertex_directives.clear(); + fragment_directives.clear(); + geometry_directives.clear(); + define_directives.clear(); + + // Parse directives + for (std::size_t i = 0; i < template_source.lines.size(); ++i) + { + std::istringstream line_stream(template_source.lines[i]); + std::string token; + + // Detect `#pragma` directives + if (line_stream >> token && token == "#pragma") + { + if (line_stream >> token) + { + // Map line numbers of supported directives + if (token == "define") + { + if (line_stream >> token) + { + define_directives.insert({token, i}); + } + } + else if (token == "vertex") + { + vertex_directives.insert(i); + } + else if (token == "fragment") + { + fragment_directives.insert(i); + } + else if (token == "geometry") + { + geometry_directives.insert(i); + } + } + } + } +} + +void shader_template::rehash() +{ + m_hash = 0; + for (const auto& line: template_source.lines) + { + m_hash = hash::combine(m_hash, std::hash{}(line)); + } +} + +void shader_template::replace_stage_directives(gl::shader_stage stage) const +{ + // Determine stage directives according to the shader stage being generated + const char* vertex_directive = (stage == gl::shader_stage::vertex) ? "#define __VERTEX__" : "/* #undef __VERTEX__ */"; + const char* fragment_directive = (stage == gl::shader_stage::fragment) ? "#define __FRAGMENT__" : "/* #undef __FRAGMENT__ */"; + const char* geometry_directive = (stage == gl::shader_stage::geometry) ? "#define __GEOMETRY__" : "/* #undef __GEOMETRY__ */"; + + // Handle `#pragma ` directives + for (const auto directive_line: vertex_directives) + { + template_source.lines[directive_line] = vertex_directive; + } + for (const auto directive_line: fragment_directives) + { + template_source.lines[directive_line] = fragment_directive; + } + for (const auto directive_line: geometry_directives) + { + template_source.lines[directive_line] = geometry_directive; + } +} + +void shader_template::replace_define_directives(const dictionary_type& definitions) const +{ + // For each `#pragma define ` directive + for (const auto& define_directive: define_directives) + { + // Get a reference to the directive line + std::string& line = template_source.lines[define_directive.second]; + + // Check if the corresponding definition was given by the configuration + auto definitions_it = definitions.find(define_directive.first); + if (definitions_it != definitions.end()) + { + // Definition found, replace `#pragma define ` with `#define ` or `#define ` + line = "#define " + define_directive.first; + if (!definitions_it->second.empty()) + { + line += " " + definitions_it->second; + } + } + else + { + // Definition not found, replace `#pragma define ` with the comment `/* #undef */`. + line = "/* #undef " + define_directive.first + " */"; + } + } +} + +bool shader_template::has_vertex_directive() const noexcept +{ + return !vertex_directives.empty(); +} + +bool shader_template::has_fragment_directive() const noexcept +{ + return !fragment_directives.empty(); +} + +bool shader_template::has_geometry_directive() const noexcept +{ + return !geometry_directives.empty(); +} + +bool shader_template::has_define_directive(const std::string& key) const +{ + return (define_directives.find(key) != define_directives.end()); +} + +} // namespace gl + +/** + * Scans a text file for the presence of a `#pragma once` directive. + * + * @param source Text file to scan. + * + * @return `true` if the file contains a `#pragma once` directive, `false` otherwise. + */ +static bool has_pragma_once(const text_file& source) +{ + for (const auto& line: source.lines) + { + std::istringstream line_stream(line); + std::string token; + + // If line contains a `#pragma once` directive + if (line_stream >> token && token == "#pragma") + { + if (line_stream >> token && token == "once") + { + return true; + } + } + } + + return false; +} + +/** + * Handles `#pragma include` directives by loading the specified text files and inserting them in place. + */ +static void handle_includes(text_file& source, std::unordered_set& include_once, resource_manager& resource_manager) +{ + // For each line in the source + for (std::size_t i = 0; i < source.lines.size(); ++i) + { + std::string token; + std::istringstream line_stream(source.lines[i]); + + // If line contains a `#pragma include` directive + if (line_stream >> token && token == "#pragma" && + line_stream >> token && token == "include") + { + // If third token is enclosed in quotes or angled brackets + if (line_stream >> token && token.size() > 2 && + ((token.front() == '\"' && token.back() == '\"') || + (token.front() == '<' && token.back() == '>'))) + { + // Extract include path + const std::filesystem::path path = token.substr(1, token.length() - 2); + + // Skip pre-included files that contain a `#pragma once` directive + if (include_once.contains(path)) + { + source.lines[i] = "/* #pragma exclude " + token + " */"; + continue; + } + + // Load include file + const auto include_file = resource_manager.load(path); + if (!include_file) + { + source.lines[i] = "#error file not found: " + path.string(); + continue; + } + + // If file has `#pragma once` directive + if (has_pragma_once(*include_file)) + { + // Add file to set of files to include once + include_once.insert(path); + } + + // Create a copy of the include file + text_file include_file_copy = *include_file; + + // Handle `#pragma include` directives inside include file + handle_includes(include_file_copy, include_once, resource_manager); + + // Replace #pragma include directive with include file contents + source.lines.erase(source.lines.begin() + i); + source.lines.insert(source.lines.begin() + i, include_file_copy.lines.begin(), include_file_copy.lines.end()); + i += include_file_copy.lines.size() - 1; + } + else + { + source.lines[i] = "#error malformed include directive: \"" + source.lines[i] + "\""; + } + } + } +} + +template <> +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) +{ + // Load shader template source file + const auto source_file = resource_loader::load(resource_manager, ctx); + + // Make a copy of the shader template source file + text_file source_file_copy = *source_file; + + // Handle `#pragma include` directives + std::unordered_set include_once; + include_once.insert(ctx.path()); + handle_includes(source_file_copy, include_once, resource_manager); + + // Construct shader template + return std::make_unique(std::move(source_file_copy)); +} diff --git a/src/engine/render/shader-template.hpp b/src/engine/gl/shader-template.hpp similarity index 73% rename from src/engine/render/shader-template.hpp rename to src/engine/gl/shader-template.hpp index 55daeaf..b5c8814 100644 --- a/src/engine/render/shader-template.hpp +++ b/src/engine/gl/shader-template.hpp @@ -17,17 +17,20 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_RENDER_SHADER_TEMPLATE_HPP -#define ANTKEEPER_RENDER_SHADER_TEMPLATE_HPP +#ifndef ANTKEEPER_GL_SHADER_TEMPLATE_HPP +#define ANTKEEPER_GL_SHADER_TEMPLATE_HPP #include #include +#include #include +#include #include +#include #include #include -namespace render { +namespace gl { /** * Template used to for generating one or more shader variants from a single source. @@ -52,48 +55,55 @@ public: /** * Constructs a shader template and sets its source code. * - * @param source_code String containing the shader template source code. - * - * @see shader_template::source(const std::string&) + * @param source_code Shader template source code. */ - shader_template(const std::string& source_code); + /// @{ + explicit shader_template(const text_file& source_code); + explicit shader_template(text_file&& source_code); + /// @} /** * Constructs an empty shader template. */ - shader_template(); + shader_template() = default; /** * Replaces the source code of the shader template. * - * @param source_code String containing shader template source code. + * @param source_code Shader template source code. */ - void source(const std::string& source_code); + /// @{ + void source(const text_file& source_code); + void source(text_file&& source_code); + /// @} /** - * Configures shader object source code given a shader stage and template dictionary. + * Configures shader object source code for a given shader stage and template dictionary. * * @param stage Shader stage of the shader object to generate. Instances of `#pragma ` in the template source will be replaced with `#define ____`. * @param definitions Container of definitions used to replace `#pragma define ` directives. + * * @return Configured shader object source code. */ - std::string configure(gl::shader_stage stage, const dictionary_type& definitions = {}) const; + [[nodiscard]] std::string configure(gl::shader_stage stage, const dictionary_type& definitions = {}) const; /** * Configures and compiles a shader object. * * @param stage Shader stage of the shader object to generate. Instances of `#pragma ` in the template source will be replaced with `#define ____`. * @param definitions Container of definitions used to replace `#pragma define ` directives. + * * @return Compiled shader object. * * @exception std::runtime_error Any exceptions thrown by gl::shader_object. */ - gl::shader_object* compile(gl::shader_stage stage, const dictionary_type& definitions = {}) const; + [[nodiscard]] std::unique_ptr compile(gl::shader_stage stage, const dictionary_type& definitions = {}) const; /** * Configures and compiles shader objects, then links them into a shader program. Shader object stages are determined according to the presence of `#pragma ` directives. * * @param definitions Container of definitions used to replace `#pragma define ` directives. + * * @return Linked shader program. * * @exception std::runtime_error Any exceptions thrown by gl::shader_object or gl::shader_program. @@ -102,44 +112,44 @@ public: * @see has_fragment_directive() const * @see has_geometry_directive() const */ - gl::shader_program* build(const dictionary_type& definitions = {}) const; + [[nodiscard]] std::unique_ptr build(const dictionary_type& definitions = {}) const; /// Returns `true` if the template source contains one or more `#pragma vertex` directive. - bool has_vertex_directive() const; + [[nodiscard]] bool has_vertex_directive() const noexcept; /// Returns `true` if the template source contains one or more `#pragma fragment` directive. - bool has_fragment_directive() const; + [[nodiscard]] bool has_fragment_directive() const noexcept; /// Returns `true` if the template source contains one or more `#pragma geometry` directive. - bool has_geometry_directive() const; + [[nodiscard]] bool has_geometry_directive() const noexcept; /** * Returns `true` if the template source contains one or more instance of `#pragma define `. * * @param key Definition key. */ - bool has_define_directive(const std::string& key) const; + [[nodiscard]] bool has_define_directive(const std::string& key) const; - /// Returns a hash of the template source. - std::size_t get_hash() const; + /// Returns a hash of the template source code. + [[nodiscard]] inline std::size_t hash() const noexcept + { + return m_hash; + } private: + void find_directives(); + void rehash(); void replace_stage_directives(gl::shader_stage stage) const; void replace_define_directives(const dictionary_type& definitions) const; - mutable std::vector template_source; + mutable text_file template_source; std::unordered_set vertex_directives; std::unordered_set fragment_directives; std::unordered_set geometry_directives; std::multimap define_directives; - std::size_t hash; + std::size_t m_hash{0}; }; -inline std::size_t shader_template::get_hash() const -{ - return hash; -} - -} // namespace render +} // namespace gl -#endif // ANTKEEPER_RENDER_SHADER_TEMPLATE_HPP +#endif // ANTKEEPER_GL_SHADER_TEMPLATE_HPP diff --git a/src/engine/gl/shader-variable-type.hpp b/src/engine/gl/shader-variable-type.hpp index 004a204..76287bc 100644 --- a/src/engine/gl/shader-variable-type.hpp +++ b/src/engine/gl/shader-variable-type.hpp @@ -20,9 +20,14 @@ #ifndef ANTKEEPER_GL_SHADER_VARIABLE_TYPE_HPP #define ANTKEEPER_GL_SHADER_VARIABLE_TYPE_HPP +#include + namespace gl { -enum class shader_variable_type +/** + * Shader variable data types. + */ +enum class shader_variable_type: std::uint8_t { bool1, bool2, @@ -52,4 +57,3 @@ enum class shader_variable_type } // namespace gl #endif // ANTKEEPER_GL_SHADER_VARIABLE_TYPE_HPP - diff --git a/src/engine/gl/shader-variable.cpp b/src/engine/gl/shader-variable.cpp new file mode 100644 index 0000000..6f238e8 --- /dev/null +++ b/src/engine/gl/shader-variable.cpp @@ -0,0 +1,396 @@ +/* + * 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 . + */ + +#include +#include + +namespace gl { + +static const char* type_mismatch_error = "Shader variable type mismatch"; + +shader_variable::shader_variable(std::size_t size) noexcept: + m_size{size} +{} + +void shader_variable::update(bool value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const bool2& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const bool3& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const bool4& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(int value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const int2& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const int3& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const int4& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(unsigned int value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const uint2& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const uint3& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const uint4& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(float value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const float2& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const float3& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const float4& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const float2x2& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const float3x3& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const float4x4& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const texture_1d& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const texture_2d& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const texture_3d& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const texture_cube& value) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(bool value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const bool2& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const bool3& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const bool4& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(int value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const int2& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const int3& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const int4& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(unsigned int value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const uint2& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const uint3& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const uint4& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(float value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const float2& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const float3& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const float4& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const float2x2& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const float3x3& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const float4x4& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const texture_1d& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const texture_2d& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const texture_3d& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(const texture_cube& value, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span> values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span> values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span> values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +void shader_variable::update(std::span> values, std::size_t index) const +{ + throw std::invalid_argument(type_mismatch_error); +} + +} // namespace gl diff --git a/src/engine/gl/shader-variable.hpp b/src/engine/gl/shader-variable.hpp new file mode 100644 index 0000000..5158a2d --- /dev/null +++ b/src/engine/gl/shader-variable.hpp @@ -0,0 +1,195 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_GL_SHADER_INPUT_HPP +#define ANTKEEPER_GL_SHADER_INPUT_HPP + +#include +#include +#include +#include +#include + +namespace gl { + +class texture_1d; +class texture_2d; +class texture_3d; +class texture_cube; + +/** + * Shader program variable. + */ +class shader_variable +{ +public: + /** + * Destructs a shader variable. + */ + virtual ~shader_variable() = default; + + /** + * Returns the shader variable data type. + */ + [[nodiscard]] virtual constexpr shader_variable_type type() const noexcept = 0; + + /** + * Returns the number of elements in an array variable, or `1` if the variable is not an array. + */ + [[nodiscard]] inline std::size_t size() const noexcept + { + return m_size; + } + + /** + * Updates the value of the variable. If the variable is an array, the value of the first element will be updated. + * + * @param value Update value. + * + * @throw std::invalid_argument Shader variable type mismatch. + */ + ///@{ + virtual void update(bool value) const; + virtual void update(const bool2& value) const; + virtual void update(const bool3& value) const; + virtual void update(const bool4& value) const; + + virtual void update(int value) const; + virtual void update(const int2& value) const; + virtual void update(const int3& value) const; + virtual void update(const int4& value) const; + + virtual void update(unsigned int value) const; + virtual void update(const uint2& value) const; + virtual void update(const uint3& value) const; + virtual void update(const uint4& value) const; + + virtual void update(float value) const; + virtual void update(const float2& value) const; + virtual void update(const float3& value) const; + virtual void update(const float4& value) const; + + virtual void update(const float2x2& value) const; + virtual void update(const float3x3& value) const; + virtual void update(const float4x4& value) const; + + virtual void update(const texture_1d& value) const; + virtual void update(const texture_2d& value) const; + virtual void update(const texture_3d& value) const; + virtual void update(const texture_cube& value) const; + ///@} + + /** + * Updates the value of a single element in an array variable. + * + * @param value Update value. + * @param index Index of the element to update. + * + * @throw std::invalid_argument Shader variable type mismatch. + */ + /// @{ + virtual void update(bool value, std::size_t index) const; + virtual void update(const bool2& value, std::size_t index) const; + virtual void update(const bool3& value, std::size_t index) const; + virtual void update(const bool4& value, std::size_t index) const; + + virtual void update(int value, std::size_t index) const; + virtual void update(const int2& value, std::size_t index) const; + virtual void update(const int3& value, std::size_t index) const; + virtual void update(const int4& value, std::size_t index) const; + + virtual void update(unsigned int value, std::size_t index) const; + virtual void update(const uint2& value, std::size_t index) const; + virtual void update(const uint3& value, std::size_t index) const; + virtual void update(const uint4& value, std::size_t index) const; + + virtual void update(float value, std::size_t index) const; + virtual void update(const float2& value, std::size_t index) const; + virtual void update(const float3& value, std::size_t index) const; + virtual void update(const float4& value, std::size_t index) const; + + virtual void update(const float2x2& value, std::size_t index) const; + virtual void update(const float3x3& value, std::size_t index) const; + virtual void update(const float4x4& value, std::size_t index) const; + + virtual void update(const texture_1d& value, std::size_t index) const; + virtual void update(const texture_2d& value, std::size_t index) const; + virtual void update(const texture_3d& value, std::size_t index) const; + virtual void update(const texture_cube& value, std::size_t index) const; + ///@} + + /** + * Updates the values of one or more elements in an array variable. + * + * @param values Contiguous sequence of update values. + * @param index Index of the first element to update. + * + * @throw std::invalid_argument Shader variable type mismatch. + */ + ///@{ + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + virtual void update(std::span values, std::size_t index = 0) const; + + virtual void update(std::span> values, std::size_t index = 0) const; + virtual void update(std::span> values, std::size_t index = 0) const; + virtual void update(std::span> values, std::size_t index = 0) const; + virtual void update(std::span> values, std::size_t index = 0) const; + ///@} + +protected: + /** + * Constructs a shader variable. + * + * @param size Number of elements in the array, or `1` if the variable is not an array. + */ + explicit shader_variable(std::size_t size) noexcept; + +private: + const std::size_t m_size; +}; + +} // namespace gl + +#endif // ANTKEEPER_GL_SHADER_VARIABLE_HPP diff --git a/src/engine/gl/texture-1d.cpp b/src/engine/gl/texture-1d.cpp index 54e858d..b3a89ae 100644 --- a/src/engine/gl/texture-1d.cpp +++ b/src/engine/gl/texture-1d.cpp @@ -21,14 +21,14 @@ namespace gl { -texture_1d::texture_1d(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): +texture_1d::texture_1d(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): texture(width, type, format, color_space, data) {} texture_1d::~texture_1d() {} -void texture_1d::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +void texture_1d::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) { texture::resize(width, type, format, color_space, data); } diff --git a/src/engine/gl/texture-1d.hpp b/src/engine/gl/texture-1d.hpp index dc2f301..b68e7c4 100644 --- a/src/engine/gl/texture-1d.hpp +++ b/src/engine/gl/texture-1d.hpp @@ -30,14 +30,14 @@ namespace gl { class texture_1d: public texture { public: - /// @copydoc texture::texture(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) - texture_1d(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + /// @copydoc texture::texture(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*) + texture_1d(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); /// Destructs a 1D texture. virtual ~texture_1d(); - /// @copydoc texture::resize(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) - virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + /// @copydoc texture::resize(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*) + virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data); /// @copydoc texture::set_wrapping(gl::texture_wrapping) virtual void set_wrapping(gl::texture_wrapping wrap_s); diff --git a/src/engine/gl/texture-2d.cpp b/src/engine/gl/texture-2d.cpp index bbc5bcb..8bab3e6 100644 --- a/src/engine/gl/texture-2d.cpp +++ b/src/engine/gl/texture-2d.cpp @@ -21,19 +21,19 @@ namespace gl { -texture_2d::texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): +texture_2d::texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): texture(width, height, type, format, color_space, data) {} texture_2d::~texture_2d() {} -void texture_2d::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +void texture_2d::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) { texture::resize(width, height, type, format, color_space, data); } -void texture_2d::resize(std::uint16_t width, std::uint16_t height, const void* data) +void texture_2d::resize(std::uint16_t width, std::uint16_t height, const std::byte* data) { texture::resize(width, height, get_pixel_type(), get_pixel_format(), get_color_space(), data); } diff --git a/src/engine/gl/texture-2d.hpp b/src/engine/gl/texture-2d.hpp index 5783b12..f044c2f 100644 --- a/src/engine/gl/texture-2d.hpp +++ b/src/engine/gl/texture-2d.hpp @@ -30,14 +30,14 @@ namespace gl { class texture_2d: public texture { public: - /// @copydoc texture::texture(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) - texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + /// @copydoc texture::texture(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*) + texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); /// Destructs a 2D texture. virtual ~texture_2d(); - /// @copydoc texture::resize(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) - virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + /// @copydoc texture::resize(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*) + virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data); /** * Resizes the texture. @@ -46,7 +46,7 @@ public: * @param height Texture height, in pixels. * @param data Pointer to pixel data. */ - void resize(std::uint16_t width, std::uint16_t height, const void* data); + void resize(std::uint16_t width, std::uint16_t height, const std::byte* data); /// @copydoc texture::set_wrapping(gl::texture_wrapping, gl::texture_wrapping) virtual void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t); diff --git a/src/engine/gl/texture-3d.cpp b/src/engine/gl/texture-3d.cpp index 6a1b9fe..b4b0515 100644 --- a/src/engine/gl/texture-3d.cpp +++ b/src/engine/gl/texture-3d.cpp @@ -21,14 +21,14 @@ namespace gl { -texture_3d::texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): +texture_3d::texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): texture(width, height, depth, type, format, color_space, data) {} texture_3d::~texture_3d() {} -void texture_3d::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +void texture_3d::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) { texture::resize(width, height, depth, type, format, color_space, data); } diff --git a/src/engine/gl/texture-3d.hpp b/src/engine/gl/texture-3d.hpp index e4e75e1..06a5e05 100644 --- a/src/engine/gl/texture-3d.hpp +++ b/src/engine/gl/texture-3d.hpp @@ -30,14 +30,14 @@ namespace gl { class texture_3d: public texture { public: - /// @copydoc texture::texture(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) - texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + /// @copydoc texture::texture(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*) + texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); /// Destructs a 3D texture. virtual ~texture_3d(); - /// @copydoc texture::resize(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) - virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + /// @copydoc texture::resize(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const std::byte*) + virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data); /// @copydoc texture::set_wrapping(gl::texture_wrapping, gl::texture_wrapping, gl::texture_wrapping) virtual void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t, texture_wrapping wrap_r); diff --git a/src/engine/gl/texture-cube.hpp b/src/engine/gl/texture-cube.hpp index af41823..a95191c 100644 --- a/src/engine/gl/texture-cube.hpp +++ b/src/engine/gl/texture-cube.hpp @@ -22,8 +22,6 @@ namespace gl { -class shader_input; - /** * A cube texture which can be uploaded to shaders via shader inputs. */ @@ -43,9 +41,6 @@ public: /// Returns the linear size of a cube face, in pixels. int get_face_size() const; -private: - friend class shader_input; - unsigned int gl_texture_id; int face_size; }; diff --git a/src/engine/gl/texture-filter.hpp b/src/engine/gl/texture-filter.hpp index 5aee9c0..43a2c21 100644 --- a/src/engine/gl/texture-filter.hpp +++ b/src/engine/gl/texture-filter.hpp @@ -20,12 +20,14 @@ #ifndef ANTKEEPER_GL_TEXTURE_FILTER_HPP #define ANTKEEPER_GL_TEXTURE_FILTER_HPP +#include + namespace gl { /** * Texture minification filter modes. */ -enum class texture_min_filter +enum class texture_min_filter: std::uint8_t { nearest, linear, @@ -38,7 +40,7 @@ enum class texture_min_filter /** * Texture magnification filter modes. */ -enum class texture_mag_filter +enum class texture_mag_filter: std::uint8_t { nearest, linear @@ -47,4 +49,3 @@ enum class texture_mag_filter } // namespace gl #endif // ANTKEEPER_GL_TEXTURE_FILTER_HPP - diff --git a/src/engine/gl/texture-wrapping.hpp b/src/engine/gl/texture-wrapping.hpp index cc3e166..73f0437 100644 --- a/src/engine/gl/texture-wrapping.hpp +++ b/src/engine/gl/texture-wrapping.hpp @@ -20,9 +20,11 @@ #ifndef ANTKEEPER_GL_TEXTURE_WRAPPING_HPP #define ANTKEEPER_GL_TEXTURE_WRAPPING_HPP +#include + namespace gl { -enum class texture_wrapping +enum class texture_wrapping: std::uint8_t { clip, extend, @@ -33,4 +35,3 @@ enum class texture_wrapping } // namespace gl #endif // ANTKEEPER_GL_TEXTURE_WRAPPING_HPP - diff --git a/src/engine/gl/texture.cpp b/src/engine/gl/texture.cpp index 682c5c6..5fea1f3 100644 --- a/src/engine/gl/texture.cpp +++ b/src/engine/gl/texture.cpp @@ -18,10 +18,21 @@ */ #include -#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include #include -#include namespace gl { @@ -112,7 +123,7 @@ static constexpr GLenum mag_filter_lut[] = GL_LINEAR }; -texture::texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): +texture::texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): gl_texture_target((depth) ? GL_TEXTURE_3D : (height) ? GL_TEXTURE_2D : GL_TEXTURE_1D), gl_texture_id(0), dimensions({0, 0, 0}), @@ -127,11 +138,11 @@ texture::texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, set_max_anisotropy(max_anisotropy); } -texture::texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): +texture::texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): texture(width, height, 0, type, format, color_space, data) {} -texture::texture(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): +texture::texture(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data): texture(width, 0, 0, type, format, color_space, data) {} @@ -204,7 +215,7 @@ void texture::set_wrapping(gl::texture_wrapping wrap_s) glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); } -void texture::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +void texture::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) { dimensions = {width, height, depth}; pixel_type = type; @@ -246,7 +257,7 @@ void texture::resize(std::uint16_t width, std::uint16_t height, std::uint16_t de glGenerateMipmap(gl_texture_target); glTexParameteriv(gl_texture_target, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle_mask); - /// TODO: remove this + /// @TODO: remove this if (format == pixel_format::d) { glTexParameteri(gl_texture_target, GL_TEXTURE_COMPARE_FUNC, GL_LESS); @@ -254,14 +265,210 @@ void texture::resize(std::uint16_t width, std::uint16_t height, std::uint16_t de } } -void texture::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +void texture::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) { resize(width, height, 0, type, format, color_space, data); } -void texture::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +void texture::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data) { resize(width, 0, 0, type, format, color_space, data); } } // namespace gl + +template <> +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) +{ + // Load JSON data + auto json_data = resource_loader::load(resource_manager, ctx); + + // Read image filename + std::string image_filename; + if (auto element = json_data->find("image"); element != json_data->end()) + image_filename = element.value().get(); + + // Load image + auto image = resource_manager.load<::image>(image_filename); + + // Read color space + gl::color_space color_space = gl::color_space::linear; + if (auto element = json_data->find("color_space"); element != json_data->end()) + { + std::string value = element.value().get(); + if (value == "linear") + color_space = gl::color_space::linear; + else if (value == "srgb") + color_space = gl::color_space::srgb; + } + + // Read extension mode + gl::texture_wrapping wrapping = gl::texture_wrapping::repeat; + if (auto element = json_data->find("extension"); element != json_data->end()) + { + std::string value = element.value().get(); + if (value == "clip") + wrapping = gl::texture_wrapping::clip; + else if (value == "extend") + wrapping = gl::texture_wrapping::extend; + else if (value == "repeat") + wrapping = gl::texture_wrapping::repeat; + else if (value == "mirrored_repeat") + wrapping = gl::texture_wrapping::mirrored_repeat; + } + + // Read interpolation mode + gl::texture_min_filter min_filter = gl::texture_min_filter::linear_mipmap_linear; + gl::texture_mag_filter mag_filter = gl::texture_mag_filter::linear; + if (auto element = json_data->find("interpolation"); element != json_data->end()) + { + std::string value = element.value().get(); + if (value == "linear") + { + min_filter = gl::texture_min_filter::linear_mipmap_linear; + mag_filter = gl::texture_mag_filter::linear; + } + else if (value == "closest") + { + min_filter = gl::texture_min_filter::nearest_mipmap_nearest; + mag_filter = gl::texture_mag_filter::nearest; + } + } + + // Read max anisotropy + float max_anisotropy = 0.0f; + if (auto element = json_data->find("max_anisotropy"); element != json_data->end()) + max_anisotropy = element.value().get(); + + // Determine pixel type + gl::pixel_type type = (image->component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8; + + // Determine pixel format + gl::pixel_format format; + if (image->channel_count() == 1) + { + format = gl::pixel_format::r; + } + else if (image->channel_count() == 2) + { + format = gl::pixel_format::rg; + } + else if (image->channel_count() == 3) + { + format = gl::pixel_format::rgb; + } + else if (image->channel_count() == 4) + { + format = gl::pixel_format::rgba; + } + else + { + throw std::runtime_error(std::format("Texture image has unsupported number of channels ({})", image->channel_count())); + } + + // Create texture + auto texture = std::make_unique(image->width(), type, format, color_space, image->data()); + texture->set_wrapping(wrapping); + texture->set_filters(min_filter, mag_filter); + texture->set_max_anisotropy(max_anisotropy); + + return texture; +} + +template <> +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) +{ + // Load JSON data + auto json_data = resource_loader::load(resource_manager, ctx); + + // Read image filename + std::string image_filename; + if (auto element = json_data->find("image"); element != json_data->end()) + image_filename = element.value().get(); + + // Load image + auto image = resource_manager.load<::image>(image_filename); + + // Read color space + gl::color_space color_space = gl::color_space::linear; + if (auto element = json_data->find("color_space"); element != json_data->end()) + { + std::string value = element.value().get(); + if (value == "linear") + color_space = gl::color_space::linear; + else if (value == "srgb") + color_space = gl::color_space::srgb; + } + + // Read extension mode + gl::texture_wrapping wrapping = gl::texture_wrapping::repeat; + if (auto element = json_data->find("extension"); element != json_data->end()) + { + std::string value = element.value().get(); + if (value == "clip") + wrapping = gl::texture_wrapping::clip; + else if (value == "extend") + wrapping = gl::texture_wrapping::extend; + else if (value == "repeat") + wrapping = gl::texture_wrapping::repeat; + else if (value == "mirrored_repeat") + wrapping = gl::texture_wrapping::mirrored_repeat; + } + + // Read interpolation mode + gl::texture_min_filter min_filter = gl::texture_min_filter::linear_mipmap_linear; + gl::texture_mag_filter mag_filter = gl::texture_mag_filter::linear; + if (auto element = json_data->find("interpolation"); element != json_data->end()) + { + std::string value = element.value().get(); + if (value == "linear") + { + min_filter = gl::texture_min_filter::linear_mipmap_linear; + mag_filter = gl::texture_mag_filter::linear; + } + else if (value == "closest") + { + min_filter = gl::texture_min_filter::nearest_mipmap_nearest; + mag_filter = gl::texture_mag_filter::nearest; + } + } + + // Read max anisotropy + float max_anisotropy = 0.0f; + if (auto element = json_data->find("max_anisotropy"); element != json_data->end()) + max_anisotropy = element.value().get(); + + // Determine pixel type + gl::pixel_type type = (image->component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8; + + // Determine pixel format + gl::pixel_format format; + if (image->channel_count() == 1) + { + format = gl::pixel_format::r; + } + else if (image->channel_count() == 2) + { + format = gl::pixel_format::rg; + } + else if (image->channel_count() == 3) + { + format = gl::pixel_format::rgb; + } + else if (image->channel_count() == 4) + { + format = gl::pixel_format::rgba; + } + else + { + throw std::runtime_error(std::format("Texture image has unsupported number of channels ({})", image->channel_count())); + } + + // Create texture + auto texture = std::make_unique(image->width(), image->height(), type, format, color_space, image->data()); + texture->set_wrapping(wrapping, wrapping); + texture->set_filters(min_filter, mag_filter); + texture->set_max_anisotropy(max_anisotropy); + + return texture; +} diff --git a/src/engine/gl/texture.hpp b/src/engine/gl/texture.hpp index c5e042f..9bc8f03 100644 --- a/src/engine/gl/texture.hpp +++ b/src/engine/gl/texture.hpp @@ -27,12 +27,16 @@ #include #include #include +#include #include namespace gl { class framebuffer; -class shader_input; +class gl_shader_texture_1d; +class gl_shader_texture_2d; +class gl_shader_texture_3d; +class gl_shader_texture_cube; /** * Abstract base class for 1D, 2D, 3D, and cube textures which can be uploaded to shaders via shader inputs. @@ -54,9 +58,9 @@ public: * @warning If the sRGB color space is specified, pixel data will be stored internally as 8 bits per channel, and automatically converted to linear space before reading. */ /// @{ - texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); - texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); - texture(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); + texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); + texture(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const std::byte* data = nullptr); /// @} /** @@ -137,14 +141,17 @@ protected: * @warning If the sRGB color space is specified, pixel data will be stored internally as 8 bits per channel, and automatically converted to linear space before reading. */ /// @{ - virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); - virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); - virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data); + virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data); + virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const std::byte* data); /// @} private: friend class framebuffer; - friend class shader_input; + friend class gl_shader_texture_1d; + friend class gl_shader_texture_2d; + friend class gl_shader_texture_3d; + friend class gl_shader_texture_cube; unsigned int gl_texture_target; unsigned int gl_texture_id; diff --git a/src/engine/gl/vertex-array.cpp b/src/engine/gl/vertex-array.cpp index 1788161..d429e3b 100644 --- a/src/engine/gl/vertex-array.cpp +++ b/src/engine/gl/vertex-array.cpp @@ -38,8 +38,7 @@ static constexpr GLenum vertex_attribute_type_lut[] = GL_DOUBLE }; -vertex_array::vertex_array(): - gl_array_id(0) +vertex_array::vertex_array() { glGenVertexArrays(1, &gl_array_id); } @@ -61,29 +60,31 @@ void vertex_array::bind(attribute_location_type location, const vertex_attribute throw std::invalid_argument("Cannot bind vertex attribute that has an unsupported number of components (" + std::to_string(attribute.components) + ")"); } - attributes[location] = attribute; + m_attributes[location] = attribute; GLenum gl_type = vertex_attribute_type_lut[static_cast(attribute.type)]; glBindVertexArray(gl_array_id); glBindBuffer(GL_ARRAY_BUFFER, attribute.buffer->gl_buffer_id); - glVertexAttribPointer( + glVertexAttribPointer + ( static_cast(location), static_cast(attribute.components), gl_type, GL_FALSE, static_cast(attribute.stride), - (const GLvoid*)attribute.offset); + (const GLvoid*)attribute.offset + ); glEnableVertexAttribArray(static_cast(location)); } void vertex_array::unbind(attribute_location_type location) { - if (auto it = attributes.find(location); it != attributes.end()) + if (auto it = m_attributes.find(location); it != m_attributes.end()) { glBindVertexArray(gl_array_id); glDisableVertexAttribArray(static_cast(location)); - attributes.erase(it); + m_attributes.erase(it); } else { @@ -91,9 +92,4 @@ void vertex_array::unbind(attribute_location_type location) } } -const typename vertex_array::attribute_map_type& vertex_array::get_attributes() const -{ - return attributes; -} - } // namespace gl diff --git a/src/engine/gl/vertex-array.hpp b/src/engine/gl/vertex-array.hpp index 66c565e..6c66b62 100644 --- a/src/engine/gl/vertex-array.hpp +++ b/src/engine/gl/vertex-array.hpp @@ -39,7 +39,7 @@ class vertex_array { public: /// Vertex attribute binding location type. - typedef std::uint_fast32_t attribute_location_type; + typedef unsigned int attribute_location_type; /// Maps vertex attribute to binding locations. typedef std::unordered_map attribute_map_type; @@ -51,7 +51,9 @@ public: ~vertex_array(); vertex_array(const vertex_array&) = delete; + vertex_array(vertex_array&&) = delete; vertex_array& operator=(const vertex_array&) = delete; + vertex_array& operator=(vertex_array&&) = delete; /** * Binds a vertex attribute to the vertex array. @@ -74,13 +76,16 @@ public: void unbind(attribute_location_type location); /// Returns a const reference to the map of vertex attributes bound to this vertex array. - const attribute_map_type& get_attributes() const; + [[nodiscard]] inline const attribute_map_type& attributes() const noexcept + { + return m_attributes; + } private: friend class rasterizer; - attribute_map_type attributes; - std::uint_fast32_t gl_array_id; + unsigned int gl_array_id{0}; + attribute_map_type m_attributes; }; } // namespace gl diff --git a/src/engine/gl/vertex-attribute.hpp b/src/engine/gl/vertex-attribute.hpp index 2af66cf..5c9673b 100644 --- a/src/engine/gl/vertex-attribute.hpp +++ b/src/engine/gl/vertex-attribute.hpp @@ -27,7 +27,7 @@ namespace gl { class vertex_buffer; -enum class vertex_attribute_type +enum class vertex_attribute_type: std::uint8_t { int_8, uint_8, diff --git a/src/engine/gl/vertex-buffer.cpp b/src/engine/gl/vertex-buffer.cpp index dc5961b..4ea9dea 100644 --- a/src/engine/gl/vertex-buffer.cpp +++ b/src/engine/gl/vertex-buffer.cpp @@ -36,19 +36,27 @@ static constexpr GLenum buffer_usage_lut[] = GL_DYNAMIC_COPY }; -vertex_buffer::vertex_buffer(std::size_t size, const void* data, buffer_usage usage): - gl_buffer_id(0), - size(size), - usage(usage) +vertex_buffer::vertex_buffer(buffer_usage usage, std::size_t size, std::span data): + m_usage{usage}, + m_size{size} { - GLenum gl_usage = buffer_usage_lut[static_cast(usage)]; + if (!data.empty()) + { + // Bounds check + if (data.size() < size) + { + throw std::out_of_range("Vertex buffer creation operation exceeded data bounds."); + } + } + + const GLenum gl_usage = buffer_usage_lut[static_cast(m_usage)]; glGenBuffers(1, &gl_buffer_id); glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); - glBufferData(GL_ARRAY_BUFFER, static_cast(size), data, gl_usage); + glBufferData(GL_ARRAY_BUFFER, static_cast(m_size), data.empty() ? nullptr : data.data(), gl_usage); } vertex_buffer::vertex_buffer(): - vertex_buffer(0, nullptr, buffer_usage::static_draw) + vertex_buffer(buffer_usage::static_draw, 0) {} vertex_buffer::~vertex_buffer() @@ -56,47 +64,86 @@ vertex_buffer::~vertex_buffer() glDeleteBuffers(1, &gl_buffer_id); } -void vertex_buffer::repurpose(buffer_usage usage, std::size_t size, const void* data) +void vertex_buffer::repurpose(buffer_usage usage, std::size_t size, std::span data) { - this->size = size; - this->usage = usage; + if (!data.empty()) + { + // Bounds check + if (data.size() < size) + { + throw std::out_of_range("Vertex buffer resize operation exceeded data bounds."); + } + } + + m_usage = usage; + m_size = size; - GLenum gl_usage = buffer_usage_lut[static_cast(usage)]; + const GLenum gl_usage = buffer_usage_lut[static_cast(m_usage)]; glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); - glBufferData(GL_ARRAY_BUFFER, static_cast(size), data, gl_usage); + glBufferData(GL_ARRAY_BUFFER, static_cast(m_size), data.empty() ? nullptr : data.data(), gl_usage); +} + +void vertex_buffer::repurpose(buffer_usage usage, std::span data) +{ + repurpose(usage, m_size, data); } -void vertex_buffer::resize(std::size_t size, const void* data) +void vertex_buffer::resize(std::size_t size, std::span data) { - repurpose(usage, size, data); + repurpose(m_usage, size, data); } -void vertex_buffer::write(std::size_t offset, std::size_t size, const void* data) +void vertex_buffer::write(std::span data, std::size_t offset) { - // Abort empty write operations - if (!size) + // Ignore empty write operations + if (data.empty()) + { return; + } // Bounds check - if (offset + size > this->size) + if (offset + data.size() > m_size) + { throw std::out_of_range("Vertex buffer write operation exceeded buffer bounds."); + } glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); - glBufferSubData(GL_ARRAY_BUFFER, static_cast(offset), static_cast(size), data); + glBufferSubData(GL_ARRAY_BUFFER, static_cast(offset), static_cast(data.size()), data.data()); } -void vertex_buffer::read(std::size_t offset, std::size_t size, void* data) const +void vertex_buffer::read(std::span data, std::size_t offset) const { // Abort empty read operations - if (!size) + if (data.empty()) + { return; + } // Bounds check - if (offset + size > this->size) + if (offset + data.size() > m_size) + { throw std::out_of_range("Vertex buffer read operation exceeded buffer bounds."); + } glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); - glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(offset), static_cast(size), data); + glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(offset), static_cast(data.size()), data.data()); +} + +void vertex_buffer::copy(const vertex_buffer& read_buffer, std::size_t copy_size, std::size_t read_offset, std::size_t write_offset) +{ + // Bounds check + if (read_offset + copy_size > read_buffer.m_size) + { + throw std::out_of_range("Vertex buffer copy operation exceeded read buffer bounds."); + } + if (write_offset + copy_size > m_size) + { + throw std::out_of_range("Vertex buffer copy operation exceeded write buffer bounds."); + } + + glBindBuffer(GL_COPY_READ_BUFFER, read_buffer.gl_buffer_id); + glBindBuffer(GL_COPY_WRITE_BUFFER, gl_buffer_id); + glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, static_cast(read_offset), static_cast(write_offset), static_cast(copy_size)); } } // namespace gl diff --git a/src/engine/gl/vertex-buffer.hpp b/src/engine/gl/vertex-buffer.hpp index dae5cdb..8ab9a55 100644 --- a/src/engine/gl/vertex-buffer.hpp +++ b/src/engine/gl/vertex-buffer.hpp @@ -21,8 +21,8 @@ #define ANTKEEPER_GL_VERTEX_BUFFER_HPP #include -#include #include +#include namespace gl { @@ -35,86 +35,106 @@ class vertex_buffer { public: /** - * Creates a vertex buffer, settings its size, uploading its data, and setting its usage hint. + * Constructs a vertex buffer. * - * @param size Size of the buffer's data, in bytes. - * @param data Pointer to data that will be copied into the buffer, or `nullptr` if no data is to be copied. * @param usage Buffer usage hint. + * @param size Buffer size, in bytes. + * @param data Buffer data. If empty, buffer data will not be set. + * + * @except std::out_of_range Vertex buffer creation operation exceeded data bounds. */ - explicit vertex_buffer(std::size_t size, const void* data = nullptr, buffer_usage usage = buffer_usage::static_draw); + vertex_buffer(buffer_usage usage, std::size_t size, std::span data = {}); - /// Creates an empty vertex buffer. + /** + * Constructs an empty vertex buffer. + */ vertex_buffer(); /// Destroys a vertex buffer. ~vertex_buffer(); - + vertex_buffer(const vertex_buffer&) = delete; + vertex_buffer(vertex_buffer&&) = delete; vertex_buffer& operator=(const vertex_buffer&) = delete; + vertex_buffer& operator=(vertex_buffer&&) = delete; /** - * Repurposes a vertex buffer, changing its usage hint, its size, and replacing its data. + * Repurposes the vertex buffer, changing its usage hint, size, and updating its data. + * + * @param usage New buffer usage hint. + * @param size New buffer size, in bytes. + * @param data New buffer data. If empty, buffer data will not be updated. * - * @param usage New usage hint for the buffer. - * @param size New size of the buffer's data, in bytes. - * @param data Pointer to data that will be copied into the buffer, or `nullptr` if no data is to be copied. + * @except std::out_of_range Vertex buffer resize operation exceeded data bounds. */ - void repurpose(buffer_usage usage, std::size_t size, const void* data = nullptr); + /// @{ + void repurpose(buffer_usage usage, std::size_t size, std::span data = {}); + void repurpose(buffer_usage usage, std::span data = {}); + /// @} /** * Resizes the vertex buffer. * - * @param size New size of the buffer's data, in bytes. - * @param data Pointer to data that will be copied into the buffer, or `nullptr` if no data is to be copied. + * @param size New buffer size, in bytes. + * @param data New buffer data. If empty, buffer data will not be updated. + * + * @except std::out_of_range Vertex buffer resize operation exceeded data bounds. */ - void resize(std::size_t size, const void* data = nullptr); + void resize(std::size_t size, std::span data = {}); /** * Writes data into the vertex buffer. * * @param offset Offset into the buffer's data, in bytes, where writing will begin. - * @param size Size of the write operation, in bytes. - * @param data Pointer to the data that will be written. + * @param data Data to write into the buffer. * * @except std::out_of_range Vertex buffer write operation exceeded buffer bounds. */ - void write(std::size_t offset, std::size_t size, const void* data); + void write(std::span data, std::size_t offset = 0); /** * Reads a subset of the buffer's data from the GL and returns it to the application. * * @param offset Offset into the buffer's data, in bytes, where reading will begin. - * @param size Size of the data to read, in bytes. - * @param data Pointer to where the read bytes will be stored. + * @param data Data buffer where the read bytes will be stored. * * @except std::out_of_range Vertex buffer read operation exceeded buffer bounds. */ - void read(std::size_t offset, std::size_t size, void* data) const; + void read(std::span data, std::size_t offset = 0) const; + + /** + * Copies a subset of another vertex buffer's data into this vertex buffer. + * + * @param read_buffer Buffer from which data will be read. + * @param copy_size Number of bytes to copy from the read buffer into this buffer. + * @param read_offset Offset into the read buffer's data, in bytes, where reading will begin. + * @param write_offset Offset into the this buffer's data, in bytes, where writing will begin. + * + * @except std::out_of_range Vertex buffer copy operation exceeded read buffer bounds. + * @except std::out_of_range Vertex buffer copy operation exceeded write buffer bounds. + */ + void copy(const vertex_buffer& read_buffer, std::size_t copy_size, std::size_t read_offset = 0, std::size_t write_offset = 0); /// Returns the size of the buffer's data, in bytes. - std::size_t get_size() const; + [[nodiscard]] inline std::size_t size() const noexcept + { + return m_size; + } /// Return's the buffer's usage hint. - buffer_usage get_usage() const; + [[nodiscard]] inline buffer_usage usage() const noexcept + { + return m_usage; + } private: friend class vertex_array; - std::uint_fast32_t gl_buffer_id; - std::size_t size; - buffer_usage usage; + unsigned int gl_buffer_id{0}; + buffer_usage m_usage{buffer_usage::static_draw}; + std::size_t m_size{0}; }; -inline std::size_t vertex_buffer::get_size() const -{ - return size; -} - -inline buffer_usage vertex_buffer::get_usage() const -{ - return usage; -} - } // namespace gl #endif // ANTKEEPER_GL_VERTEX_BUFFER_HPP diff --git a/src/engine/i18n/string-map.cpp b/src/engine/i18n/string-map.cpp index 0535092..998900b 100644 --- a/src/engine/i18n/string-map.cpp +++ b/src/engine/i18n/string-map.cpp @@ -18,12 +18,13 @@ */ #include -#include #include #include #include #include +#include #include +#include /** * Serializes a string map. @@ -76,7 +77,7 @@ void deserializer::deserialize(i18n::string_map& map, deserial for (std::uint32_t i = 0; i < size; ++i) { // Read key - std::uint32_t key = 0; + hash::fnv1a32_t key; ctx.read32(reinterpret_cast(&key), 1); // Read string length @@ -95,3 +96,13 @@ void deserializer::deserialize(i18n::string_map& map, deserial ctx.read8(reinterpret_cast(iterator->second.data()), length); } } + +template <> +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) +{ + auto resource = std::make_unique(); + + deserializer().deserialize(*resource, ctx); + + return resource; +} diff --git a/src/engine/i18n/string-map.hpp b/src/engine/i18n/string-map.hpp index efbbc2e..5cf649a 100644 --- a/src/engine/i18n/string-map.hpp +++ b/src/engine/i18n/string-map.hpp @@ -22,13 +22,14 @@ #include #include +#include namespace i18n { /** * Maps 32-bit keys to strings. */ -typedef std::unordered_map string_map; +typedef std::unordered_map string_map; } // namespace i18n diff --git a/src/engine/i18n/string-table.cpp b/src/engine/i18n/string-table.cpp new file mode 100644 index 0000000..d53fd28 --- /dev/null +++ b/src/engine/i18n/string-table.cpp @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +#include +#include +#include + +/** + * Deserializes a string table. + * + * @param[out] file Text file to serialize. + * @param[in,out] ctx Deserialize context. + * + * @throw deserialize_error Read error. + */ +template <> +void deserializer::deserialize(i18n::string_table& table, deserialize_context& ctx) +{ + table.rows.clear(); + + std::vector row; + std::string entry; + + char c; + while (ctx.read8(reinterpret_cast(&c), 1) == 1) + { + if (c == '\t') + { + row.push_back(entry); + entry.clear(); + } + else if (c == '\n') + { + row.push_back(entry); + entry.clear(); + table.rows.push_back(row); + row.clear(); + } + else if (c != '\r') + { + entry.push_back(c); + } + } + + if (!entry.empty()) + { + row.push_back(entry); + } + + if (!row.empty()) + { + table.rows.push_back(row); + } +} + +template <> +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) +{ + auto resource = std::make_unique(); + + deserializer().deserialize(*resource, ctx); + + return resource; +} diff --git a/src/engine/i18n/string-table.hpp b/src/engine/i18n/string-table.hpp index 6b2562e..f35506b 100644 --- a/src/engine/i18n/string-table.hpp +++ b/src/engine/i18n/string-table.hpp @@ -26,14 +26,13 @@ namespace i18n { /** - * A single row in a string table. + * Table of strings. */ -typedef std::vector string_table_row; - -/** - * A table of strings. - */ -typedef std::vector string_table; +struct string_table +{ + /// Rows of column strings. + std::vector> rows; +}; } // namespace i18n diff --git a/src/engine/resources/ephemeris-loader.cpp b/src/engine/physics/orbit/ephemeris.cpp similarity index 66% rename from src/engine/resources/ephemeris-loader.cpp rename to src/engine/physics/orbit/ephemeris.cpp index 23c0530..0691523 100644 --- a/src/engine/resources/ephemeris-loader.cpp +++ b/src/engine/physics/orbit/ephemeris.cpp @@ -17,11 +17,11 @@ * along with Antkeeper source code. If not, see . */ -#include #include -#include +#include #include -#include +#include +#include /// Offset to time data in the JPL DE header, in bytes. static constexpr std::size_t jpl_de_offset_time = 0xA5C; @@ -109,7 +109,7 @@ enum }; /// Number of components for each JPL DE item. -static constexpr std::size_t jpl_de_component_count[jpl_de_max_item_count] = +static constexpr std::uint8_t jpl_de_component_count[jpl_de_max_item_count] = { 3, // Mercury: x,y,z (km) 3, // Venus: x,y,z (km) @@ -128,48 +128,48 @@ static constexpr std::size_t jpl_de_component_count[jpl_de_max_item_count] = 1 // TT-TDB: t (seconds) }; -/// Reads and swaps the byte order of 32-bit numbers. -static PHYSFS_sint64 read_swap32(PHYSFS_File* handle, void* buffer, PHYSFS_uint64 len) -{ - PHYSFS_sint64 status = PHYSFS_readBytes(handle, buffer, len); - for (std::uint32_t* ptr32 = (uint32_t*)buffer; len >= 8; len -= 8, ++ptr32) - *ptr32 = bit::swap32(*ptr32); - return status; -} - -/// Reads and swaps the byte order of 64-bit numbers. -static PHYSFS_sint64 read_swap64(PHYSFS_File* handle, void* buffer, PHYSFS_uint64 len) -{ - PHYSFS_sint64 status = PHYSFS_readBytes(handle, buffer, len); - for (std::uint64_t* ptr64 = (uint64_t*)buffer; len >= 8; len -= 8, ++ptr64) - *ptr64 = bit::swap64(*ptr64); - return status; -} - +/** + * Deserializes an ephemeris. + * + * @param[out] file Text file to serialize. + * @param[in,out] ctx Deserialize context. + * + * @throw deserialize_error Read error. + */ template <> -physics::orbit::ephemeris* resource_loader>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +void deserializer>::deserialize(physics::orbit::ephemeris& ephemeris, deserialize_context& ctx) { + ephemeris.trajectories.clear(); + // Init file reading function pointers - PHYSFS_sint64 (*read32)(PHYSFS_File*, void*, PHYSFS_uint64) = &PHYSFS_readBytes; - PHYSFS_sint64 (*read64)(PHYSFS_File*, void*, PHYSFS_uint64) = &PHYSFS_readBytes; + std::size_t (deserialize_context::*read32)(std::byte*, std::size_t) = &deserialize_context::read32; + std::size_t (deserialize_context::*read64)(std::byte*, std::size_t) = &deserialize_context::read64; // Read DE version number std::int32_t denum; - PHYSFS_seek(file, jpl_de_offset_denum); - PHYSFS_readBytes(file, &denum, sizeof(std::int32_t)); + ctx.seek(jpl_de_offset_denum); + ctx.read8(reinterpret_cast(&denum), sizeof(std::int32_t)); - // If file endianness does not match host + // If file endianness does not match host endianness if (denum & jpl_de_denum_endian_mask) { // Use endian-swapping read functions - read32 = &read_swap32; - read64 = &read_swap64; + if constexpr (std::endian::native == std::endian::little) + { + read32 = &deserialize_context::read32; + read64 = &deserialize_context::read64; + } + else + { + read32 = &deserialize_context::read32; + read64 = &deserialize_context::read64; + } } // Read ephemeris time double ephemeris_time[3]; - PHYSFS_seek(file, jpl_de_offset_time); - read64(file, ephemeris_time, sizeof(double) * 3); + ctx.seek(jpl_de_offset_time); + std::invoke(read64, ctx, reinterpret_cast(ephemeris_time), 3); // Make time relative to J2000 epoch const double epoch = 2451545.0; @@ -178,29 +178,31 @@ physics::orbit::ephemeris* resource_loader(&constant_count), 1); // Read first coefficient table std::int32_t coeff_table[jpl_de_max_item_count][3]; - PHYSFS_seek(file, jpl_de_offset_table1); - read32(file, coeff_table, sizeof(std::int32_t) * jpl_de_table1_count * 3); + ctx.seek(jpl_de_offset_table1); + std::invoke(read32, ctx, reinterpret_cast(coeff_table), jpl_de_table1_count * 3); // Read second coefficient table - PHYSFS_seek(file, jpl_de_offset_table2); - read32(file, &coeff_table[jpl_de_table1_count][0], sizeof(std::int32_t) * jpl_de_table2_count * 3); + ctx.seek(jpl_de_offset_table2); + std::invoke(read32, ctx, reinterpret_cast(&coeff_table[jpl_de_table1_count][0]), jpl_de_table2_count * 3); // Seek past extra constant names if (constant_count > jpl_de_constant_limit) - PHYSFS_seek(file, jpl_de_offset_table3 + (constant_count - jpl_de_constant_limit) * jpl_de_constant_length); + { + ctx.seek(jpl_de_offset_table3 + (constant_count - jpl_de_constant_limit) * jpl_de_constant_length); + } // Read third coefficient table - read32(file, &coeff_table[jpl_de_table1_count + jpl_de_table2_count][0], sizeof(std::int32_t) * jpl_de_table3_count * 3); + std::invoke(read32, ctx, reinterpret_cast(&coeff_table[jpl_de_table1_count + jpl_de_table2_count][0]), jpl_de_table3_count * 3); // Calculate number of coefficients per record std::int32_t record_coeff_count = 0; for (int i = 0; i < jpl_de_max_item_count; ++i) { - std::int32_t coeff_count = coeff_table[i][0] + coeff_table[i][1] * coeff_table[i][2] * jpl_de_component_count[i] - 1; + std::int32_t coeff_count = coeff_table[i][0] + coeff_table[i][1] * coeff_table[i][2] * static_cast(jpl_de_component_count[i]) - 1; record_coeff_count = std::max(record_coeff_count, coeff_count); } @@ -211,16 +213,17 @@ physics::orbit::ephemeris* resource_loader* ephemeris = new physics::orbit::ephemeris(); - ephemeris->resize(11); + // Resize ephemeris to accommodate items 0-10 + ephemeris.trajectories.resize(11); // Init trajectories for (int i = 0; i < 11; ++i) { - auto& trajectory = (*ephemeris)[i]; + auto& trajectory = ephemeris.trajectories[i]; trajectory.t0 = ephemeris_time[0]; trajectory.t1 = ephemeris_time[1]; trajectory.dt = ephemeris_time[2] / static_cast(coeff_table[i][2]); @@ -232,13 +235,21 @@ physics::orbit::ephemeris* resource_loader(&ephemeris.trajectories[j].a[i * strides[j]]), strides[j]); } } +} + +template <> +std::unique_ptr> resource_loader>::load(::resource_manager& resource_manager, deserialize_context& ctx) +{ + auto resource = std::make_unique>(); + + deserializer>().deserialize(*resource, ctx); - return ephemeris; + return resource; } diff --git a/src/engine/physics/orbit/ephemeris.hpp b/src/engine/physics/orbit/ephemeris.hpp index fac57f2..124e875 100644 --- a/src/engine/physics/orbit/ephemeris.hpp +++ b/src/engine/physics/orbit/ephemeris.hpp @@ -21,6 +21,7 @@ #define ANTKEEPER_PHYSICS_ORBIT_EPHEMERIS_HPP #include +#include namespace physics { namespace orbit { @@ -31,7 +32,11 @@ namespace orbit { * @tparam t Real type. */ template -using ephemeris = std::vector>; +struct ephemeris +{ + /// Table of orbital trajectories. + std::vector> trajectories; +}; } // namespace orbit } // namespace physics diff --git a/src/engine/physics/orbit/trajectory.hpp b/src/engine/physics/orbit/trajectory.hpp index 76a0a8c..ebf8e4a 100644 --- a/src/engine/physics/orbit/trajectory.hpp +++ b/src/engine/physics/orbit/trajectory.hpp @@ -56,7 +56,7 @@ struct trajectory * @param t Time, on `[t0, t1)`. * @return Trajectory position at time @p t. */ - math::vector position(T t) const; + [[nodiscard]] math::vector position(T t) const; }; template diff --git a/src/engine/render/compositor.cpp b/src/engine/render/compositor.cpp index cf99089..57fa106 100644 --- a/src/engine/render/compositor.cpp +++ b/src/engine/render/compositor.cpp @@ -37,9 +37,9 @@ void compositor::remove_passes() passes.clear(); } -void compositor::composite(const render::context& ctx, render::queue& queue) const +void compositor::composite(const render::context& ctx, render::queue& queue) { - for (const pass* pass: passes) + for (pass* pass: passes) { if (pass->is_enabled()) { diff --git a/src/engine/render/compositor.hpp b/src/engine/render/compositor.hpp index 04e1538..8345002 100644 --- a/src/engine/render/compositor.hpp +++ b/src/engine/render/compositor.hpp @@ -38,7 +38,7 @@ public: void remove_pass(pass* pass); void remove_passes(); - void composite(const render::context& ctx, render::queue& queue) const; + void composite(const render::context& ctx, render::queue& queue); const std::list* get_passes() const; diff --git a/src/engine/render/blend-mode.hpp b/src/engine/render/material-blend-mode.hpp similarity index 73% rename from src/engine/render/blend-mode.hpp rename to src/engine/render/material-blend-mode.hpp index 10aae86..4479e21 100644 --- a/src/engine/render/blend-mode.hpp +++ b/src/engine/render/material-blend-mode.hpp @@ -17,26 +17,28 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_RENDER_BLEND_MODE_HPP -#define ANTKEEPER_RENDER_BLEND_MODE_HPP +#ifndef ANTKEEPER_RENDER_MATERIAL_BLEND_MODE_HPP +#define ANTKEEPER_RENDER_MATERIAL_BLEND_MODE_HPP + +#include namespace render { /** * Material blend modes. */ -enum class blend_mode +enum class material_blend_mode: std::uint8_t { - /// Fully opaque. + /// Material is fully opaque. opaque, - /// Binary masked opacity. + /// Material has binary masked opacity. masked, - /// Translucent. + /// Material is translucent. translucent }; } // namespace render -#endif // ANTKEEPER_RENDER_BLEND_MODE_HPP +#endif // ANTKEEPER_RENDER_MATERIAL_BLEND_MODE_HPP diff --git a/src/engine/render/material-flags.hpp b/src/engine/render/material-flags.hpp index c73d985..0a9d287 100644 --- a/src/engine/render/material-flags.hpp +++ b/src/engine/render/material-flags.hpp @@ -29,4 +29,3 @@ #define MATERIAL_FLAG_WIREFRAME 0x80000000 #endif // ANTKEEPER_MATERIAL_FLAGS_HPP - diff --git a/src/engine/render/material-property.hpp b/src/engine/render/material-property.hpp deleted file mode 100644 index 92bf022..0000000 --- a/src/engine/render/material-property.hpp +++ /dev/null @@ -1,460 +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 . - */ - -#ifndef ANTKEEPER_RENDER_MATERIAL_PROPERTY_HPP -#define ANTKEEPER_RENDER_MATERIAL_PROPERTY_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace render { - -class material; - -/** - * Abstract base class for material properties. - */ -class material_property_base -{ -public: - /** - * Connects the material property to a shader input. - * - * @param input Shader input to which the material property should be connected. - * @return `true` if the property was connected to the input successfully, `false` otherwise. - */ - bool connect(const gl::shader_input* input); - - /** - * Disconnects the material property from its shader input. - */ - void disconnect(); - - /** - * Sets state 0 = state 1. - */ - virtual void update_tweens() = 0; - - /** - * Uploads the material property to its shader program. - * - * @param a Interpolation factor. Should be on `[0.0, 1.0]`. - * @return `true` if the property was uploaded successfully, `false` otherwise. - */ - virtual bool upload(double a) const = 0; - - /** - * Returns the type of data which the property contains. - */ - virtual gl::shader_variable_type get_data_type() const = 0; - - /** - * Returns `true` if the material property is connected to a shader input, `false` otherwise. - */ - bool is_connected() const; - - /** - * Creates a copy of this material property. - */ - virtual material_property_base* clone() const = 0; - -protected: - material_property_base(); - - const gl::shader_input* input; -}; - -inline bool material_property_base::is_connected() const -{ - return (input != nullptr); -} - -/** - * A property of a material which can be uploaded to a shader program via a shader input. - * - * @tparam T Property data type. - */ -template -class material_property: public material_property_base -{ -public: - typedef tween tween_type; - typedef typename tween::interpolator_type interpolator_type; - - /// Default tween interpolator function for this material property type. - static T default_interpolator(const T& x, const T& y, double a); - - /** - * Creates a material property. - * - * @param element_count Number of elements in the property array. - */ - material_property(std::size_t element_count); - - /** - * Destroys a material property. - */ - virtual ~material_property(); - - material_property(const material_property&) = delete; - material_property& operator=(const material_property&) = delete; - - /// @copydoc material_property_base::update_tweens() - virtual void update_tweens(); - - /// @copydoc material_property_base::upload() const - virtual bool upload(double a) const; - - /** - * Sets the value of this property. - * - * @param value Value to set. - */ - void set_value(const T& value); - - /** - * Sets the value of a single element in this array property. - * - * @param index Index of an array element. - * @param value Value to set. - */ - void set_value(std::size_t index, const T& value); - - /** - * Sets the values of a range of elements in this array property. - * - * @param index Index of the first array element to set. - * @param values Pointer to an array of values to set. - * @param count Number of elements to set. - */ - void set_values(std::size_t index, const T* values, std::size_t count); - - /** - * Sets the tween interpolator function. - * - * @param interpolator Tween interpolator function. - */ - void set_tween_interpolator(interpolator_type interpolator); - - /// Returns the value of the first element in this property. - const T& get_value() const; - - /** - * Returns the value of the first element in this property. - * - * @param index Index of an array element. - * @return Value of the element at the specified index. - */ - const T& get_value(std::size_t index) const; - - /// @copydoc material_property_base::get_data_type() const - virtual gl::shader_variable_type get_data_type() const; - - /// @copydoc material_property_base::clone() const - virtual material_property_base* clone() const; - -private: - std::size_t element_count; - tween* values; -}; - -template -inline T material_property::default_interpolator(const T& x, const T& y, double a) -{ - return y; -} - -template <> -inline float material_property::default_interpolator(const float& x, const float& y, double a) -{ - return math::lerp(x, y, static_cast(a)); -} - -template <> -inline float2 material_property::default_interpolator(const float2& x, const float2& y, double a) -{ - return math::lerp(x, y, static_cast(a)); -} - -template <> -inline float3 material_property::default_interpolator(const float3& x, const float3& y, double a) -{ - return math::lerp(x, y, static_cast(a)); -} - -template <> -inline float4 material_property::default_interpolator(const float4& x, const float4& y, double a) -{ - return math::lerp(x, y, static_cast(a)); -} - -template -material_property::material_property(std::size_t element_count): - element_count(element_count), - values(nullptr) -{ - values = new tween[element_count]; - set_tween_interpolator(default_interpolator); -} - -template -material_property::~material_property() -{ - delete[] values; -} - -template -void material_property::update_tweens() -{ - for (std::size_t i = 0; i < element_count; ++i) - { - values[i].update(); - } -} - -template -bool material_property::upload(double a) const -{ - if (!is_connected()) - { - return false; - } - - if (element_count > 1) - { - for (std::size_t i = 0; i < element_count; ++i) - { - if (!input->upload(i, values[i].interpolate(static_cast(a)))) - return false; - } - - return true; - } - else - { - return input->upload(values[0].interpolate(static_cast(a))); - } -} - -template -void material_property::set_value(const T& value) -{ - values[0][1] = value; -} - -template -void material_property::set_value(std::size_t index, const T& value) -{ - values[index][1] = value; -} - -template -void material_property::set_values(std::size_t index, const T* values, std::size_t count) -{ - for (std::size_t i = 0; i < count; ++i) - { - this->values[index + i][1] = values[i]; - } -} - -template -void material_property::set_tween_interpolator(interpolator_type interpolator) -{ - for (std::size_t i = 0; i < element_count; ++i) - { - this->values[i].set_interpolator(interpolator); - } -} - -template -inline const T& material_property::get_value() const -{ - return values[0][1]; -} - -template -inline const T& material_property::get_value(std::size_t index) const -{ - return values[index][1]; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::bool1; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::bool2; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::bool3; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::bool4; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::int1; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::int2; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::int3; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::int4; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::uint1; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::uint2; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::uint3; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::uint4; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float1; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float2; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float3; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float4; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float2x2; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float3x3; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float4x4; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::texture_1d; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::texture_2d; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::texture_3d; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::texture_cube; -} - -template -material_property_base* material_property::clone() const -{ - material_property* property = new material_property(element_count); - for (std::size_t i = 0; i < element_count; ++i) - { - property->values[i][0] = values[i][0]; - property->values[i][1] = values[i][1]; - } - property->input = input; - - return property; -} - -} // namespace render - -#endif // ANTKEEPER_RENDER_MATERIAL_PROPERTY_HPP diff --git a/src/engine/render/shadow-mode.hpp b/src/engine/render/material-shadow-mode.hpp similarity index 73% rename from src/engine/render/shadow-mode.hpp rename to src/engine/render/material-shadow-mode.hpp index 1a29709..2fb4d72 100644 --- a/src/engine/render/shadow-mode.hpp +++ b/src/engine/render/material-shadow-mode.hpp @@ -17,23 +17,25 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_RENDER_SHADOW_MODE_HPP -#define ANTKEEPER_RENDER_SHADOW_MODE_HPP +#ifndef ANTKEEPER_RENDER_MATERIAL_SHADOW_MODE_HPP +#define ANTKEEPER_RENDER_MATERIAL_SHADOW_MODE_HPP + +#include namespace render { /** * Material shadow casting modes. */ -enum class shadow_mode +enum class material_shadow_mode: std::uint8_t { - /// Fully opaque shadow casting. - opaque, + /// Material does not cast shadows. + none, - /// No shadows cast. - none + /// Material casts fully opaque shadows. + opaque }; } // namespace render -#endif // ANTKEEPER_RENDER_SHADOW_MODE_HPP +#endif // ANTKEEPER_RENDER_MATERIAL_SHADOW_MODE_HPP diff --git a/src/engine/render/material-property.cpp b/src/engine/render/material-variable-type.hpp similarity index 63% rename from src/engine/render/material-property.cpp rename to src/engine/render/material-variable-type.hpp index 3a37f33..a3f9756 100644 --- a/src/engine/render/material-property.cpp +++ b/src/engine/render/material-variable-type.hpp @@ -17,30 +17,43 @@ * along with Antkeeper source code. If not, see . */ -#include -#include +#ifndef ANTKEEPER_RENDER_MATERIAL_VARIABLE_TYPE_HPP +#define ANTKEEPER_RENDER_MATERIAL_VARIABLE_TYPE_HPP -namespace render { - -material_property_base::material_property_base(): - input(nullptr) -{} - -bool material_property_base::connect(const gl::shader_input* input) -{ - if (!input || input->get_data_type() != get_data_type()) - { - return false; - } +#include - this->input = input; - - return true; -} +namespace render { -void material_property_base::disconnect() +/** + * Material variable data types. + */ +enum class material_variable_type: std::uint8_t { - this->input = nullptr; -} + bool1, + bool2, + bool3, + bool4, + int1, + int2, + int3, + int4, + uint1, + uint2, + uint3, + uint4, + float1, + float2, + float3, + float4, + float2x2, + float3x3, + float4x4, + texture_1d, + texture_2d, + texture_3d, + texture_cube +}; } // namespace render + +#endif // ANTKEEPER_RENDER_MATERIAL_VARIABLE_TYPE_HPP diff --git a/src/engine/render/material-variable.hpp b/src/engine/render/material-variable.hpp new file mode 100644 index 0000000..8777cf1 --- /dev/null +++ b/src/engine/render/material-variable.hpp @@ -0,0 +1,373 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_RENDER_MATERIAL_VARIABLE_HPP +#define ANTKEEPER_RENDER_MATERIAL_VARIABLE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +/** + * Abstract base class for material variables. + */ +class material_variable_base +{ +public: + /** + * Destructs a material variable base. + */ + virtual ~material_variable_base() = default; + + /** + * Returns the material variable data type. + */ + [[nodiscard]] virtual constexpr material_variable_type type() const noexcept = 0; + + /** + * Returns the number of elements in an array variable, or `1` if the variable is not an array. + */ + [[nodiscard]] virtual std::size_t size() const noexcept = 0; + + /** + * Creates a copy of this material property. + */ + [[nodiscard]] virtual std::unique_ptr clone() const = 0; +}; + +/** + * Material variable. + * + * @tparam T Material variable value type. + */ +template +class material_variable: public material_variable_base +{ +public: + /// Material variable element type. + using element_type = T; + + /** + * Constructs a material variable. + * + * @param size Number of elements in the array, or `1` if the variable is not an array. + * @param value Value with which to initialize the elements. + */ + inline material_variable(std::size_t size, const element_type& value = element_type()): + elements(size, value) + {} + + /** + * Constructs a material variable with a single element. + */ + inline material_variable(): + material_variable(1) + {} + + /** + * Constructs a material variable from a list of element values. + * + * @param list List of element values. + */ + inline material_variable(std::initializer_list list): + elements(list) + {} + + [[nodiscard]] virtual constexpr material_variable_type type() const noexcept override; + + [[nodiscard]] inline std::size_t size() const noexcept override + { + return elements.size(); + } + + [[nodiscard]] inline std::unique_ptr clone() const override + { + return std::make_unique>(*this); + } + + /** + * Sets the value of the the variable, or the value of the first element if the variable is an array. + * + * @param value Value to set. + */ + inline void set(const element_type& value) + { + elements.front() = value; + } + + /** + * Sets the value of a single element in an array variable. + * + * @param index Index of an element. + * @param value Value to set. + */ + inline void set(std::size_t index, const element_type& value) + { + elements[index] = value; + } + + /** + * Returns a reference to the first element in the array. + */ + [[nodiscard]] inline const element_type& get() const + { + return elements.front(); + } + + /** + * Returns a reference to the element at a given index. + * + * @param index Index of an element. + * + * @return Reference to the element at @p index. + */ + [[nodiscard]] inline const element_type& get(std::size_t index) const + { + return elements[index]; + } + + /** + * Returns a pointer to the element array. + */ + [[nodiscard]] inline const element_type* data() const noexcept + { + return elements.data(); + } + +private: + std::vector elements; +}; + +/// Boolean material variable. +using material_bool = material_variable; + +/// 2-dimensional boolean vector material variable. +using material_bool2 = material_variable; + +/// 3-dimensional boolean vector material variable. +using material_bool3 = material_variable; + +/// 4-dimensional boolean vector material variable. +using material_bool4 = material_variable; + +/// Integer material variable. +using material_int = material_variable; + +/// 2-dimensional integer vector material variable. +using material_int2 = material_variable; + +/// 3-dimensional integer vector material variable. +using material_int3 = material_variable; + +/// 4-dimensional integer vector material variable. +using material_int4 = material_variable; + +/// Unsigned integer material variable. +using material_uint = material_variable; + +/// 2-dimensional unsigned integer vector material variable. +using material_uint2 = material_variable; + +/// 3-dimensional unsigned integer vector material variable. +using material_uint3 = material_variable; + +/// 4-dimensional unsigned integer vector material variable. +using material_uint4 = material_variable; + +/// Floating-point material variable. +using material_float = material_variable; + +/// 2-dimensional floating-point vector material variable. +using material_float2 = material_variable; + +/// 3-dimensional floating-point vector material variable. +using material_float3 = material_variable; + +/// 4-dimensional floating-point vector material variable. +using material_float4 = material_variable; + +/// 2x2 floating-point matrix material variable. +using material_float2x2 = material_variable; + +/// 3x3 floating-point matrix material variable. +using material_float3x3 = material_variable; + +/// 4x4 floating-point matrix material variable. +using material_float4x4 = material_variable; + +/// 1-dimensional texture material variable. +using material_texture_1d = material_variable>; + +/// 2-dimensional texture material variable. +using material_texture_2d = material_variable>; + +/// 3-dimensional texture material variable. +using material_texture_3d = material_variable>; + +/// Cube texture material variable. +using material_texture_cube = material_variable>; + +template <> +inline constexpr material_variable_type material_bool::type() const noexcept +{ + return material_variable_type::bool1; +} + +template <> +inline constexpr material_variable_type material_bool2::type() const noexcept +{ + return material_variable_type::bool2; +} + +template <> +inline constexpr material_variable_type material_bool3::type() const noexcept +{ + return material_variable_type::bool3; +} + +template <> +inline constexpr material_variable_type material_bool4::type() const noexcept +{ + return material_variable_type::bool4; +} + +template <> +inline constexpr material_variable_type material_int::type() const noexcept +{ + return material_variable_type::int1; +} + +template <> +inline constexpr material_variable_type material_int2::type() const noexcept +{ + return material_variable_type::int2; +} + +template <> +inline constexpr material_variable_type material_int3::type() const noexcept +{ + return material_variable_type::int3; +} + +template <> +inline constexpr material_variable_type material_int4::type() const noexcept +{ + return material_variable_type::int4; +} + +template <> +inline constexpr material_variable_type material_uint::type() const noexcept +{ + return material_variable_type::uint1; +} + +template <> +inline constexpr material_variable_type material_uint2::type() const noexcept +{ + return material_variable_type::uint2; +} + +template <> +inline constexpr material_variable_type material_uint3::type() const noexcept +{ + return material_variable_type::uint3; +} + +template <> +inline constexpr material_variable_type material_uint4::type() const noexcept +{ + return material_variable_type::uint4; +} + +template <> +inline constexpr material_variable_type material_float::type() const noexcept +{ + return material_variable_type::float1; +} + +template <> +inline constexpr material_variable_type material_float2::type() const noexcept +{ + return material_variable_type::float2; +} + +template <> +inline constexpr material_variable_type material_float3::type() const noexcept +{ + return material_variable_type::float3; +} + +template <> +inline constexpr material_variable_type material_float4::type() const noexcept +{ + return material_variable_type::float4; +} + +template <> +inline constexpr material_variable_type material_float2x2::type() const noexcept +{ + return material_variable_type::float2x2; +} + +template <> +inline constexpr material_variable_type material_float3x3::type() const noexcept +{ + return material_variable_type::float3x3; +} + +template <> +inline constexpr material_variable_type material_float4x4::type() const noexcept +{ + return material_variable_type::float4x4; +} + +template <> +inline constexpr material_variable_type material_texture_1d::type() const noexcept +{ + return material_variable_type::texture_1d; +} + +template <> +inline constexpr material_variable_type material_texture_2d::type() const noexcept +{ + return material_variable_type::texture_2d; +} + +template <> +inline constexpr material_variable_type material_texture_3d::type() const noexcept +{ + return material_variable_type::texture_3d; +} + +template <> +inline constexpr material_variable_type material_texture_cube::type() const noexcept +{ + return material_variable_type::texture_cube; +} + +} // namespace render + +#endif // ANTKEEPER_RENDER_MATERIAL_VARIABLE_HPP diff --git a/src/engine/render/material.cpp b/src/engine/render/material.cpp index d5a7579..05da3e8 100644 --- a/src/engine/render/material.cpp +++ b/src/engine/render/material.cpp @@ -18,140 +18,517 @@ */ #include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace render { -material::material(gl::shader_program* program): - program(program), - flags(0), - blend_mode(blend_mode::opaque), - opacity_threshold(0.5f), - two_sided(false), - shadow_mode(shadow_mode::opaque) -{} - -material::material(): - material(nullptr) -{} - material::material(const material& other) { *this = other; } -material::~material() +material& material::operator=(const material& other) { - for (material_property_base* property: properties) + two_sided = other.two_sided; + blend_mode = other.blend_mode; + shadow_mode = other.shadow_mode; + flags = other.flags; + shader_template = other.shader_template; + + variable_map.clear(); + for (const auto& [key, value]: other.variable_map) { - delete property; + if (value) + { + variable_map.emplace(key, value->clone()); + } } + + m_hash = other.m_hash; + + return *this; } -material& material::operator=(const material& other) +void material::set_two_sided(bool two_sided) noexcept { - // Remove all properties - for (material_property_base* property: properties) - { - delete property; - } - properties.clear(); - property_map.clear(); + this->two_sided = two_sided; + + rehash(); +} - 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(); - properties.push_back(property); - property_map[it->first] = property; - } +void material::set_blend_mode(material_blend_mode mode) noexcept +{ + blend_mode = mode; + + rehash(); +} - return *this; +void material::set_shadow_mode(material_shadow_mode mode) noexcept +{ + shadow_mode = mode; + + rehash(); +} + +void material::set_flags(std::uint32_t flags) noexcept +{ + this->flags = flags; + + rehash(); +} + +void material::set_shader_template(std::shared_ptr shader_template) +{ + this->shader_template = shader_template; + + rehash(); } -void material::update_tweens() +void material::set_variable(hash::fnv1a32_t key, std::shared_ptr value) { - for (material_property_base* property: properties) + variable_map[key] = std::move(value); +} + +std::shared_ptr material::get_variable(hash::fnv1a32_t key) const +{ + if (auto i = variable_map.find(key); i != variable_map.end()) { - property->update_tweens(); + return i->second; } + + return nullptr; } -std::size_t material::upload(double a) const +void material::rehash() noexcept { - if (!program) + m_hash = 0; + if (shader_template) { - return false; + m_hash = shader_template->hash(); } + + m_hash = hash::combine(m_hash, std::hash{}(two_sided)); + m_hash = hash::combine(m_hash, std::hash{}(blend_mode)); + m_hash = hash::combine(m_hash, std::hash{}(shadow_mode)); + m_hash = hash::combine(m_hash, std::hash{}(flags)); +} - std::size_t failed_upload_count = 0; +} // namespace render - for (material_property_base* property: properties) +template +static bool read_value(T* value, const nlohmann::json& json, const std::string& name) +{ + if (auto element = json.find(name); element != json.end()) { - if (!property->upload(a)) - { - ++failed_upload_count; - } + *value = element.value().get(); + return true; } - - return failed_upload_count; + + return false; } -void material::set_shader_program(gl::shader_program* program) +static bool load_texture_1d_property(resource_manager& resource_manager, render::material& material, hash::fnv1a32_t key, const nlohmann::json& json) { - this->program = program; - reconnect_properties(); + // If JSON element is an array + if (json.is_array()) + { + // Create variable + auto variable = std::make_shared(json.size()); + + // Load textures + std::size_t i = 0; + for (const auto& element: json) + { + variable->set(i, resource_manager.load(element.get())); + ++i; + } + + material.set_variable(key, variable); + } + else + { + // Create variable + auto variable = std::make_shared(json.size()); + + // Load texture + variable->set(resource_manager.load(json.get())); + + material.set_variable(key, variable); + } + + return true; } -void material::set_flags(std::uint32_t flags) noexcept +static bool load_texture_2d_property(resource_manager& resource_manager, render::material& material, hash::fnv1a32_t key, const nlohmann::json& json) { - this->flags = flags; + // If JSON element is an array + if (json.is_array()) + { + // Create variable + auto variable = std::make_shared(json.size()); + + // Load textures + std::size_t i = 0; + for (const auto& element: json) + { + variable->set(i, resource_manager.load(element.get())); + ++i; + } + + material.set_variable(key, variable); + } + else + { + // Create variable + auto variable = std::make_shared(json.size()); + + // Load texture + variable->set(resource_manager.load(json.get())); + + material.set_variable(key, variable); + } + + return true; } -void material::set_blend_mode(render::blend_mode mode) noexcept +static bool load_texture_cube_property(resource_manager& resource_manager, render::material& material, hash::fnv1a32_t key, const nlohmann::json& json) { - blend_mode = mode; + return false; } -void material::set_opacity_threshold(float threshold) noexcept +template +static bool load_scalar_property(render::material& material, hash::fnv1a32_t key, const nlohmann::json& json) { - opacity_threshold = threshold; + // If JSON element is an array + if (json.is_array()) + { + // Determine size of the array + std::size_t array_size = json.size(); + + // Create variable + auto variable = std::make_shared>(json.size()); + + // Set variable values + std::size_t i = 0; + for (const auto& element: json) + { + variable->set(i, element.get()); + } + + material.set_variable(key, variable); + } + else + { + material.set_variable(key, std::make_shared>(1, json.get())); + } + + return true; } -void material::set_two_sided(bool two_sided) noexcept +template +static bool load_vector_property(render::material& material, hash::fnv1a32_t key, std::size_t vector_size, const nlohmann::json& json) { - this->two_sided = two_sided; + // If JSON element is an array of arrays + if (json.is_array() && json.begin().value().is_array()) + { + // Determine size of the array + std::size_t array_size = json.size(); + + // Create variable + auto variable = std::make_shared>(json.size()); + + // For each vector in the array + std::size_t i = 0; + for (const auto& vector_element: json) + { + // Read vector elements + T value; + std::size_t j = 0; + for (const auto& value_element: vector_element) + value[j++] = value_element.get(); + + variable->set(i, value); + + ++i; + } + + material.set_variable(key, variable); + } + else + { + // Read vector elements + T value; + std::size_t i = 0; + for (const auto& value_element: json) + value[i++] = value_element.get(); + + material.set_variable(key, std::make_shared>(1, value)); + } + + return true; } -void material::set_shadow_mode(render::shadow_mode mode) noexcept +template +static bool load_matrix_property(render::material& material, hash::fnv1a32_t key, std::size_t column_count, std::size_t row_count, const nlohmann::json& json) { - shadow_mode = mode; + // If JSON element is an array of arrays of arrays + if (json.is_array() && json.begin().value().is_array()) + { + if (json.begin().value().begin().value().is_array()) + { + // Create variable + auto variable = std::make_shared>(json.size()); + + // For each matrix in the array + std::size_t i = 0; + for (const auto& matrix_element: json) + { + // Read vector elements + T value; + std::size_t j = 0; + for (const auto& column_element: matrix_element) + { + std::size_t k = 0; + for (const auto& row_element: column_element) + { + value[j][k] = row_element.get(); + ++k; + } + + ++j; + } + + // Set matrix value + variable->set(i, value); + + ++i; + } + + material.set_variable(key, variable); + + return true; + } + else + { + // Read matrix elements + T value; + std::size_t i = 0; + for (const auto& column_element: json) + { + std::size_t j = 0; + for (const auto& row_element: column_element) + { + value[i][j] = row_element.get(); + ++j; + } + + ++i; + } + + material.set_variable(key, std::make_shared>(1, value)); + + return true; + } + } + + return false; } -std::size_t material::reconnect_properties() +template <> +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { - std::size_t disconnected_property_count = properties.size(); - - for (auto it = property_map.begin(); it != property_map.end(); ++it) + auto material = std::make_unique(); + + // Load JSON data + auto json = resource_loader::load(resource_manager, ctx); + + // Read two sided + bool two_sided = false; + read_value(&two_sided, *json, "two_sided"); + material->set_two_sided(two_sided); + + // Read blend mode + std::string blend_mode; + read_value(&blend_mode, *json, "blend_mode"); + if (blend_mode == "opaque") { - material_property_base* property = it->second; - - property->disconnect(); - - if (program != nullptr) + material->set_blend_mode(render::material_blend_mode::opaque); + } + else if (blend_mode == "masked") + { + material->set_blend_mode(render::material_blend_mode::masked); + } + else if (blend_mode == "translucent") + { + material->set_blend_mode(render::material_blend_mode::translucent); + } + + // Read shadow mode + std::string shadow_mode; + read_value(&shadow_mode, *json, "shadow_mode"); + if (shadow_mode == "opaque") + { + material->set_shadow_mode(render::material_shadow_mode::opaque); + } + else if (shadow_mode == "none") + { + material->set_shadow_mode(render::material_shadow_mode::none); + } + + // Init material flags + std::uint32_t flags = 0; + + // Read depth mode + std::string depth_mode; + read_value(&depth_mode, *json, "depth_mode"); + if (depth_mode == "in_front") + flags |= MATERIAL_FLAG_X_RAY; + + // Read decal mode + std::string decal_mode; + read_value(&decal_mode, *json, "decal_mode"); + if (decal_mode == "decal") + flags |= MATERIAL_FLAG_DECAL; + else if (decal_mode == "surface") + flags |= MATERIAL_FLAG_DECAL_SURFACE; + + // Set material flags + material->set_flags(flags); + + // Read shader template filename + std::string shader_template_filename; + if (read_value(&shader_template_filename, *json, "shader_template")) + { + // Loader shader template + material->set_shader_template(resource_manager.load(shader_template_filename)); + } + + // Read material variables + if (auto variables_element = json->find("variables"); variables_element != json->end()) + { + for (const auto& variable_element: variables_element.value()) { - if (property->connect(program->get_input(it->first))) + // Read variable name + std::string name; + if (!read_value(&name, variable_element, "name")) + { + // Ignore nameless properties + continue; + } + + // Read variable type + std::string type; + if (!read_value(&type, variable_element, "type")) + { + // Ignore typeless properties + continue; + } + + // Find value element + auto value_element = variable_element.find("value"); + if (value_element == variable_element.end()) + { + // Ignore valueless properties + continue; + } + + // Hash variable name + const hash::fnv1a32_t key = hash::fnv1a32(name); + + if (type == "texture_1d") { - --disconnected_property_count; + load_texture_1d_property(resource_manager, *material, key, value_element.value()); + } + else if (type == "texture_2d") + { + load_texture_2d_property(resource_manager, *material, key, value_element.value()); + } + else if (type == "texture_cube") + { + load_texture_cube_property(resource_manager, *material, key, value_element.value()); + } + // If variable type is a matrix + else if (type[type.size() - 2] == 'x' && + std::isdigit(type[type.size() - 3]) && + std::isdigit(type.back())) + { + std::size_t columns = std::stoul(type.substr(type.size() - 3, 1)); + std::size_t rows = std::stoul(type.substr(type.size() - 1, 1)); + + if (type.find("float") != std::string::npos) + { + if (columns == 2 && rows == 2) + load_matrix_property(*material, key, columns, rows, value_element.value()); + else if (columns == 3 && rows == 3) + load_matrix_property(*material, key, columns, rows, value_element.value()); + else if (columns == 4 && rows == 4) + load_matrix_property(*material, key, columns, rows, value_element.value()); + } + } + // If variable type is a vector + else if (std::isdigit(type.back())) + { + std::size_t size = std::stoul(type.substr(type.size() - 1, 1)); + + if (type.find("float") != std::string::npos) + { + if (size == 2) + load_vector_property(*material, key, size, value_element.value()); + else if (size == 3) + load_vector_property(*material, key, size, value_element.value()); + else if (size == 4) + load_vector_property(*material, key, size, value_element.value()); + } + else if (type.find("uint") != std::string::npos) + { + if (size == 2) + load_vector_property(*material, key, size, value_element.value()); + else if (size == 3) + load_vector_property(*material, key, size, value_element.value()); + else if (size == 4) + load_vector_property(*material, key, size, value_element.value()); + } + else if (type.find("int") != std::string::npos) + { + if (size == 2) + load_vector_property(*material, key, size, value_element.value()); + else if (size == 3) + load_vector_property(*material, key, size, value_element.value()); + else if (size == 4) + load_vector_property(*material, key, size, value_element.value()); + } + else if (type.find("bool") != std::string::npos) + { + if (size == 2) + load_vector_property(*material, key, size, value_element.value()); + else if (size == 3) + load_vector_property(*material, key, size, value_element.value()); + else if (size == 4) + load_vector_property(*material, key, size, value_element.value()); + } + } + // If variable type is a scalar + else + { + if (type.find("float") != std::string::npos) + load_scalar_property(*material, key, value_element.value()); + else if (type.find("uint") != std::string::npos) + load_scalar_property(*material, key, value_element.value()); + else if (type.find("int") != std::string::npos) + load_scalar_property(*material, key, value_element.value()); + else if (type.find("bool") != std::string::npos) + load_scalar_property(*material, key, value_element.value()); } } } - - return disconnected_property_count; + + return material; } - -} // namespace render diff --git a/src/engine/render/material.hpp b/src/engine/render/material.hpp index 2e1122c..edf7b9f 100644 --- a/src/engine/render/material.hpp +++ b/src/engine/render/material.hpp @@ -20,15 +20,13 @@ #ifndef ANTKEEPER_RENDER_MATERIAL_HPP #define ANTKEEPER_RENDER_MATERIAL_HPP -#include -#include -#include -#include #include -#include -#include +#include +#include +#include +#include +#include #include -#include namespace render { @@ -39,29 +37,22 @@ class material { public: /** - * Creates a material. - * - * @param program Shader program with which to associate this material. - */ - explicit material(gl::shader_program* program); - - /** - * Creates a material. + * Constructs a material. */ - material(); - + material() = default; + /** - * Creates a copy of another material. + * Constructs a copy of another material. * * @param other Material to copy. */ material(const material& other); - + /** * Destroys a material. */ - ~material(); - + ~material() = default; + /** * Makes this material a copy of aother material. * @@ -70,26 +61,30 @@ public: */ material& operator=(const material& other); + /// @name Settings + /// @{ + /** - * Sets state 0 = state 1 for each material property tween. + * 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 update_tweens(); - + void set_two_sided(bool two_sided) noexcept; + /** - * Uploads each material property to the material's shader program. + * Sets the material blend mode. * - * @param a Interpolation factor. Should be on `[0.0, 1.0]`. - * @return Number of material property uploads which failed. + * @param mode Blend mode. */ - std::size_t upload(double a) const; - + void set_blend_mode(material_blend_mode mode) noexcept; + /** - * Sets the material's shader program and reconnects all shader properties to their corresponding shader inputs. + * Sets the material shadow mode. * - * @param program Shader program with which to associate the material. + * @param mode Shadow mode. */ - void set_shader_program(gl::shader_program* program); - + void set_shadow_mode(material_shadow_mode mode) noexcept; + /** * Sets the material flags. * @@ -97,158 +92,110 @@ public: */ void set_flags(std::uint32_t flags) noexcept; + /// Returns `true` if the material surface is two-sided, and `false` otherwise. + [[nodiscard]] inline bool is_two_sided() const noexcept + { + return two_sided; + } + + /// Returns the material blend mode. + [[nodiscard]] inline material_blend_mode get_blend_mode() const noexcept + { + return blend_mode; + } + + /// Returns the material shadow mode. + [[nodiscard]] inline material_shadow_mode get_shadow_mode() const noexcept + { + return shadow_mode; + } + + /// Returns the material flags. + [[nodiscard]] inline std::uint32_t get_flags() const noexcept + { + return flags; + } + + /// @} + + /// @name Shading + /// @{ + /** - * Sets the material blend mode. + * Sets the material's shader template. * - * @param mode Blend mode. + * @param shader_template Shader template with which to associate the material. */ - void set_blend_mode(blend_mode mode) noexcept; + void set_shader_template(std::shared_ptr shader_template); /** - * 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 + * Returns the shader template with which this material is associated. */ - void set_opacity_threshold(float threshold) noexcept; + [[nodiscard]] inline const std::shared_ptr& get_shader_template() const noexcept + { + return shader_template; + } /** - * Enables or disables back-face culling of the material surface. + * Sets the value of a material variable with the given name. * - * @param two_sided `true` to disable back-face culling, or `false` to enable it. + * @param key 32-bit FNV-1a hash value of the variable name. + * @param value Shared pointer to the material variable value. */ - void set_two_sided(bool two_sided) noexcept; + void set_variable(hash::fnv1a32_t key, std::shared_ptr value); /** - * Sets the material shadow mode. + * Returns a shared pointer to the material variable with the given name, or `nullptr` if not found. * - * @param mode Shadow mode. - */ - void set_shadow_mode(shadow_mode mode) noexcept; - - /** - * Adds a material array property to the material. + * @param key 32-bit FNV-1a hash value of the variable name. * - * @param name Name of the material array property. - * @param element_count Number of elements in the array. - * @return Pointer to the added material property. + * @return Shared pointer to the material variable with the given name, or `nullptr` if not found. */ - template - material_property* add_property(const std::string& name, std::size_t element_count = 1); - + [[nodiscard]] std::shared_ptr get_variable(hash::fnv1a32_t key) const; + /** - * Returns the shader program with which this material is associated. + * Returns all material variables. + * + * @return Map of 32-bit FNV-1a hash values of variable names to variables. */ - gl::shader_program* get_shader_program() 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; + [[nodiscard]] inline const std::unordered_map>& get_variables() const noexcept + { + return variable_map; + } - /// 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. - */ - material_property_base* get_property(const std::string& name) const; - - /** - * Returns a list of all material properties in the material. + * Returns a hash of the material state. + * + * The followings functions may change the material hash: + * + * * material::set_shader_template + * * material::set_flags + * * material::set_blend_mode + * * material::set_two_sided + * * material::set_shadow_mode */ - const std::list* get_properties() const; - + [[nodiscard]] inline std::size_t hash() const noexcept + { + return m_hash; + } + private: /** - * Attempts to reconnect all material properties to their corresponding shader inputs. - * - * @return Number of disconnected properties. + * Recalculates the material state hash. */ - std::size_t reconnect_properties(); - - gl::shader_program* program; - std::uint32_t flags; - blend_mode blend_mode; - float opacity_threshold; - bool two_sided; - shadow_mode shadow_mode; - std::list properties; - std::unordered_map property_map; + void rehash() noexcept; + + bool two_sided{false}; + material_blend_mode blend_mode{material_blend_mode::opaque}; + material_shadow_mode shadow_mode{material_shadow_mode::opaque}; + std::uint32_t flags{0}; + std::shared_ptr shader_template; + std::unordered_map> variable_map; + std::size_t m_hash{0}; }; -template -material_property* material::add_property(const std::string& name, std::size_t element_count) -{ - // Allocate property - material_property* property = new material_property(element_count); - - // Add to property list and map - properties.push_back(property); - property_map[name] = property; - - // Attempt to connect property to its corresponding shader input - if (program) - { - property->connect(program->get_input(name)); - } - - return property; -} - -inline gl::shader_program* material::get_shader_program() const -{ - return program; -} - -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()) - { - return it->second; - } - - return nullptr; -} - -inline const std::list* material::get_properties() const -{ - return &properties; -} - } // namespace render #endif // ANTKEEPER_RENDER_MATERIAL_HPP diff --git a/src/engine/render/model.cpp b/src/engine/render/model.cpp index 9b3dc88..a1b653f 100644 --- a/src/engine/render/model.cpp +++ b/src/engine/render/model.cpp @@ -18,103 +18,322 @@ */ #include +#include +#include +#include +#include +#include +#include +#include +#include namespace render { -model::model(): - bounds({0, 0, 0}, {0, 0, 0}) -{} - -model::~model() +model::model() { - for (model_group* group: groups) - { - delete group; - } + vertex_array = std::make_shared(); + vertex_buffer = std::make_shared(); } -model_group* model::add_group(const std::string& name) +} // namespace render + +inline constexpr std::uint16_t vertex_attribute_position = 0b0000000000000001; +inline constexpr std::uint16_t vertex_attribute_uv = 0b0000000000000010; +inline constexpr std::uint16_t vertex_attribute_normal = 0b0000000000000100; +inline constexpr std::uint16_t vertex_attribute_tangent = 0b0000000000001000; +inline constexpr std::uint16_t vertex_attribute_color = 0b0000000000010000; +inline constexpr std::uint16_t vertex_attribute_bone = 0b0000000000100000; +inline constexpr std::uint16_t vertex_attribute_barycentric = 0b0000000001000000; +inline constexpr std::uint16_t vertex_attribute_morph_target = 0b0000000010000000; +inline constexpr std::uint16_t vertex_attribute_index = 0b0000000100000000; + +template <> +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { - if (!name.empty()) + // Read vertex format + std::uint16_t vertex_format_flags = 0; + ctx.read16(reinterpret_cast(&vertex_format_flags), 1); + + // Read bone per vertex (if any) + std::uint8_t bones_per_vertex = 0; + if (vertex_format_flags & vertex_attribute_bone) { - if (auto it = group_map.find(name); it != group_map.end()) - { - return it->second; - } + ctx.read8(reinterpret_cast(&bones_per_vertex), 1); } - - model_group* group = new model_group(); - group->index = groups.size(); - group->name = name; - group->material = nullptr; - group->drawing_mode = gl::drawing_mode::triangles; - group->start_index = 0; - group->index_count = 0; - - groups.push_back(group); - - if (!name.empty()) + + // Read vertex count + std::uint32_t vertex_count = 0; + ctx.read32(reinterpret_cast(&vertex_count), 1); + + // Determine vertex size + std::size_t vertex_size = 0; + if (vertex_format_flags & vertex_attribute_position) { - group_map[name] = group; + vertex_size += sizeof(float) * 3; } - - return group; -} - -bool model::remove_group(const std::string& name) -{ - if (auto it = group_map.find(name); it != group_map.end()) + if (vertex_format_flags & vertex_attribute_uv) { - return remove_group(it->second); + vertex_size += sizeof(float) * 2; } - - return false; -} - -bool model::remove_group(model_group* group) -{ - // Remove from group map - if (!group->name.empty()) + if (vertex_format_flags & vertex_attribute_normal) + { + vertex_size += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_tangent) + { + vertex_size += sizeof(float) * 4; + } + if (vertex_format_flags & vertex_attribute_color) + { + vertex_size += sizeof(float) * 4; + } + if (vertex_format_flags & vertex_attribute_bone) + { + vertex_size += sizeof(std::uint32_t) * bones_per_vertex; + vertex_size += sizeof(float) * bones_per_vertex; + } + if (vertex_format_flags & vertex_attribute_barycentric) + { + vertex_size += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_morph_target) + { + vertex_size += sizeof(float) * 3; + } + + // Allocate vertex data + std::vector vertex_data(vertex_count * vertex_size); + + // Read vertices + if constexpr (std::endian::native == std::endian::little) { - if (auto it = group_map.find(group->name); it != group_map.end()) + ctx.read8(vertex_data.data(), vertex_count * vertex_size); + } + else + { + std::byte* vertex_data_offset = vertex_data.data(); + for (std::uint32_t i = 0; i < vertex_count; ++i) { - group_map.erase(it); + if (vertex_format_flags & vertex_attribute_position) + { + ctx.read32(vertex_data_offset, 3); + vertex_data_offset += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_uv) + { + ctx.read32(vertex_data_offset, 2); + vertex_data_offset += sizeof(float) * 2; + } + if (vertex_format_flags & vertex_attribute_normal) + { + ctx.read32(vertex_data_offset, 3); + vertex_data_offset += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_tangent) + { + ctx.read32(vertex_data_offset, 4); + vertex_data_offset += sizeof(float) * 4; + } + if (vertex_format_flags & vertex_attribute_color) + { + ctx.read32(vertex_data_offset, 4); + vertex_data_offset += sizeof(float) * 4; + } + if (vertex_format_flags & vertex_attribute_bone) + { + ctx.read32(vertex_data_offset, bones_per_vertex); + ctx.read32(vertex_data_offset, bones_per_vertex); + + vertex_data_offset += sizeof(std::uint32_t) * bones_per_vertex; + vertex_data_offset += sizeof(float) * bones_per_vertex; + } + if (vertex_format_flags & vertex_attribute_barycentric) + { + ctx.read32(vertex_data_offset, 3); + vertex_data_offset += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_morph_target) + { + ctx.read32(vertex_data_offset, 3); + vertex_data_offset += sizeof(float) * 3; + } } } - - // Adjust indices of groups after this group - for (std::size_t i = group->index + 1; i < groups.size(); ++i) + + // Allocate model + std::unique_ptr model = std::make_unique(); + + // Resize model VBO and upload vertex data + gl::vertex_buffer& vbo = *model->get_vertex_buffer(); + vbo.resize(vertex_data.size(), vertex_data); + + // Free vertex data + vertex_data.clear(); + + // Bind vertex attributes to VAO + gl::vertex_array& vao = *model->get_vertex_array(); + gl::vertex_attribute attribute; + attribute.buffer = &vbo; + attribute.offset = 0; + attribute.stride = vertex_size; + if (vertex_format_flags & vertex_attribute_position) { - --groups[i]->index; + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 3; + vao.bind(render::vertex_attribute::position, attribute); + attribute.offset += sizeof(float) * attribute.components; } - - // Remove from groups - groups.erase(groups.begin() + group->index); - - // Deallocate group - delete group; - - return true; -} - -const model_group* model::get_group(const std::string& name) const -{ - if (auto it = group_map.find(name); it != group_map.end()) + if (vertex_format_flags & vertex_attribute_uv) { - return it->second; + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 2; + vao.bind(render::vertex_attribute::uv, attribute); + attribute.offset += sizeof(float) * attribute.components; } - - return nullptr; -} - -model_group* model::get_group(const std::string& name) -{ - if (auto it = group_map.find(name); it != group_map.end()) + if (vertex_format_flags & vertex_attribute_normal) { - return it->second; + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 3; + vao.bind(render::vertex_attribute::normal, attribute); + attribute.offset += sizeof(float) * attribute.components; } - - return nullptr; + if (vertex_format_flags & vertex_attribute_tangent) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 4; + vao.bind(render::vertex_attribute::tangent, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_color) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 4; + vao.bind(render::vertex_attribute::color, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_bone) + { + attribute.type = gl::vertex_attribute_type::uint_16; + attribute.components = bones_per_vertex; + vao.bind(render::vertex_attribute::bone_index, attribute); + attribute.offset += sizeof(std::uint32_t) * attribute.components; + + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = bones_per_vertex; + vao.bind(render::vertex_attribute::bone_weight, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_barycentric) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 3; + vao.bind(render::vertex_attribute::barycentric, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_morph_target) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 3; + vao.bind(render::vertex_attribute::target, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + + // Read model bounds + ctx.read32(reinterpret_cast(model->get_bounds().min_point.data()), 3); + ctx.read32(reinterpret_cast(model->get_bounds().max_point.data()), 3); + + // Read material count + std::uint16_t material_count = 0; + ctx.read16(reinterpret_cast(&material_count), 1); + + // Allocate material groups + model->get_groups().resize(material_count); + + // Read materials + for (auto& group: model->get_groups()) + { + // Read material name length + std::uint8_t material_name_length = 0; + ctx.read8(reinterpret_cast(&material_name_length), 1); + + // Read material name + std::string material_name(static_cast(material_name_length), '\0'); + ctx.read8(reinterpret_cast(material_name.data()), material_name_length); + + // Generate group ID by hashing material name + group.id = hash::fnv1a32(material_name); + + // Set group drawing mode + group.drawing_mode = gl::drawing_mode::triangles; + + // Read offset to index of first vertex + ctx.read32(reinterpret_cast(&group.start_index), 1); + + // Read vertex count + ctx.read32(reinterpret_cast(&group.index_count), 1); + + // Slugify material filename + std::string material_filename = material_name + ".mtl"; + std::replace(material_filename.begin(), material_filename.end(), '_', '-'); + + // Load group material + group.material = resource_manager.load(material_filename); + } + + // Read skeleton + if (vertex_format_flags & vertex_attribute_bone) + { + ::skeleton& skeleton = model->get_skeleton(); + pose& bind_pose = skeleton.bind_pose; + + // Read bone count + std::uint16_t bone_count = 0; + ctx.read16(reinterpret_cast(&bone_count), 1); + + // Read bones + for (std::uint16_t i = 0; i < bone_count; ++i) + { + // Read bone name length + std::uint8_t bone_name_length = 0; + ctx.read8(reinterpret_cast(&bone_name_length), 1); + + // Read and hash bone name + std::string bone_name(static_cast(bone_name_length), '\0'); + ctx.read8(reinterpret_cast(bone_name.data()), bone_name_length); + hash::fnv1a32_t bone_key = hash::fnv1a32(bone_name); + + // Read parent bone index + std::uint16_t parent_bone_index = i; + ctx.read16(reinterpret_cast(&parent_bone_index), 1); + + // Construct bone identifier + ::bone bone = make_bone(i, parent_bone_index); + + // Add bone to bone map + skeleton.bone_map[bone_key] = bone; + + // Get reference to the bone's bind pose transform + auto& bone_transform = bind_pose[bone]; + + // Read bone translation + ctx.read32(reinterpret_cast(bone_transform.translation.data()), 3); + + // Read bone rotation + ctx.read32(reinterpret_cast(&bone_transform.rotation.r), 1); + ctx.read32(reinterpret_cast(bone_transform.rotation.i.data()), 3); + + // Set bone scale + bone_transform.scale = {1, 1, 1}; + + // Read bone length + float bone_length = 0.0f; + ctx.read32(reinterpret_cast(&bone_length), 1); + } + + // Calculate inverse skeleton-space bind pose + ::concatenate(skeleton.bind_pose, skeleton.inverse_bind_pose); + ::inverse(skeleton.inverse_bind_pose, skeleton.inverse_bind_pose); + } + + return model; } - -} // namespace render diff --git a/src/engine/render/model.hpp b/src/engine/render/model.hpp index 6b871ef..dd2793b 100644 --- a/src/engine/render/model.hpp +++ b/src/engine/render/model.hpp @@ -21,12 +21,14 @@ #define ANTKEEPER_RENDER_MODEL_HPP #include +#include +#include #include #include -#include #include -#include -#include +#include +#include +#include #include #include @@ -35,176 +37,107 @@ namespace render { /** * Part of a model which is associated with exactly one material. */ -class model_group +struct model_group { -public: - void set_material(material* material); - void set_drawing_mode(gl::drawing_mode mode); - void set_start_index(std::size_t index); - void set_index_count(std::size_t count); - - std::size_t get_index() const; - const std::string& get_name() const; - const material* get_material() const; - material* get_material(); - gl::drawing_mode get_drawing_mode() const; - std::size_t get_start_index() const; - std::size_t get_index_count() const; - -private: - friend class model; - - std::size_t index; - std::string name; - material* material; + hash::fnv1a32_t id; gl::drawing_mode drawing_mode; - std::size_t start_index; - std::size_t index_count; + std::uint32_t start_index; + std::uint32_t index_count; + std::shared_ptr material; }; -inline void model_group::set_material(render::material* material) -{ - this->material = material; -} - -inline void model_group::set_drawing_mode(gl::drawing_mode mode) -{ - this->drawing_mode = mode; -} - -inline void model_group::set_start_index(std::size_t index) -{ - this->start_index = index; -} - -inline void model_group::set_index_count(std::size_t count) -{ - this->index_count = count; -} - -inline std::size_t model_group::get_index() const -{ - return index; -} - -inline const std::string& model_group::get_name() const -{ - return name; -} - -inline const material* model_group::get_material() const -{ - return material; -} - -inline material* model_group::get_material() -{ - return material; -} - -inline gl::drawing_mode model_group::get_drawing_mode() const -{ - return drawing_mode; -} - -inline std::size_t model_group::get_start_index() const -{ - return start_index; -} - -inline std::size_t model_group::get_index_count() const -{ - return index_count; -} - /** - * + * */ class model { public: + /// AABB type. typedef geom::aabb aabb_type; + /** + * Constructs a model. + */ model(); - ~model(); - void set_bounds(const aabb_type& bounds); - - model_group* add_group(const std::string& name = std::string()); - - bool remove_group(const std::string& name); - bool remove_group(model_group* group); + /** + * Returns the vertex array associated with this model. + */ + /// @{ + [[nodiscard]] inline const std::shared_ptr& get_vertex_array() const noexcept + { + return vertex_array; + } + [[nodiscard]] inline std::shared_ptr& get_vertex_array() noexcept + { + return vertex_array; + } + /// @} - const aabb_type& get_bounds() const; - - const model_group* get_group(const std::string& name) const; - model_group* get_group(const std::string& name); - - const std::vector* get_groups() const; - - const gl::vertex_array* get_vertex_array() const; - gl::vertex_array* get_vertex_array(); - - const gl::vertex_buffer* get_vertex_buffer() const; - gl::vertex_buffer* get_vertex_buffer(); + /** + * Returns the vertex buffer associated with this model. + */ + /// @{ + [[nodiscard]] inline const std::shared_ptr& get_vertex_buffer() const noexcept + { + return vertex_buffer; + } + [[nodiscard]] inline std::shared_ptr& get_vertex_buffer() noexcept + { + return vertex_buffer; + } + /// @} + + /** + * Returns the bounds of the model. + */ + /// @{ + [[nodiscard]] inline const aabb_type& get_bounds() const noexcept + { + return bounds; + } + [[nodiscard]] inline aabb_type& get_bounds() noexcept + { + return bounds; + } + /// @} + + /** + * Returns the model's model groups. + */ + /// @{ + [[nodiscard]] inline const std::vector& get_groups() const noexcept + { + return groups; + } + [[nodiscard]] inline std::vector& get_groups() noexcept + { + return groups; + } + /// @} + + /** + * Returns the skeleton associated with this model. + */ + /// @{ + [[nodiscard]] inline const ::skeleton& get_skeleton() const noexcept + { + return skeleton; + } + [[nodiscard]] inline ::skeleton& get_skeleton() noexcept + { + return skeleton; + } + /// @} - const skeleton& get_skeleton() const; - skeleton& get_skeleton(); - private: - aabb_type bounds; - std::vector groups; - std::unordered_map group_map; - gl::vertex_array vao; - gl::vertex_buffer vbo; + std::shared_ptr vertex_array; + std::shared_ptr vertex_buffer; + aabb_type bounds{{0, 0, 0}, {0, 0, 0}}; + std::vector groups; ::skeleton skeleton; }; -inline void model::set_bounds(const aabb_type& bounds) -{ - this->bounds = bounds; -} - -inline const typename model::aabb_type& model::get_bounds() const -{ - return bounds; -} - -inline const std::vector* model::get_groups() const -{ - return &groups; -} - -inline const gl::vertex_array* model::get_vertex_array() const -{ - return &vao; -} - -inline gl::vertex_array* model::get_vertex_array() -{ - return &vao; -} - -inline const gl::vertex_buffer* model::get_vertex_buffer() const -{ - return &vbo; -} - -inline gl::vertex_buffer* model::get_vertex_buffer() -{ - return &vbo; -} - -inline const skeleton& model::get_skeleton() const -{ - return skeleton; -} - -inline skeleton& model::get_skeleton() -{ - return skeleton; -} - } // namespace render #endif // ANTKEEPER_RENDER_MODEL_HPP diff --git a/src/engine/render/pass.hpp b/src/engine/render/pass.hpp index 7b2601b..203fd1f 100644 --- a/src/engine/render/pass.hpp +++ b/src/engine/render/pass.hpp @@ -36,7 +36,7 @@ public: pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer); virtual ~pass(); - virtual void render(const render::context& ctx, render::queue& queue) const = 0; + virtual void render(const render::context& ctx, render::queue& queue) = 0; void set_enabled(bool enabled); bool is_enabled() const; diff --git a/src/engine/render/passes/bloom-pass.cpp b/src/engine/render/passes/bloom-pass.cpp index 6fbeea9..47588cc 100644 --- a/src/engine/render/passes/bloom-pass.cpp +++ b/src/engine/render/passes/bloom-pass.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -46,7 +46,7 @@ bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_ma corrected_filter_radius{filter_radius, filter_radius} { // Load downsample shader template - downsample_shader_template = resource_manager->load("bloom-downsample.glsl"); + auto downsample_shader_template = resource_manager->load("bloom-downsample.glsl"); // Build downsample shader program with Karis averaging downsample_karis_shader = downsample_shader_template->build @@ -55,40 +55,37 @@ bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_ma {"KARIS_AVERAGE", std::string()} } ); - downsample_karis_source_texture_input = downsample_karis_shader->get_input("source_texture"); // Build downsample shader program without Karis averaging downsample_shader = downsample_shader_template->build(); - downsample_source_texture_input = downsample_shader->get_input("source_texture"); // Load upsample shader template - upsample_shader_template = resource_manager->load("bloom-upsample.glsl"); + auto upsample_shader_template = resource_manager->load("bloom-upsample.glsl"); // Build upsample shader program upsample_shader = upsample_shader_template->build(); - upsample_source_texture_input = upsample_shader->get_input("source_texture"); - upsample_filter_radius_input = upsample_shader->get_input("filter_radius"); - const float vertex_data[] = + const float2 vertex_positions[] = { - -1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, 1.0f, - 1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, -1.0f + {-1.0f, 1.0f}, + {-1.0f, -1.0f}, + { 1.0f, 1.0f}, + { 1.0f, 1.0f}, + {-1.0f, -1.0f}, + { 1.0f, -1.0f} }; + const auto vertex_data = std::as_bytes(std::span{vertex_positions}); std::size_t vertex_size = 2; std::size_t vertex_stride = sizeof(float) * vertex_size; std::size_t vertex_count = 6; - quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); - quad_vao = new gl::vertex_array(); + quad_vbo = std::make_unique(gl::buffer_usage::static_draw, vertex_data.size(), vertex_data); + quad_vao = std::make_unique(); // Define position vertex attribute gl::vertex_attribute position_attribute; - position_attribute.buffer = quad_vbo; + position_attribute.buffer = quad_vbo.get(); position_attribute.offset = 0; position_attribute.stride = vertex_stride; position_attribute.type = gl::vertex_attribute_type::float_32; @@ -98,79 +95,12 @@ bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_ma quad_vao->bind(render::vertex_attribute::position, position_attribute); } -bloom_pass::~bloom_pass() +void bloom_pass::render(const render::context& ctx, render::queue& queue) { - delete quad_vao; - delete quad_vbo; - - set_mip_chain_length(0); - - delete downsample_karis_shader; - delete downsample_shader; - delete upsample_shader; - - /// @TODO - //resource_manager->unload("bloom-downsample.glsl"); - //resource_manager->unload("bloom-upsample.glsl"); -} - -void bloom_pass::render(const render::context& ctx, render::queue& queue) const -{ - if (!source_texture || !mip_chain_length) - return; - - // Disable depth testing - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - - // Enable back-face culling - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - // Disable blending - glDisable(GL_BLEND); - - // Downsample first mip with Karis average - { - rasterizer->use_program(*downsample_karis_shader); - downsample_karis_source_texture_input->upload(source_texture); - - rasterizer->use_framebuffer(*framebuffers[0]); - rasterizer->set_viewport(0, 0, textures[0]->get_width(), textures[0]->get_height()); - - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); - } - - // Downsample remaining mips - rasterizer->use_program(*downsample_shader); - for (int i = 1; i < static_cast(mip_chain_length); ++i) + // Execute command buffer + for (const auto& command: command_buffer) { - rasterizer->use_framebuffer(*framebuffers[i]); - rasterizer->set_viewport(0, 0, textures[i]->get_width(), textures[i]->get_height()); - - // Use previous downsample texture as downsample source - downsample_source_texture_input->upload(textures[i - 1]); - - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); - } - - // Enable additive blending - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE); - glBlendEquation(GL_FUNC_ADD); - - // Upsample - rasterizer->use_program(*upsample_shader); - upsample_filter_radius_input->upload(corrected_filter_radius); - for (int i = static_cast(mip_chain_length) - 1; i > 0; --i) - { - const int j = i - 1; - rasterizer->use_framebuffer(*framebuffers[j]); - rasterizer->set_viewport(0, 0, textures[j]->get_width(), textures[j]->get_height()); - - upsample_source_texture_input->upload(textures[i]); - - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + command(); } } @@ -226,11 +156,13 @@ void bloom_pass::set_source_texture(const gl::texture_2d* texture) { source_texture = texture; resize(); + rebuild_command_buffer(); } } else { - source_texture = texture; + source_texture = nullptr; + rebuild_command_buffer(); } } } @@ -256,33 +188,30 @@ void bloom_pass::set_mip_chain_length(unsigned int length) unsigned int mip_height = std::max(1, source_height >> (i + 1)); // Generate mip texture - gl::texture_2d* texture = new gl::texture_2d(mip_width, mip_height, gl::pixel_type::float_16, gl::pixel_format::rgb); + auto texture = std::make_unique(mip_width, mip_height, gl::pixel_type::float_16, gl::pixel_format::rgb); texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); texture->set_max_anisotropy(0.0f); - textures.push_back(texture); // Generate mip framebuffer - gl::framebuffer* framebuffer = new gl::framebuffer(mip_width, mip_height); - framebuffer->attach(gl::framebuffer_attachment_type::color, texture); - framebuffers.push_back(framebuffer); + auto framebuffer = std::make_unique(mip_width, mip_height); + framebuffer->attach(gl::framebuffer_attachment_type::color, texture.get()); + + textures.push_back(std::move(texture)); + framebuffers.emplace_back(std::move(framebuffer)); } } else if (length < mip_chain_length) { - // Free excess framebuffers - while (framebuffers.size() > length) - { - delete framebuffers.back(); - framebuffers.pop_back(); - - delete textures.back(); - textures.pop_back(); - } + framebuffers.resize(length); + textures.resize(length); } // Update mip chain length mip_chain_length = length; + + // Rebuild command buffer + rebuild_command_buffer(); } void bloom_pass::set_filter_radius(float radius) @@ -300,4 +229,118 @@ void bloom_pass::set_filter_radius(float radius) corrected_filter_radius = {filter_radius * aspect_ratio, filter_radius}; } +void bloom_pass::rebuild_command_buffer() +{ + command_buffer.clear(); + + if (!source_texture || + !mip_chain_length || + !downsample_karis_shader || + !downsample_shader || + !upsample_shader) + { + return; + } + + // Setup downsample state + command_buffer.emplace_back + ( + []() + { + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glDisable(GL_BLEND); + } + ); + + // Downsample first mip with Karis average + if (auto source_texture_var = downsample_karis_shader->variable("source_texture")) + { + command_buffer.emplace_back + ( + [&, source_texture_var]() + { + rasterizer->use_program(*downsample_karis_shader); + rasterizer->use_framebuffer(*framebuffers[0]); + rasterizer->set_viewport(0, 0, textures[0]->get_width(), textures[0]->get_height()); + + source_texture_var->update(*source_texture); + + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + } + ); + } + + // Downsample remaining mips + if (mip_chain_length > 1) + { + if (auto source_texture_var = downsample_shader->variable("source_texture")) + { + command_buffer.emplace_back([&](){rasterizer->use_program(*downsample_shader);}); + + for (int i = 1; i < static_cast(mip_chain_length); ++i) + { + command_buffer.emplace_back + ( + [&, source_texture_var, i]() + { + rasterizer->use_framebuffer(*framebuffers[i]); + rasterizer->set_viewport(0, 0, textures[i]->get_width(), textures[i]->get_height()); + + // Use previous downsample texture as downsample source + source_texture_var->update(*textures[i - 1]); + + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + } + ); + } + } + } + + // Setup upsample state + command_buffer.emplace_back + ( + [&]() + { + // Enable additive blending + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + glBlendEquation(GL_FUNC_ADD); + + // Bind upsample shader + rasterizer->use_program(*upsample_shader); + } + ); + + // Update upsample filter radius + if (auto filter_radius_var = upsample_shader->variable("filter_radius")) + { + command_buffer.emplace_back([&, filter_radius_var](){filter_radius_var->update(corrected_filter_radius);}); + } + + // Upsample + if (auto source_texture_var = upsample_shader->variable("source_texture")) + { + for (int i = static_cast(mip_chain_length) - 1; i > 0; --i) + { + const int j = i - 1; + + command_buffer.emplace_back + ( + [&, source_texture_var, i, j]() + { + rasterizer->use_framebuffer(*framebuffers[j]); + rasterizer->set_viewport(0, 0, textures[j]->get_width(), textures[j]->get_height()); + + source_texture_var->update(*textures[i]); + + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + } + ); + } + } +} + } // namespace render diff --git a/src/engine/render/passes/bloom-pass.hpp b/src/engine/render/passes/bloom-pass.hpp index 44fd932..e7a0fab 100644 --- a/src/engine/render/passes/bloom-pass.hpp +++ b/src/engine/render/passes/bloom-pass.hpp @@ -21,12 +21,14 @@ #define ANTKEEPER_RENDER_BLOOM_PASS_HPP #include -#include +#include #include -#include +#include #include #include #include +#include +#include class resource_manager; @@ -49,18 +51,13 @@ public: */ bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager); - /** - * Destructs a bloom pass. - */ - virtual ~bloom_pass(); - /** * Renders a bloom texture. * * @param ctx Render context. * @param queue Render queue. */ - virtual void render(const render::context& ctx, render::queue& queue) const final; + void render(const render::context& ctx, render::queue& queue) override; /** * Resizes the mip chain resolution according to the resolution of the source texture. @@ -94,34 +91,29 @@ public: const gl::texture_2d* get_bloom_texture() const; private: - const gl::texture_2d* source_texture; + void rebuild_command_buffer(); - shader_template* downsample_shader_template; - shader_template* upsample_shader_template; - - gl::shader_program* downsample_karis_shader; - const gl::shader_input* downsample_karis_source_texture_input; - - gl::shader_program* downsample_shader; - const gl::shader_input* downsample_source_texture_input; + const gl::texture_2d* source_texture; - gl::shader_program* upsample_shader; - const gl::shader_input* upsample_source_texture_input; - const gl::shader_input* upsample_filter_radius_input; + std::unique_ptr downsample_karis_shader; + std::unique_ptr downsample_shader; + std::unique_ptr upsample_shader; - gl::vertex_buffer* quad_vbo; - gl::vertex_array* quad_vao; + std::unique_ptr quad_vbo; + std::unique_ptr quad_vao; unsigned int mip_chain_length; - std::vector framebuffers; - std::vector textures; + std::vector> framebuffers; + std::vector> textures; float filter_radius; float2 corrected_filter_radius; + + std::vector> command_buffer; }; inline const gl::texture_2d* bloom_pass::get_bloom_texture() const { - return textures.empty() ? nullptr : textures.front(); + return textures.empty() ? nullptr : textures.front().get(); } } // namespace render diff --git a/src/engine/render/passes/clear-pass.cpp b/src/engine/render/passes/clear-pass.cpp index 86f4814..e6ecf9d 100644 --- a/src/engine/render/passes/clear-pass.cpp +++ b/src/engine/render/passes/clear-pass.cpp @@ -34,27 +34,12 @@ clear_pass::clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb clear_stencil(0) {} -clear_pass::~clear_pass() -{} - -void clear_pass::render(const render::context& ctx, render::queue& queue) const +void clear_pass::render(const render::context& ctx, render::queue& queue) { - if (clear_color_buffer) - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - if (clear_depth_buffer) - glDepthMask(GL_TRUE); - if (clear_stencil_buffer) - glStencilMask(0xFF); - - rasterizer->use_framebuffer(*framebuffer); - - auto viewport = framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); - - rasterizer->set_clear_color(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); - rasterizer->set_clear_depth(clear_depth); - rasterizer->set_clear_stencil(clear_stencil); - rasterizer->clear_framebuffer(clear_color_buffer, clear_depth_buffer, clear_stencil_buffer); + for (const auto& command: command_buffer) + { + command(); + } } void clear_pass::set_cleared_buffers(bool color, bool depth, bool stencil) @@ -62,6 +47,8 @@ void clear_pass::set_cleared_buffers(bool color, bool depth, bool stencil) clear_color_buffer = color; clear_depth_buffer = depth; clear_stencil_buffer = stencil; + + rebuild_command_buffer(); } void clear_pass::set_clear_color(const float4& color) @@ -79,4 +66,75 @@ void clear_pass::set_clear_stencil(int stencil) clear_stencil = stencil; } +void clear_pass::rebuild_command_buffer() +{ + command_buffer.clear(); + + if (!clear_color_buffer && + !clear_depth_buffer && + !clear_stencil_buffer) + { + return; + } + + command_buffer.emplace_back + ( + [&]() + { + rasterizer->use_framebuffer(*framebuffer); + + auto viewport = framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); + } + ); + + if (clear_color_buffer) + { + // Update color buffer clear state + command_buffer.emplace_back + ( + [&]() + { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + rasterizer->set_clear_color(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); + } + ); + } + + if (clear_depth_buffer) + { + // Update depth buffer clear state + command_buffer.emplace_back + ( + [&]() + { + glDepthMask(GL_TRUE); + rasterizer->set_clear_depth(clear_depth); + } + ); + } + + if (clear_stencil_buffer) + { + // Update stencil buffer clear state + command_buffer.emplace_back + ( + [&]() + { + glStencilMask(0xFF); + rasterizer->set_clear_stencil(clear_stencil); + } + ); + } + + // Clear buffers + command_buffer.emplace_back + ( + [&]() + { + rasterizer->clear_framebuffer(clear_color_buffer, clear_depth_buffer, clear_stencil_buffer); + } + ); +} + } // namespace render diff --git a/src/engine/render/passes/clear-pass.hpp b/src/engine/render/passes/clear-pass.hpp index 568a5b7..6198700 100644 --- a/src/engine/render/passes/clear-pass.hpp +++ b/src/engine/render/passes/clear-pass.hpp @@ -22,6 +22,7 @@ #include #include +#include namespace render { @@ -32,8 +33,8 @@ class clear_pass: public pass { public: clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer); - virtual ~clear_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; + + void render(const render::context& ctx, render::queue& queue) override; /** * Sets the buffers to be cleared. @@ -66,15 +67,18 @@ public: void set_clear_stencil(int stencil); private: + void rebuild_command_buffer(); + bool clear_color_buffer; bool clear_depth_buffer; bool clear_stencil_buffer; float4 clear_color; float clear_depth; int clear_stencil; + + std::vector> command_buffer; }; } // namespace render #endif // ANTKEEPER_RENDER_CLEAR_PASS_HPP - diff --git a/src/engine/render/passes/final-pass.cpp b/src/engine/render/passes/final-pass.cpp index e901b78..8eee565 100644 --- a/src/engine/render/passes/final-pass.cpp +++ b/src/engine/render/passes/final-pass.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -34,6 +34,7 @@ #include #include #include +#include namespace render { @@ -45,39 +46,31 @@ final_pass::final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb blue_noise_texture(nullptr), blue_noise_scale(1.0f) { - // Load shader template - shader_template = resource_manager->load("final.glsl"); - - // Build shader program + // Load shader template and build shader program + auto shader_template = resource_manager->load("final.glsl"); shader_program = shader_template->build(); - color_texture_input = shader_program->get_input("color_texture"); - bloom_texture_input = shader_program->get_input("bloom_texture"); - bloom_weight_input = shader_program->get_input("bloom_weight"); - blue_noise_texture_input = shader_program->get_input("blue_noise_texture"); - blue_noise_scale_input = shader_program->get_input("blue_noise_scale"); - resolution_input = shader_program->get_input("resolution"); - time_input = shader_program->get_input("time"); - - const float vertex_data[] = + + const float2 vertex_positions[] = { - -1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, 1.0f, - 1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, -1.0f + {-1.0f, 1.0f}, + {-1.0f, -1.0f}, + { 1.0f, 1.0f}, + { 1.0f, 1.0f}, + {-1.0f, -1.0f}, + { 1.0f, -1.0f} }; + const auto vertex_data = std::as_bytes(std::span{vertex_positions}); std::size_t vertex_size = 2; std::size_t vertex_stride = sizeof(float) * vertex_size; std::size_t vertex_count = 6; - - quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); - quad_vao = new gl::vertex_array(); + + quad_vbo = std::make_unique(gl::buffer_usage::static_draw, vertex_data.size(), vertex_data); + quad_vao = std::make_unique(); // Define position vertex attribute gl::vertex_attribute position_attribute; - position_attribute.buffer = quad_vbo; + position_attribute.buffer = quad_vbo.get(); position_attribute.offset = 0; position_attribute.stride = vertex_stride; position_attribute.type = gl::vertex_attribute_type::float_32; @@ -87,62 +80,34 @@ final_pass::final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb quad_vao->bind(render::vertex_attribute::position, position_attribute); } -final_pass::~final_pass() -{ - delete quad_vao; - delete quad_vbo; - - delete shader_program; - - /// @TODO - // resource_manager->unload("final.glsl"); -} - -void final_pass::render(const render::context& ctx, render::queue& queue) const +void final_pass::render(const render::context& ctx, render::queue& queue) { - rasterizer->use_framebuffer(*framebuffer); + // Update resolution + const auto viewport_size = framebuffer->get_dimensions(); + resolution = {static_cast(std::get<0>(viewport_size)), static_cast(std::get<1>(viewport_size))}; - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - auto viewport = framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); + // Update time + time = ctx.t; - float2 resolution = {std::get<0>(viewport), std::get<1>(viewport)}; - - // Change shader program - rasterizer->use_program(*shader_program); - - // Upload shader parameters - color_texture_input->upload(color_texture); - if (bloom_texture && bloom_texture_input) - bloom_texture_input->upload(bloom_texture); - if (bloom_weight_input) - bloom_weight_input->upload(bloom_weight); - if (blue_noise_texture && blue_noise_texture_input) - blue_noise_texture_input->upload(blue_noise_texture); - if (blue_noise_scale_input) - blue_noise_scale_input->upload(blue_noise_scale); - if (resolution_input) - resolution_input->upload(resolution); - if (time_input) - time_input->upload(ctx.t); - - // Draw quad - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + // Execute render commands + for (const auto& command: command_buffer) + { + command(); + } } void final_pass::set_color_texture(const gl::texture_2d* texture) { this->color_texture = texture; + + rebuild_command_buffer(); } void final_pass::set_bloom_texture(const gl::texture_2d* texture) noexcept { this->bloom_texture = texture; + + rebuild_command_buffer(); } void final_pass::set_bloom_weight(float weight) noexcept @@ -150,10 +115,81 @@ void final_pass::set_bloom_weight(float weight) noexcept this->bloom_weight = weight; } -void final_pass::set_blue_noise_texture(const gl::texture_2d* texture) +void final_pass::set_blue_noise_texture(std::shared_ptr texture) { this->blue_noise_texture = texture; blue_noise_scale = 1.0f / static_cast(texture->get_dimensions()[0]); + + rebuild_command_buffer(); +} + +void final_pass::rebuild_command_buffer() +{ + command_buffer.clear(); + + command_buffer.emplace_back + ( + [&]() + { + rasterizer->use_framebuffer(*framebuffer); + rasterizer->set_viewport(0, 0, static_cast(resolution.x()), static_cast(resolution.y())); + + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + rasterizer->use_program(*shader_program); + } + ); + + if (color_texture) + { + if (const auto var = shader_program->variable("color_texture")) + { + command_buffer.emplace_back([&, var](){var->update(*color_texture);}); + } + } + if (bloom_texture) + { + if (const auto var = shader_program->variable("bloom_texture")) + { + command_buffer.emplace_back([&, var](){var->update(*bloom_texture);}); + } + } + if (blue_noise_texture) + { + if (const auto var = shader_program->variable("blue_noise_texture")) + { + command_buffer.emplace_back([&, var](){var->update(*blue_noise_texture);}); + } + } + + if (const auto var = shader_program->variable("bloom_weight")) + { + command_buffer.emplace_back([&, var](){var->update(bloom_weight);}); + } + if (const auto var = shader_program->variable("blue_noise_scale")) + { + command_buffer.emplace_back([&, var](){var->update(blue_noise_scale);}); + } + if (const auto var = shader_program->variable("resolution")) + { + command_buffer.emplace_back([&, var](){var->update(resolution);}); + } + if (const auto var = shader_program->variable("time")) + { + command_buffer.emplace_back([&, var](){var->update(time);}); + } + + command_buffer.emplace_back + ( + [&]() + { + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + } + ); } } // namespace render diff --git a/src/engine/render/passes/final-pass.hpp b/src/engine/render/passes/final-pass.hpp index 40e2913..6257edb 100644 --- a/src/engine/render/passes/final-pass.hpp +++ b/src/engine/render/passes/final-pass.hpp @@ -21,12 +21,14 @@ #define ANTKEEPER_RENDER_FINAL_PASS_HPP #include -#include +#include #include -#include +#include #include #include #include +#include +#include class resource_manager; @@ -39,33 +41,29 @@ class final_pass: public pass { public: final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - virtual ~final_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; + void render(const render::context& ctx, render::queue& queue) override; void set_color_texture(const gl::texture_2d* texture); void set_bloom_texture(const gl::texture_2d* texture) noexcept; void set_bloom_weight(float weight) noexcept; - void set_blue_noise_texture(const gl::texture_2d* texture); + void set_blue_noise_texture(std::shared_ptr texture); private: - render::shader_template* shader_template; + void rebuild_command_buffer(); - gl::shader_program* shader_program; - const gl::shader_input* color_texture_input; - const gl::shader_input* bloom_texture_input; - const gl::shader_input* bloom_weight_input; - const gl::shader_input* blue_noise_texture_input; - const gl::shader_input* blue_noise_scale_input; - const gl::shader_input* resolution_input; - const gl::shader_input* time_input; - gl::vertex_buffer* quad_vbo; - gl::vertex_array* quad_vao; + std::unique_ptr shader_program; + std::unique_ptr quad_vbo; + std::unique_ptr quad_vao; const gl::texture_2d* color_texture; const gl::texture_2d* bloom_texture; float bloom_weight; - const gl::texture_2d* blue_noise_texture; + std::shared_ptr blue_noise_texture; float blue_noise_scale; + float2 resolution; + float time; + + std::vector> command_buffer; }; } // namespace render diff --git a/src/engine/render/passes/fxaa-pass.cpp b/src/engine/render/passes/fxaa-pass.cpp index 45a267e..40a9eb4 100644 --- a/src/engine/render/passes/fxaa-pass.cpp +++ b/src/engine/render/passes/fxaa-pass.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -36,37 +36,35 @@ namespace render { fxaa_pass::fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): - pass(rasterizer, framebuffer), - source_texture(nullptr) + pass(rasterizer, framebuffer) { // Load FXAA shader template - shader_template = resource_manager->load("fxaa.glsl"); + auto shader_template = resource_manager->load("fxaa.glsl"); // Build FXAA shader program shader = shader_template->build(); - source_texture_input = shader->get_input("source_texture"); - texel_size_input = shader->get_input("texel_size"); - const float vertex_data[] = + const float2 vertex_positions[] = { - -1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, 1.0f, - 1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, -1.0f + {-1.0f, 1.0f}, + {-1.0f, -1.0f}, + { 1.0f, 1.0f}, + { 1.0f, 1.0f}, + {-1.0f, -1.0f}, + { 1.0f, -1.0f} }; + const auto vertex_data = std::as_bytes(std::span{vertex_positions}); std::size_t vertex_size = 2; std::size_t vertex_stride = sizeof(float) * vertex_size; std::size_t vertex_count = 6; - - quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); - quad_vao = new gl::vertex_array(); + + quad_vbo = std::make_unique(gl::buffer_usage::static_draw, vertex_data.size(), vertex_data); + quad_vao = std::make_unique(); // Define position vertex attribute gl::vertex_attribute position_attribute; - position_attribute.buffer = quad_vbo; + position_attribute.buffer = quad_vbo.get(); position_attribute.offset = 0; position_attribute.stride = vertex_stride; position_attribute.type = gl::vertex_attribute_type::float_32; @@ -76,47 +74,72 @@ fxaa_pass::fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuf quad_vao->bind(render::vertex_attribute::position, position_attribute); } -fxaa_pass::~fxaa_pass() +void fxaa_pass::render(const render::context& ctx, render::queue& queue) { - delete quad_vao; - delete quad_vbo; - - delete shader; - - /// @TODO - // resource_manager->unload("fxaa.glsl"); + for (const auto& command: command_buffer) + { + command(); + } } -void fxaa_pass::render(const render::context& ctx, render::queue& queue) const +void fxaa_pass::set_source_texture(const gl::texture_2d* texture) { - if (!source_texture) - return; + source_texture = texture; + rebuild_command_buffer(); +} + +void fxaa_pass::rebuild_command_buffer() +{ + command_buffer.clear(); - // Set rasterizer state - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glDisable(GL_BLEND); + if (!source_texture || !shader) + { + return; + } - // Render FXAA - rasterizer->use_framebuffer(*framebuffer); - rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]); - rasterizer->use_program(*shader); - source_texture_input->upload(source_texture); + // Setup FXAA state + command_buffer.emplace_back + ( + [&]() + { + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glDisable(GL_BLEND); + + // Render FXAA + rasterizer->use_framebuffer(*framebuffer); + rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]); + rasterizer->use_program(*shader); + } + ); - if (texel_size_input) + // Update shader variables + if (auto source_texture_var = shader->variable("source_texture")) + { + command_buffer.emplace_back([&, source_texture_var](){source_texture_var->update(*source_texture);}); + } + if (auto texel_size_var = shader->variable("texel_size")) { - const float2 texel_size = 1.0f / float2{static_cast(source_texture->get_width()), static_cast(source_texture->get_height())}; - texel_size_input->upload(texel_size); + command_buffer.emplace_back + ( + [&, texel_size_var]() + { + const float2 texel_size = 1.0f / float2{static_cast(source_texture->get_width()), static_cast(source_texture->get_height())}; + texel_size_var->update(texel_size); + } + ); } - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); -} - -void fxaa_pass::set_source_texture(const gl::texture_2d* texture) -{ - source_texture = texture; + // Draw quad + command_buffer.emplace_back + ( + [&]() + { + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + } + ); } } // namespace render diff --git a/src/engine/render/passes/fxaa-pass.hpp b/src/engine/render/passes/fxaa-pass.hpp index dd09471..8d553bf 100644 --- a/src/engine/render/passes/fxaa-pass.hpp +++ b/src/engine/render/passes/fxaa-pass.hpp @@ -21,12 +21,13 @@ #define ANTKEEPER_RENDER_FXAA_PASS_HPP #include -#include +#include #include -#include +#include #include #include #include +#include class resource_manager; @@ -49,18 +50,13 @@ public: */ fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - /** - * Destructs an FXAA pass. - */ - virtual ~fxaa_pass(); - /** * Renders FXAA. * * @param ctx Render context. * @param queue Render queue. */ - virtual void render(const render::context& ctx, render::queue& queue) const final; + void render(const render::context& ctx, render::queue& queue) override; /** * Sets the FXAA source texture. @@ -70,15 +66,15 @@ public: void set_source_texture(const gl::texture_2d* texture); private: - const gl::texture_2d* source_texture; + void rebuild_command_buffer(); + + std::unique_ptr shader; + std::unique_ptr quad_vbo; + std::unique_ptr quad_vao; - render::shader_template* shader_template; - gl::shader_program* shader; - const gl::shader_input* source_texture_input; - const gl::shader_input* texel_size_input; + const gl::texture_2d* source_texture{nullptr}; - gl::vertex_buffer* quad_vbo; - gl::vertex_array* quad_vao; + std::vector> command_buffer; }; } // namespace render diff --git a/src/engine/render/passes/ground-pass.cpp b/src/engine/render/passes/ground-pass.cpp index a4d8a9f..c0e2665 100644 --- a/src/engine/render/passes/ground-pass.cpp +++ b/src/engine/render/passes/ground-pass.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -58,8 +58,9 @@ ground_pass::ground_pass(gl::rasterizer* rasterizer, const gl::framebuffer* fram ground_pass::~ground_pass() {} -void ground_pass::render(const render::context& ctx, render::queue& queue) const +void ground_pass::render(const render::context& ctx, render::queue& queue) { + /* if (!ground_model) return; @@ -91,8 +92,6 @@ void ground_pass::render(const render::context& ctx, render::queue& queue) const const float4x4& view_projection = ctx.view_projection; float4x4 model_view_projection = projection * model_view; - - float3 ambient_light_color = {0.0f, 0.0f, 0.0f}; float3 directional_light_color = {0.0f, 0.0f, 0.0f}; float3 directional_light_direction = {0.0f, 0.0f, 0.0f}; @@ -140,41 +139,40 @@ void ground_pass::render(const render::context& ctx, render::queue& queue) const // Draw ground rasterizer->use_program(*shader_program); - if (model_view_projection_input) - model_view_projection_input->upload(model_view_projection); - if (view_projection_input) - view_projection_input->upload(view_projection); - if (camera_position_input) - camera_position_input->upload(ctx.camera_transform.translation); - if (directional_light_colors_input) - directional_light_colors_input->upload(0, &directional_light_color, 1); - if (directional_light_directions_input) - directional_light_directions_input->upload(0, &directional_light_direction, 1); - if (ambient_light_colors_input) - ambient_light_colors_input->upload(0, &ambient_light_color, 1); - - ground_material->upload(ctx.alpha); - + if (model_view_projection_var) + model_view_projection_var->update(model_view_projection); + if (view_projection_var) + view_projection_var->update(view_projection); + if (camera_position_var) + camera_position_var->update(ctx.camera_transform.translation); + if (directional_light_colors_var) + directional_light_colors_var->update(0, &directional_light_color, 1); + if (directional_light_directions_var) + directional_light_directions_var->update(0, &directional_light_direction, 1); + if (ambient_light_colors_var) + ambient_light_colors_var->update(0, &ambient_light_color, 1); + ground_material->update(ctx.alpha); rasterizer->draw_arrays(*ground_model_vao, ground_model_drawing_mode, ground_model_start_index, ground_model_index_count); + */ } -void ground_pass::set_ground_model(const model* model) +void ground_pass::set_ground_model(std::shared_ptr model) { + /* ground_model = model; if (ground_model) { - ground_model_vao = model->get_vertex_array(); - - const std::vector& groups = *model->get_groups(); - for (model_group* group: groups) + ground_model_vao = model->get_vertex_array().get(); + + for (const auto& group: model->get_groups()) { - ground_material = group->get_material(); - ground_model_drawing_mode = group->get_drawing_mode(); - ground_model_start_index = group->get_start_index(); - ground_model_index_count = group->get_index_count(); + ground_material = group.material; + ground_model_drawing_mode = group.drawing_mode; + ground_model_start_index = group.start_index; + ground_model_index_count = group.index_count; } if (ground_material) @@ -183,12 +181,12 @@ void ground_pass::set_ground_model(const model* model) if (shader_program) { - model_view_projection_input = shader_program->get_input("model_view_projection"); - view_projection_input = shader_program->get_input("view_projection"); - camera_position_input = shader_program->get_input("camera.position"); - directional_light_colors_input = shader_program->get_input("directional_light_colors"); - directional_light_directions_input = shader_program->get_input("directional_light_directions"); - ambient_light_colors_input = shader_program->get_input("ambient_light_colors"); + model_view_projection_var = shader_program->get_var("model_view_projection"); + view_projection_var = shader_program->get_var("view_projection"); + camera_position_var = shader_program->get_var("camera.position"); + directional_light_colors_var = shader_program->get_var("directional_light_colors"); + directional_light_directions_var = shader_program->get_var("directional_light_directions"); + ambient_light_colors_var = shader_program->get_var("ambient_light_colors"); } } } @@ -196,6 +194,7 @@ void ground_pass::set_ground_model(const model* model) { ground_model_vao = nullptr; } + */ } } // namespace render diff --git a/src/engine/render/passes/ground-pass.hpp b/src/engine/render/passes/ground-pass.hpp index 1dfec08..113e3da 100644 --- a/src/engine/render/passes/ground-pass.hpp +++ b/src/engine/render/passes/ground-pass.hpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -44,20 +44,20 @@ class ground_pass: public pass public: ground_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); virtual ~ground_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; + void render(const render::context& ctx, render::queue& queue) override; - void set_ground_model(const model* model); + void set_ground_model(std::shared_ptr model); private: - gl::shader_program* shader_program; - const gl::shader_input* model_view_projection_input; - const gl::shader_input* view_projection_input; - const gl::shader_input* camera_position_input; - const gl::shader_input* directional_light_colors_input; - const gl::shader_input* directional_light_directions_input; - const gl::shader_input* ambient_light_colors_input; + std::shared_ptr shader_program; + const gl::shader_variable* model_view_projection_var; + const gl::shader_variable* view_projection_var; + const gl::shader_variable* camera_position_var; + const gl::shader_variable* directional_light_colors_var; + const gl::shader_variable* directional_light_directions_var; + const gl::shader_variable* ambient_light_colors_var; - const model* ground_model; + std::shared_ptr ground_model; const material* ground_material; const gl::vertex_array* ground_model_vao; gl::drawing_mode ground_model_drawing_mode; diff --git a/src/engine/render/passes/material-pass.cpp b/src/engine/render/passes/material-pass.cpp index c39c7c6..564c4f5 100644 --- a/src/engine/render/passes/material-pass.cpp +++ b/src/engine/render/passes/material-pass.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include #include @@ -52,51 +54,10 @@ namespace render { 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): - pass(rasterizer, framebuffer), - fallback_material(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; - max_directional_light_count = MATERIAL_PASS_MAX_DIRECTIONAL_LIGHT_COUNT; - max_spot_light_count = MATERIAL_PASS_MAX_SPOTLIGHT_COUNT; - - ambient_light_colors = new float3[max_ambient_light_count]; - point_light_colors = new float3[max_point_light_count]; - point_light_positions = new float3[max_point_light_count]; - point_light_attenuations = new float3[max_point_light_count]; - directional_light_colors = new float3[max_directional_light_count]; - directional_light_directions = new float3[max_directional_light_count]; - directional_light_textures = new const gl::texture_2d*[max_directional_light_count]; - directional_light_texture_matrices = new float4x4[max_directional_light_count]; - directional_light_texture_opacities = new float[max_directional_light_count]; - - spot_light_colors = new float3[max_spot_light_count]; - spot_light_positions = new float3[max_spot_light_count]; - spot_light_directions = new float3[max_spot_light_count]; - spot_light_attenuations = new float3[max_spot_light_count]; - spot_light_cutoffs = new float2[max_spot_light_count]; -} - -material_pass::~material_pass() -{ - delete[] ambient_light_colors; - delete[] point_light_colors; - delete[] point_light_positions; - delete[] point_light_attenuations; - delete[] directional_light_colors; - delete[] directional_light_directions; - delete[] directional_light_textures; - delete[] directional_light_texture_matrices; - delete[] directional_light_texture_opacities; - delete[] spot_light_colors; - delete[] spot_light_positions; - delete[] spot_light_directions; - delete[] spot_light_attenuations; - delete[] spot_light_cutoffs; -} + pass(rasterizer, framebuffer) +{} -void material_pass::render(const render::context& ctx, render::queue& queue) const +void material_pass::render(const render::context& ctx, render::queue& queue) { rasterizer->use_framebuffer(*framebuffer); @@ -107,55 +68,199 @@ void material_pass::render(const render::context& ctx, render::queue& queue) con glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glDisable(GL_STENCIL_TEST); - glStencilMask(0x00); // For half-z buffer glDepthRange(-1.0f, 1.0f); - + auto viewport = framebuffer->get_dimensions(); rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); - float2 resolution = {static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}; - - const float3& camera_position = ctx.camera_transform.translation; - const float4x4& view = ctx.view; - const float4x4& projection = ctx.projection; - const float4x4& view_projection = ctx.view_projection; - float4x4 model_view_projection; - float4x4 model; - float4x4 model_view; - float3x3 normal_model; - float3x3 normal_model_view; - float2 clip_depth; - 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); - - int active_material_flags = 0; 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; + std::size_t active_material_hash = 0; bool active_two_sided = false; + material_blend_mode active_blend_mode = material_blend_mode::opaque; + std::size_t active_cache_key = 0; + shader_cache_entry* active_cache_entry = nullptr; - // Reset light counts + // Gather information + evaluate_camera(ctx); + evaluate_lighting(ctx); + evaluate_misc(ctx); + + // Sort render queue + queue.sort(operation_compare); + + for (const render::operation& operation: queue) + { + // Get operation material + const render::material* material = operation.material; + if (!material) + { + if (!fallback_material) + { + // No material specified and no fallback material, skip operation + continue; + } + + // Use fallback material + material = fallback_material.get(); + } + + // Switch materials if necessary + if (active_material != material) + { + if (!material->get_shader_template()) + { + continue; + } + + if (active_material_hash != material->hash()) + { + // Set culling mode + if (active_two_sided != material->is_two_sided()) + { + if (material->is_two_sided()) + { + glDisable(GL_CULL_FACE); + } + else + { + glEnable(GL_CULL_FACE); + } + + active_two_sided = material->is_two_sided(); + } + + // Set blend mode + if (active_blend_mode != material->get_blend_mode()) + { + if (material->get_blend_mode() == material_blend_mode::translucent) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else + { + glDisable(GL_BLEND); + } + + active_blend_mode = material->get_blend_mode(); + } + + active_material_hash = material->hash(); + } + + // Calculate shader cache key + std::size_t cache_key = hash::combine(lighting_state_hash, material->get_shader_template()->hash()); + if (active_cache_key != cache_key) + { + // Lookup shader cache entry + if (auto i = shader_cache.find(cache_key); i != shader_cache.end()) + { + active_cache_entry = &i->second; + } + else + { + // Construct cache entry + active_cache_entry = &shader_cache[cache_key]; + active_cache_entry->shader_program = generate_shader_program(*material->get_shader_template()); + build_shader_command_buffer(active_cache_entry->shader_command_buffer, *active_cache_entry->shader_program); + build_geometry_command_buffer(active_cache_entry->geometry_command_buffer, *active_cache_entry->shader_program); + + debug::log::trace("Generated material cache entry {:x}", cache_key); + } + + // Bind shader and update shader-specific variables + for (const auto& command: active_cache_entry->shader_command_buffer) + { + command(); + } + + active_cache_key = cache_key; + } + + // Find or build material command buffer + std::vector>* material_command_buffer; + if (auto i = active_cache_entry->material_command_buffers.find(material); i != active_cache_entry->material_command_buffers.end()) + { + material_command_buffer = &i->second; + } + else + { + material_command_buffer = &active_cache_entry->material_command_buffers[material]; + build_material_command_buffer(*material_command_buffer, *active_cache_entry->shader_program, *material); + + debug::log::trace("Generated material command buffer"); + } + + // Update material-dependent shader variables + for (const auto& command: *material_command_buffer) + { + command(); + } + + active_material = material; + } + + // Update geometry-dependent shader variables + model = &operation.transform; + for (const auto& command: active_cache_entry->geometry_command_buffer) + { + command(); + } + + // Draw geometry + if (operation.instance_count) + { + rasterizer->draw_arrays_instanced(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count, operation.instance_count); + } + else + { + rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); + } + } + + ++frame; +} + +void material_pass::set_fallback_material(std::shared_ptr fallback) +{ + this->fallback_material = fallback; +} + +void material_pass::evaluate_camera(const render::context& ctx) +{ + view = &ctx.view; + projection = &ctx.projection; + view_projection = &ctx.view_projection; + camera_position = &ctx.camera_transform.translation; + camera_exposure = ctx.exposure; + clip_depth = + { + ctx.camera->get_clip_near_tween().interpolate(ctx.alpha), + ctx.camera->get_clip_far_tween().interpolate(ctx.alpha) + }; + log_depth_coef = 2.0f / std::log2(clip_depth[1] + 1.0f); +} + +void material_pass::evaluate_lighting(const render::context& ctx) +{ + // Reset light and shadow counts ambient_light_count = 0; point_light_count = 0; directional_light_count = 0; + directional_shadow_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; - float shadow_bias_directional = 0.0f; - // Collect lights const std::list* lights = ctx.collection->get_objects(scene::light::object_type_id); for (const scene::object_base* object: *lights) { - // Skip inactive lights + // Ignore inactive lights if (!object->is_active()) - continue; + { + continue; + } const scene::light* light = static_cast(object); switch (light->get_light_type()) @@ -163,77 +268,72 @@ void material_pass::render(const render::context& ctx, render::queue& queue) con // Add ambient light case scene::light_type::ambient: { - if (ambient_light_count < max_ambient_light_count) + const std::size_t index = ambient_light_count; + + ++ambient_light_count; + if (ambient_light_count > ambient_light_colors.size()) { - // Pre-expose light - ambient_light_colors[ambient_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; - ++ambient_light_count; + ambient_light_colors.resize(ambient_light_count); } + + ambient_light_colors[index] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; break; } // Add point light case scene::light_type::point: { - if (point_light_count < max_point_light_count) + const std::size_t index = point_light_count; + + ++point_light_count; + if (point_light_count > point_light_colors.size()) { - // Pre-expose light - point_light_colors[point_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; - - 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(ctx.alpha); - ++point_light_count; + point_light_colors.resize(point_light_count); + point_light_positions.resize(point_light_count); + point_light_attenuations.resize(point_light_count); } + + point_light_colors[index] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; + point_light_positions[index] = light->get_transform_tween().interpolate(ctx.alpha).translation; + point_light_attenuations[index] = static_cast(light)->get_attenuation_tween().interpolate(ctx.alpha); break; } // Add directional light case scene::light_type::directional: { - if (directional_light_count < max_directional_light_count) + const scene::directional_light& directional_light = static_cast(*light); + + const std::size_t index = directional_light_count; + + ++directional_light_count; + if (directional_light_count > directional_light_colors.size()) { - const scene::directional_light* directional_light = static_cast(light); - - // Pre-expose light - directional_light_colors[directional_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; - - float3 direction = static_cast(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_bias_directional = directional_light->get_shadow_bias(); - 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(); - } + directional_light_colors.resize(directional_light_count); + directional_light_directions.resize(directional_light_count); + } + + directional_light_colors[index] = directional_light.get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; + directional_light_directions[index] = directional_light.get_direction_tween().interpolate(ctx.alpha); + + // Add directional shadow + if (directional_light.is_shadow_caster() && directional_light.get_shadow_framebuffer()) + { + const std::size_t index = directional_shadow_count; - if (directional_light->get_light_texture()) + ++directional_shadow_count; + if (directional_shadow_count > directional_shadow_maps.size()) { - 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(ctx.alpha); - - math::transform 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); - - 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; - } - else - { - directional_light_textures[directional_light_count] = nullptr; - directional_light_texture_opacities[directional_light_count] = 0.0f; + directional_shadow_maps.resize(directional_shadow_count); + directional_shadow_biases.resize(directional_shadow_count); + directional_shadow_splits.resize(directional_shadow_count); + directional_shadow_matrices.resize(directional_shadow_count); } - ++directional_light_count; + directional_shadow_maps[index] = directional_light.get_shadow_framebuffer()->get_depth_attachment(); + directional_shadow_biases[index] = directional_light.get_shadow_bias(); + directional_shadow_splits[index] = &directional_light.get_shadow_cascade_distances(); + directional_shadow_matrices[index] = &directional_light.get_shadow_cascade_matrices(); } break; } @@ -241,489 +341,681 @@ void material_pass::render(const render::context& ctx, render::queue& queue) con // Add spot_light case scene::light_type::spot: { - if (spot_light_count < max_spot_light_count) + const scene::spot_light& spot_light = static_cast(*light); + + const std::size_t index = spot_light_count; + + ++spot_light_count; + if (spot_light_count > spot_light_colors.size()) { - const scene::spot_light* spot_light = static_cast(light); - - // Pre-expose light - spot_light_colors[spot_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; - - 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(ctx.alpha); - spot_light_directions[spot_light_count] = direction; - - 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; + spot_light_colors.resize(spot_light_count); + spot_light_positions.resize(spot_light_count); + spot_light_directions.resize(spot_light_count); + spot_light_attenuations.resize(spot_light_count); + spot_light_cutoffs.resize(spot_light_count); } + + spot_light_colors[spot_light_count] = spot_light.get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; + spot_light_positions[spot_light_count] = spot_light.get_transform_tween().interpolate(ctx.alpha).translation; + spot_light_directions[spot_light_count] = spot_light.get_direction_tween().interpolate(ctx.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); break; } - + default: break; } } - // Sort render queue - queue.sort(operation_compare); + // Generate lighting state hash + lighting_state_hash = std::hash{}(ambient_light_count); + lighting_state_hash = hash::combine(lighting_state_hash, std::hash{}(directional_light_count)); + lighting_state_hash = hash::combine(lighting_state_hash, std::hash{}(directional_shadow_count)); + lighting_state_hash = hash::combine(lighting_state_hash, std::hash{}(point_light_count)); + lighting_state_hash = hash::combine(lighting_state_hash, std::hash{}(spot_light_count)); +} + +void material_pass::evaluate_misc(const render::context& ctx) +{ + time = ctx.t; + timestep = ctx.dt; + subframe = ctx.alpha; - for (const render::operation& operation: queue) + const auto viewport_size = framebuffer->get_dimensions(); + resolution = { - // Get operation material - const render::material* material = operation.material; - if (!material) + static_cast(std::get<0>(viewport_size)), + static_cast(std::get<1>(viewport_size)) + }; + + ///mouse_position = ... +} + +std::unique_ptr material_pass::generate_shader_program(const gl::shader_template& shader_template) const +{ + std::unordered_map definitions; + + definitions["VERTEX_POSITION"] = std::to_string(vertex_attribute::position); + definitions["VERTEX_UV"] = std::to_string(vertex_attribute::uv); + definitions["VERTEX_NORMAL"] = std::to_string(vertex_attribute::normal); + definitions["VERTEX_TANGENT"] = std::to_string(vertex_attribute::tangent); + definitions["VERTEX_COLOR"] = std::to_string(vertex_attribute::color); + definitions["VERTEX_BONE_INDEX"] = std::to_string(vertex_attribute::bone_index); + definitions["VERTEX_BONE_WEIGHT"] = std::to_string(vertex_attribute::bone_weight); + definitions["VERTEX_BARYCENTRIC"] = std::to_string(vertex_attribute::barycentric); + definitions["VERTEX_TARGET"] = std::to_string(vertex_attribute::target); + + definitions["FRAGMENT_OUTPUT_COLOR"] = std::to_string(0); + + definitions["AMBIENT_LIGHT_COUNT"] = std::to_string(ambient_light_count); + definitions["DIRECTIONAL_LIGHT_COUNT"] = std::to_string(directional_light_count); + definitions["DIRECTIONAL_SHADOW_COUNT"] = std::to_string(directional_shadow_count); + definitions["POINT_LIGHT_COUNT"] = std::to_string(point_light_count); + definitions["SPOT_LIGHT_COUNT"] = std::to_string(spot_light_count); + + auto shader_program = shader_template.build(definitions); + + if (!shader_program->linked()) + { + debug::log::error("Failed to link material shader program: {}", shader_program->info()); + debug::log::warning("{}", shader_template.configure(gl::shader_stage::fragment, definitions)); + } + + return shader_program; +} + +void material_pass::build_shader_command_buffer(std::vector>& command_buffer, const gl::shader_program& shader_program) const +{ + // Bind shader program + command_buffer.emplace_back([&](){rasterizer->use_program(shader_program);}); + + // Update camera variables + if (auto view_var = shader_program.variable("view")) + { + command_buffer.emplace_back([&, view_var](){view_var->update(*view);}); + } + if (auto projection_var = shader_program.variable("projection")) + { + command_buffer.emplace_back([&, projection_var](){projection_var->update(*projection);}); + } + if (auto view_projection_var = shader_program.variable("view_projection")) + { + command_buffer.emplace_back([&, view_projection_var](){view_projection_var->update(*view_projection);}); + } + if (auto camera_position_var = shader_program.variable("camera_position")) + { + command_buffer.emplace_back([&, camera_position_var](){camera_position_var->update(*camera_position);}); + } + if (auto camera_exposure_var = shader_program.variable("camera_exposure")) + { + command_buffer.emplace_back([&, camera_exposure_var](){camera_exposure_var->update(camera_exposure);}); + } + if (auto clip_depth_var = shader_program.variable("clip_depth")) + { + command_buffer.emplace_back([&, clip_depth_var](){clip_depth_var->update(clip_depth);}); + } + + // Update ambient light variables + if (ambient_light_count) + { + if (auto ambient_light_colors_var = shader_program.variable("ambient_light_colors")) { - if (fallback_material) + command_buffer.emplace_back([&, ambient_light_colors_var](){ambient_light_colors_var->update(std::span{ambient_light_colors.data(), ambient_light_count});}); + } + } + + // Update directional light variables + if (directional_light_count) + { + if (auto directional_light_colors_var = shader_program.variable("directional_light_colors")) + { + if (auto directional_light_directions_var = shader_program.variable("directional_light_directions")) { - // No material specified, use fallback material - material = fallback_material; + command_buffer.emplace_back + ( + [&, directional_light_colors_var, directional_light_directions_var]() + { + directional_light_colors_var->update(std::span{directional_light_colors.data(), directional_light_count}); + directional_light_directions_var->update(std::span{directional_light_directions.data(), directional_light_count}); + } + ); } - else + } + } + + // Update directional shadow variables + if (directional_shadow_count) + { + if (auto directional_shadow_maps_var = shader_program.variable("directional_shadow_maps")) + { + auto directional_shadow_biases_var = shader_program.variable("directional_shadow_biases"); + auto directional_shadow_splits_var = shader_program.variable("directional_shadow_splits"); + auto directional_shadow_matrices_var = shader_program.variable("directional_shadow_matrices"); + + if (directional_shadow_maps_var && directional_shadow_biases_var && directional_shadow_splits_var && directional_shadow_matrices_var) { - // No material specified and no fallback material, skip operation - continue; + command_buffer.emplace_back + ( + [&, directional_shadow_maps_var, directional_shadow_biases_var, directional_shadow_splits_var, directional_shadow_matrices_var]() + { + directional_shadow_maps_var->update(std::span{directional_shadow_maps.data(), directional_shadow_count}); + directional_shadow_biases_var->update(std::span{directional_shadow_biases.data(), directional_shadow_count}); + + std::size_t offset = 0; + for (std::size_t i = 0; i < directional_shadow_count; ++i) + { + directional_shadow_splits_var->update(*directional_shadow_splits[i], offset * 4); + directional_shadow_matrices_var->update(*directional_shadow_matrices[i], offset); + offset += directional_shadow_splits[i]->size(); + } + } + ); } } - - // Switch materials if necessary - if (active_material != material) + } + + // Update point light variables + if (point_light_count) + { + if (auto point_light_colors_var = shader_program.variable("point_light_colors")) { - active_material = material; + auto point_light_positions_var = shader_program.variable("point_light_positions"); + auto point_light_attenuations_var = shader_program.variable("point_light_attenuations"); - // Set blend mode - const blend_mode material_blend_mode = active_material->get_blend_mode(); - if (material_blend_mode != active_blend_mode) + if (point_light_positions_var && point_light_attenuations_var) + { + command_buffer.emplace_back + ( + [&, point_light_colors_var, point_light_positions_var, point_light_attenuations_var]() + { + point_light_colors_var->update(std::span{point_light_colors.data(), point_light_count}); + point_light_positions_var->update(std::span{point_light_positions.data(), point_light_count}); + point_light_attenuations_var->update(std::span{point_light_attenuations.data(), point_light_count}); + } + ); + } + } + } + + // Update spot light variables + if (spot_light_count) + { + if (auto spot_light_colors_var = shader_program.variable("spot_light_colors")) + { + auto spot_light_positions_var = shader_program.variable("spot_light_positions"); + auto spot_light_directions_var = shader_program.variable("spot_light_directions"); + auto spot_light_attenuations_var = shader_program.variable("spot_light_attenuations"); + auto spot_light_cutoffs_var = shader_program.variable("spot_light_cutoffs"); + + if (spot_light_positions_var && spot_light_directions_var && spot_light_attenuations_var && spot_light_cutoffs_var) + { + command_buffer.emplace_back + ( + [&, spot_light_colors_var, spot_light_positions_var, spot_light_directions_var, spot_light_attenuations_var, spot_light_cutoffs_var]() + { + spot_light_colors_var->update(std::span{spot_light_colors.data(), spot_light_count}); + spot_light_positions_var->update(std::span{spot_light_positions.data(), spot_light_count}); + spot_light_directions_var->update(std::span{spot_light_directions.data(), spot_light_count}); + spot_light_attenuations_var->update(std::span{spot_light_attenuations.data(), spot_light_count}); + spot_light_cutoffs_var->update(std::span{spot_light_cutoffs.data(), spot_light_count}); + } + ); + } + } + } + + // Update time variable + if (auto time_var = shader_program.variable("time")) + { + command_buffer.emplace_back([&, time_var](){time_var->update(time);}); + } + + // Update timestep variable + if (auto timestep_var = shader_program.variable("timestep")) + { + command_buffer.emplace_back([&, timestep_var](){timestep_var->update(timestep);}); + } + + // Update frame variable + if (auto frame_var = shader_program.variable("frame")) + { + command_buffer.emplace_back([&, frame_var](){frame_var->update(frame);}); + } + + // Update subframe variable + if (auto subframe_var = shader_program.variable("subframe")) + { + command_buffer.emplace_back([&, subframe_var](){subframe_var->update(subframe);}); + } + + // Update resolution variable + if (auto resolution_var = shader_program.variable("resolution")) + { + command_buffer.emplace_back([&, resolution_var](){resolution_var->update(resolution);}); + } + + // Update mouse position variable + if (auto mouse_position_var = shader_program.variable("mouse_position")) + { + command_buffer.emplace_back([&, mouse_position_var](){mouse_position_var->update(mouse_position);}); + } +} + +void material_pass::build_geometry_command_buffer(std::vector>& command_buffer, const gl::shader_program& shader_program) const +{ + // Update model matrix variable + if (auto model_var = shader_program.variable("model")) + { + command_buffer.emplace_back([&, model_var](){model_var->update(*model);}); + } + + // Update normal-model matrix variable + if (auto normal_model_var = shader_program.variable("normal_model")) + { + command_buffer.emplace_back + ( + [&, normal_model_var]() + { + normal_model_var->update(math::transpose(math::inverse(math::matrix(*model)))); + } + ); + } + + // Update model-view matrix and normal-model-view matrix variables + auto model_view_var = shader_program.variable("model_view"); + auto normal_model_view_var = shader_program.variable("normal_model_view"); + if (model_view_var && normal_model_view_var) + { + command_buffer.emplace_back + ( + [&, model_view_var, normal_model_view_var]() { - if (material_blend_mode == blend_mode::translucent) + const auto model_view = (*view) * (*model); + model_view_var->update(model_view); + normal_model_view_var->update(math::transpose(math::inverse(math::matrix(model_view)))); + } + ); + } + else + { + if (model_view_var) + { + command_buffer.emplace_back([&, model_view_var](){model_view_var->update((*view) * (*model));}); + } + else if (normal_model_view_var) + { + command_buffer.emplace_back + ( + [&, normal_model_view_var]() { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + const auto model_view = (*view) * (*model); + normal_model_view_var->update(math::transpose(math::inverse(math::matrix(model_view)))); } - else if (active_blend_mode == blend_mode::translucent && (material_blend_mode == blend_mode::opaque || material_blend_mode == blend_mode::masked)) + ); + } + } + + // Update model-view-projection matrix variable + if (auto model_view_projection_var = shader_program.variable("model_view_projection")) + { + command_buffer.emplace_back([&, model_view_projection_var](){model_view_projection_var->update((*view_projection) * (*model));}); + } +} + +void material_pass::build_material_command_buffer(std::vector>& command_buffer, const gl::shader_program& shader_program, const material& material) const +{ + for (const auto& [key, material_var]: material.get_variables()) + { + if (!material_var) + { + continue; + } + + const auto shader_var = shader_program.variable(key); + if (!shader_var) + { + continue; + } + + const std::size_t size = std::min(material_var->size(), shader_var->size()); + + switch (shader_var->type()) + { + /// @TODO render::material_bool is broken due to the std::vector specialization. + // case gl::shader_variable_type::bool1: + // if (material_var->type() == material_variable_type::bool1) + // { + // command_buffer.emplace_back + // ( + // [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + // { + // shader_var->update(std::span{material_var->data(), size}); + // } + // ); + // } + // break; + case gl::shader_variable_type::bool2: + if (material_var->type() == material_variable_type::bool2) { - glDisable(GL_BLEND); + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); } - - 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) + break; + case gl::shader_variable_type::bool3: + if (material_var->type() == material_variable_type::bool3) { - glDisable(GL_CULL_FACE); + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); } - else + break; + case gl::shader_variable_type::bool4: + if (material_var->type() == material_variable_type::bool4) { - glEnable(GL_CULL_FACE); + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); } - - 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)) + break; + case gl::shader_variable_type::int1: + if (material_var->type() == material_variable_type::int1) { - if (material_flags & MATERIAL_FLAG_X_RAY) - { - glDisable(GL_DEPTH_TEST); - - } - else - { - glEnable(GL_DEPTH_TEST); - } + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); } - - if ((material_flags & MATERIAL_FLAG_DECAL_SURFACE) != (active_material_flags & MATERIAL_FLAG_DECAL_SURFACE)) + break; + case gl::shader_variable_type::int2: + if (material_var->type() == material_variable_type::int2) { - if (material_flags & MATERIAL_FLAG_DECAL_SURFACE) - { - glEnable(GL_STENCIL_TEST); - glStencilFunc(GL_ALWAYS, 1, ~0); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - glStencilMask(~0); - } - else - { - glDisable(GL_STENCIL_TEST); - glStencilMask(0); - } + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); } - - if ((material_flags & MATERIAL_FLAG_DECAL) != (active_material_flags & MATERIAL_FLAG_DECAL)) + break; + case gl::shader_variable_type::int3: + if (material_var->type() == material_variable_type::int3) { - if (material_flags & MATERIAL_FLAG_DECAL) - { - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_GEQUAL); - glDepthMask(GL_FALSE); - - glEnable(GL_STENCIL_TEST); - glStencilFunc(GL_EQUAL, 1, ~0); - //glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); - //glStencilMask(~0); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilMask(0); - } - else - { - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_GREATER); - glDepthMask(GL_TRUE); - glDisable(GL_STENCIL_TEST); - glStencilMask(0); - } + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); } - - /* - if ((material_flags & MATERIAL_FLAG_OUTLINE) != (active_material_flags & MATERIAL_FLAG_OUTLINE)) + break; + case gl::shader_variable_type::int4: + if (material_var->type() == material_variable_type::int4) { - if (material_flags & MATERIAL_FLAG_OUTLINE) - { - glEnable(GL_STENCIL_TEST); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - glStencilFunc(GL_ALWAYS, 2, 0xFF); - glStencilMask(0xFF); - } - else - { - glDisable(GL_STENCIL_TEST); - glStencilMask(0x00); - } + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); } - */ - - active_material_flags = material_flags; - } - - // Switch shaders if necessary - const gl::shader_program* shader_program = active_material->get_shader_program(); - if (active_shader_program != shader_program) - { - active_shader_program = shader_program; - if (!active_shader_program) + break; + case gl::shader_variable_type::uint1: + if (material_var->type() == material_variable_type::uint1) { - continue; + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); } - - // Change shader program - rasterizer->use_program(*active_shader_program); - - // Get set of known shader input parameters - if (auto it = parameter_sets.find(active_shader_program); it != parameter_sets.end()) + break; + case gl::shader_variable_type::uint2: + if (material_var->type() == material_variable_type::uint2) { - parameters = it->second; + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); } - else + break; + case gl::shader_variable_type::uint3: + if (material_var->type() == material_variable_type::uint3) { - parameters = load_parameter_set(active_shader_program); + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); } - - // Upload context-dependent shader parameters - if (parameters->time) - parameters->time->upload(ctx.t); - if (parameters->mouse) - parameters->mouse->upload(mouse_position); - if (parameters->resolution) - parameters->resolution->upload(resolution); - if (parameters->camera_position) - parameters->camera_position->upload(camera_position); - if (parameters->camera_exposure) - parameters->camera_exposure->upload(ctx.exposure); - if (parameters->view) - parameters->view->upload(view); - if (parameters->view_projection) - parameters->view_projection->upload(view_projection); - if (parameters->ambient_light_count) - parameters->ambient_light_count->upload(ambient_light_count); - if (parameters->ambient_light_colors) - parameters->ambient_light_colors->upload(0, ambient_light_colors, ambient_light_count); - if (parameters->point_light_count) - parameters->point_light_count->upload(point_light_count); - if (parameters->point_light_colors) - parameters->point_light_colors->upload(0, point_light_colors, point_light_count); - if (parameters->point_light_positions) - parameters->point_light_positions->upload(0, point_light_positions, point_light_count); - if (parameters->point_light_attenuations) - parameters->point_light_attenuations->upload(0, point_light_attenuations, point_light_count); - if (parameters->directional_light_count) - parameters->directional_light_count->upload(directional_light_count); - if (parameters->directional_light_colors) - parameters->directional_light_colors->upload(0, directional_light_colors, directional_light_count); - if (parameters->directional_light_directions) - parameters->directional_light_directions->upload(0, directional_light_directions, directional_light_count); - - if (parameters->directional_light_textures) - parameters->directional_light_textures->upload(0, directional_light_textures, directional_light_count); - if (parameters->directional_light_texture_matrices) - parameters->directional_light_texture_matrices->upload(0, directional_light_texture_matrices, directional_light_count); - 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_bias_directional) - parameters->shadow_bias_directional->upload(shadow_bias_directional); - 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) - parameters->spot_light_colors->upload(0, spot_light_colors, spot_light_count); - if (parameters->spot_light_positions) - parameters->spot_light_positions->upload(0, spot_light_positions, spot_light_count); - if (parameters->spot_light_directions) - parameters->spot_light_directions->upload(0, spot_light_directions, spot_light_count); - if (parameters->spot_light_attenuations) - 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); - } - - // Upload material properties to shader - active_material->upload(ctx.alpha); - } - - // Calculate operation-dependent parameters - model = operation.transform; - model_view_projection = view_projection * model; - model_view = view * model; - normal_model = math::transpose(math::inverse(math::matrix(model))); - normal_model_view = math::transpose(math::inverse(math::matrix(model_view))); - - // Skinning palette - if (operation.bone_count && parameters->skinning_palette) - { - parameters->skinning_palette->upload(0, operation.skinning_palette, operation.bone_count); + break; + case gl::shader_variable_type::uint4: + if (material_var->type() == material_variable_type::uint4) + { + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); + } + break; + case gl::shader_variable_type::float1: + if (material_var->type() == material_variable_type::float1) + { + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); + } + break; + case gl::shader_variable_type::float2: + if (material_var->type() == material_variable_type::float2) + { + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); + } + break; + case gl::shader_variable_type::float3: + if (material_var->type() == material_variable_type::float3) + { + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); + } + break; + case gl::shader_variable_type::float4: + if (material_var->type() == material_variable_type::float4) + { + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); + } + break; + case gl::shader_variable_type::float2x2: + if (material_var->type() == material_variable_type::float2x2) + { + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); + } + break; + case gl::shader_variable_type::float3x3: + if (material_var->type() == material_variable_type::float3x3) + { + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); + } + break; + case gl::shader_variable_type::float4x4: + if (material_var->type() == material_variable_type::float4x4) + { + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span{material_var->data(), size}); + } + ); + } + break; + case gl::shader_variable_type::texture_1d: + if (material_var->type() == material_variable_type::texture_1d) + { + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span>{material_var->data(), size}); + } + ); + } + break; + case gl::shader_variable_type::texture_2d: + if (material_var->type() == material_variable_type::texture_2d) + { + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span>{material_var->data(), size}); + } + ); + } + break; + case gl::shader_variable_type::texture_3d: + if (material_var->type() == material_variable_type::texture_3d) + { + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span>{material_var->data(), size}); + } + ); + } + break; + case gl::shader_variable_type::texture_cube: + if (material_var->type() == material_variable_type::texture_cube) + { + command_buffer.emplace_back + ( + [size, shader_var, material_var = std::static_pointer_cast(material_var)]() + { + shader_var->update(std::span>{material_var->data(), size}); + } + ); + } + break; + default: + break; } - - // Upload operation-dependent parameters - if (parameters->model) - parameters->model->upload(model); - if (parameters->model_view) - parameters->model_view->upload(model_view); - if (parameters->model_view_projection) - parameters->model_view_projection->upload(model_view_projection); - if (parameters->normal_model) - parameters->normal_model->upload(normal_model); - if (parameters->normal_model_view) - parameters->normal_model_view->upload(normal_model_view); - if (parameters->clip_depth) - parameters->clip_depth->upload(clip_depth); - if (parameters->log_depth_coef) - parameters->log_depth_coef->upload(log_depth_coef); - - // Draw geometry - if (operation.instance_count) - rasterizer->draw_arrays_instanced(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count, operation.instance_count); - else - rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); } } -void material_pass::set_fallback_material(const material* fallback) -{ - this->fallback_material = fallback; -} - -const material_pass::parameter_set* material_pass::load_parameter_set(const gl::shader_program* program) const -{ - // Allocate a new parameter set - parameter_set* parameters = new parameter_set(); - - // Connect inputs - parameters->time = program->get_input("time"); - parameters->mouse = program->get_input("mouse"); - parameters->resolution = program->get_input("resolution"); - parameters->camera_position = program->get_input("camera.position"); - parameters->camera_exposure = program->get_input("camera.exposure"); - parameters->model = program->get_input("model"); - parameters->view = program->get_input("view"); - parameters->projection = program->get_input("projection"); - parameters->model_view = program->get_input("model_view"); - parameters->view_projection = program->get_input("view_projection"); - parameters->model_view_projection = program->get_input("model_view_projection"); - parameters->normal_model = program->get_input("normal_model"); - parameters->normal_model_view = program->get_input("normal_model_view"); - parameters->clip_depth = program->get_input("clip_depth"); - parameters->log_depth_coef = program->get_input("log_depth_coef"); - parameters->ambient_light_count = program->get_input("ambient_light_count"); - parameters->ambient_light_colors = program->get_input("ambient_light_colors"); - parameters->point_light_count = program->get_input("point_light_count"); - parameters->point_light_colors = program->get_input("point_light_colors"); - parameters->point_light_positions = program->get_input("point_light_positions"); - parameters->point_light_attenuations = program->get_input("point_light_attenuations"); - parameters->directional_light_count = program->get_input("directional_light_count"); - parameters->directional_light_colors = program->get_input("directional_light_colors"); - parameters->directional_light_directions = program->get_input("directional_light_directions"); - parameters->directional_light_textures = program->get_input("directional_light_textures"); - parameters->directional_light_texture_matrices = program->get_input("directional_light_texture_matrices"); - parameters->directional_light_texture_opacities = program->get_input("directional_light_texture_opacities"); - parameters->spot_light_count = program->get_input("spot_light_count"); - parameters->spot_light_colors = program->get_input("spot_light_colors"); - parameters->spot_light_positions = program->get_input("spot_light_positions"); - parameters->spot_light_directions = program->get_input("spot_light_directions"); - parameters->spot_light_attenuations = program->get_input("spot_light_attenuations"); - parameters->spot_light_cutoffs = program->get_input("spot_light_cutoffs"); - parameters->shadow_map_directional = program->get_input("shadow_map_directional"); - parameters->shadow_bias_directional = program->get_input("shadow_bias_directional"); - parameters->shadow_splits_directional = program->get_input("shadow_splits_directional"); - parameters->shadow_matrices_directional = program->get_input("shadow_matrices_directional"); - parameters->skinning_palette = program->get_input("skinning_palette"); - - // Add parameter set to map of parameter sets - parameter_sets[program] = parameters; - - return parameters; -} - bool operation_compare(const render::operation& a, const render::operation& b) { + // Render operations with materials first if (!a.material) + { return false; + } else if (!b.material) + { return true; + } - 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; + const bool translucent_a = a.material->get_blend_mode() == material_blend_mode::translucent; + const bool translucent_b = b.material->get_blend_mode() == material_blend_mode::translucent; - if (xray_a) + if (translucent_a) { - if (xray_b) + if (translucent_b) { - // A and B are both xray, render back to front - return (a.depth > b.depth); + // A and B are both translucent, render back to front + return (a.depth < b.depth); } else { - // A is xray, B is not. Render B first + // A is translucent, B is opaque. Render B first return false; } } else { - if (xray_b) + if (translucent_b) { - // A is opaque, B is xray. Render A first + // A is opaque, B is translucent. Render A first return true; } else { - // Determine transparency - bool transparent_a = a.material->get_blend_mode() == blend_mode::translucent; - bool transparent_b = b.material->get_blend_mode() == blend_mode::translucent; + // A and B are both opaque - if (transparent_a) + const std::size_t hash_a = a.material->hash(); + const std::size_t hash_b = b.material->hash(); + + if (hash_a == hash_b) { - if (transparent_b) - { - // Determine decal status - bool decal_a = a.material->get_flags() & MATERIAL_FLAG_DECAL; - bool decal_b = b.material->get_flags() & MATERIAL_FLAG_DECAL; - - if (decal_a) - { - if (decal_b) - { - // A and B are both transparent decals, render back to front - return (a.depth > b.depth); - } - else - { - // A is a transparent decal, B is transparent but not a decal, render A first - return true; - } - } - else - { - if (decal_b) - { - // A is transparent but not a decal, B is a transparent decal, render B first - return false; - } - else - { - // A and B are both transparent, but not decals, render back to front - return (a.depth < b.depth); - } - } - } - else - { - // A is transparent, B is opaque. Render B first - return false; - } + // A and B have same material hash, sort by VAO + return (a.vertex_array < b.vertex_array); } else { - if (transparent_b) - { - // A is opaque, B is transparent. Render A first - return true; - } - else - { - // A and B are both opaque - if (a.material->get_shader_program() == b.material->get_shader_program()) - { - // A and B have the same shader - if (a.vertex_array == b.vertex_array) - { - // A and B have the same VAO, render front to back - return (a.depth < b.depth); - } - else - { - // 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 - { - // A and B are both opaque and have different shaders, sort by shader - return (a.material->get_shader_program() < b.material->get_shader_program()); - } - } + // A and B have different material hashes, sort by hash + return (hash_a < hash_b); } } } diff --git a/src/engine/render/passes/material-pass.hpp b/src/engine/render/passes/material-pass.hpp index 1e3936e..cdcbcf9 100644 --- a/src/engine/render/passes/material-pass.hpp +++ b/src/engine/render/passes/material-pass.hpp @@ -24,8 +24,9 @@ #include #include #include -#include +#include #include +#include #include class resource_manager; @@ -39,92 +40,102 @@ class material_pass: public pass { public: material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - virtual ~material_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; + + void render(const render::context& ctx, render::queue& queue) override; /// 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); + void set_fallback_material(std::shared_ptr fallback); private: - /** - * Sets of known shader input parameters. Each time a new shader is encountered, a parameter set will be created and its inputs connected to the shader program. A null input indiciates that the shader doesn't have that parameter. - */ - struct parameter_set + struct shader_cache_entry { - const gl::shader_input* time; - const gl::shader_input* mouse; - const gl::shader_input* resolution; - const gl::shader_input* camera_position; - const gl::shader_input* camera_exposure; - const gl::shader_input* model; - const gl::shader_input* view; - const gl::shader_input* projection; - const gl::shader_input* model_view; - const gl::shader_input* view_projection; - const gl::shader_input* model_view_projection; - const gl::shader_input* normal_model; - const gl::shader_input* normal_model_view; - const gl::shader_input* clip_depth; - const gl::shader_input* log_depth_coef; + std::unique_ptr shader_program; - const gl::shader_input* ambient_light_count; - const gl::shader_input* ambient_light_colors; - const gl::shader_input* point_light_count; - const gl::shader_input* point_light_colors; - const gl::shader_input* point_light_positions; - const gl::shader_input* point_light_attenuations; - const gl::shader_input* directional_light_count; - const gl::shader_input* directional_light_colors; - const gl::shader_input* directional_light_directions; - const gl::shader_input* directional_light_textures; - const gl::shader_input* directional_light_texture_matrices; - const gl::shader_input* directional_light_texture_opacities; - const gl::shader_input* spot_light_count; - const gl::shader_input* spot_light_colors; - const gl::shader_input* spot_light_positions; - const gl::shader_input* spot_light_directions; - const gl::shader_input* spot_light_attenuations; - const gl::shader_input* spot_light_cutoffs; + /// Command buffer which enables the shader and updates render state-related shader variables. + std::vector> shader_command_buffer; - const gl::shader_input* shadow_map_directional; - const gl::shader_input* shadow_bias_directional; - const gl::shader_input* shadow_splits_directional; - const gl::shader_input* shadow_matrices_directional; + /// Command buffer which updates geometry-related shader variables. + std::vector> geometry_command_buffer; - const gl::shader_input* skinning_palette; + /// Map of materials to command buffers which update corresponding material shader variables. + std::unordered_map>> material_command_buffers; }; - - const parameter_set* load_parameter_set(const gl::shader_program* program) const; - - mutable std::unordered_map parameter_sets; - const material* fallback_material; + + /// Map of state hashes to shader cache entries. + std::unordered_map shader_cache; + + /** + * Evaluates the active camera and stores camera information in local variables to be passed to shaders. + */ + void evaluate_camera(const render::context& ctx); + + /** + * Evaluates scene lights and stores lighting information in local variables to be passed to shaders. + */ + void evaluate_lighting(const render::context& ctx); + + void evaluate_misc(const render::context& ctx); + + [[nodiscard]] std::unique_ptr generate_shader_program(const gl::shader_template& shader_template) const; + + void build_shader_command_buffer(std::vector>& command_buffer, const gl::shader_program& shader_program) const; + void build_geometry_command_buffer(std::vector>& command_buffer, const gl::shader_program& shader_program) const; + void build_material_command_buffer(std::vector>& command_buffer, const gl::shader_program& shader_program, const material& material) const; + + // Camera + const float4x4* view; + const float4x4* projection; + const float4x4* view_projection; + const float3* camera_position; + float camera_exposure; + float2 clip_depth; + float log_depth_coef; + + // Ambient lights + std::vector ambient_light_colors; + std::size_t ambient_light_count; + + // Point lights + std::vector point_light_colors; + std::vector point_light_positions; + std::vector point_light_attenuations; + std::size_t point_light_count; + + // Directional lights + std::vector directional_light_colors; + std::vector directional_light_directions; + std::size_t directional_light_count; + + // Directional shadows + std::vector directional_shadow_maps; + std::vector directional_shadow_biases; + std::vector*> directional_shadow_splits; + std::vector*> directional_shadow_matrices; + std::size_t directional_shadow_count; + + // Spot lights + std::vector spot_light_colors; + std::vector spot_light_positions; + std::vector spot_light_directions; + std::vector spot_light_attenuations; + std::vector spot_light_cutoffs; + std::size_t spot_light_count; + + // Misc + float time; + float timestep; + unsigned int frame{0}; + float subframe; + float2 resolution; float2 mouse_position; - int max_ambient_light_count; - int max_point_light_count; - int max_directional_light_count; - int max_spot_light_count; - int max_bone_count; + // Geometry + const float4x4* model; - mutable int ambient_light_count; - mutable int point_light_count; - mutable int directional_light_count; - mutable int spot_light_count; - - float3* ambient_light_colors; - float3* point_light_colors; - float3* point_light_positions; - float3* point_light_attenuations; - float3* directional_light_colors; - float3* directional_light_directions; - const gl::texture_2d** directional_light_textures; - float4x4* directional_light_texture_matrices; - float* directional_light_texture_opacities; - float3* spot_light_colors; - float3* spot_light_positions; - float3* spot_light_directions; - float3* spot_light_attenuations; - float2* spot_light_cutoffs; + /// Hash of the lighting state. + std::size_t lighting_state_hash; + + std::shared_ptr fallback_material; }; } // namespace render diff --git a/src/engine/render/passes/outline-pass.cpp b/src/engine/render/passes/outline-pass.cpp index 18967b9..e5e1497 100644 --- a/src/engine/render/passes/outline-pass.cpp +++ b/src/engine/render/passes/outline-pass.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -42,21 +43,24 @@ outline_pass::outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* fr fill_shader(nullptr), stroke_shader(nullptr) { - // Load fill shader - fill_shader = resource_manager->load("outline-fill-unskinned.glsl"); - fill_model_view_projection_input = fill_shader->get_input("model_view_projection"); + // Load fill shader template + auto fill_shader_template = resource_manager->load("outline-fill-unskinned.glsl"); - // Load stroke shader - stroke_shader = resource_manager->load("outline-stroke-unskinned.glsl"); - stroke_model_view_projection_input = stroke_shader->get_input("model_view_projection"); - stroke_width_input = stroke_shader->get_input("width"); - stroke_color_input = stroke_shader->get_input("color"); + // Build fill shader + fill_shader = fill_shader_template->build({}); + fill_model_view_projection_var = fill_shader->variable("model_view_projection"); + + // Load stroke shader template + auto stroke_shader_template = resource_manager->load("outline-stroke-unskinned.glsl"); + + // Build stroke shader + stroke_shader = stroke_shader_template->build({}); + stroke_model_view_projection_var = stroke_shader->variable("model_view_projection"); + stroke_width_var = stroke_shader->variable("width"); + stroke_color_var = stroke_shader->variable("color"); } -outline_pass::~outline_pass() -{} - -void outline_pass::render(const render::context& ctx, render::queue& queue) const +void outline_pass::render(const render::context& ctx, render::queue& queue) { rasterizer->use_framebuffer(*framebuffer); @@ -91,10 +95,12 @@ void outline_pass::render(const render::context& ctx, render::queue& queue) cons { const render::material* material = operation.material; if (!material || !(material->get_flags() & MATERIAL_FLAG_OUTLINE)) + { continue; + } model_view_projection = view_projection * operation.transform; - fill_model_view_projection_input->upload(model_view_projection); + fill_model_view_projection_var->update(model_view_projection); rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); } @@ -119,8 +125,8 @@ void outline_pass::render(const render::context& ctx, render::queue& queue) cons // Setup stroke shader rasterizer->use_program(*stroke_shader); - stroke_width_input->upload(outline_width); - stroke_color_input->upload(outline_color); + stroke_width_var->update(outline_width); + stroke_color_var->update(outline_color); // Render strokes for (const render::operation& operation: queue) @@ -130,7 +136,7 @@ void outline_pass::render(const render::context& ctx, render::queue& queue) cons continue; model_view_projection = view_projection * operation.transform; - stroke_model_view_projection_input->upload(model_view_projection); + stroke_model_view_projection_var->update(model_view_projection); rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); } diff --git a/src/engine/render/passes/outline-pass.hpp b/src/engine/render/passes/outline-pass.hpp index b7393b5..e1ea39c 100644 --- a/src/engine/render/passes/outline-pass.hpp +++ b/src/engine/render/passes/outline-pass.hpp @@ -23,7 +23,8 @@ #include #include #include -#include +#include +#include class resource_manager; @@ -36,20 +37,20 @@ class outline_pass: public pass { public: outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - virtual ~outline_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; + virtual ~outline_pass() = default; + void render(const render::context& ctx, render::queue& queue) override; void set_outline_width(float width); void set_outline_color(const float4& color); private: - gl::shader_program* fill_shader; - const gl::shader_input* fill_model_view_projection_input; + std::unique_ptr fill_shader; + const gl::shader_variable* fill_model_view_projection_var; - gl::shader_program* stroke_shader; - const gl::shader_input* stroke_model_view_projection_input; - const gl::shader_input* stroke_width_input; - const gl::shader_input* stroke_color_input; + std::unique_ptr stroke_shader; + const gl::shader_variable* stroke_model_view_projection_var; + const gl::shader_variable* stroke_width_var; + const gl::shader_variable* stroke_color_var; float outline_width; float4 outline_color; diff --git a/src/engine/render/passes/resample-pass.cpp b/src/engine/render/passes/resample-pass.cpp index 9746ef4..edca6db 100644 --- a/src/engine/render/passes/resample-pass.cpp +++ b/src/engine/render/passes/resample-pass.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -36,35 +36,35 @@ namespace render { resample_pass::resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): pass(rasterizer, framebuffer), - source_texture(nullptr) + source_texture{nullptr} { // Load resample shader template - shader_template = resource_manager->load("resample.glsl"); + auto shader_template = resource_manager->load("resample.glsl"); // Build resample shader program shader = shader_template->build(); - source_texture_input = shader->get_input("source_texture"); - - const float vertex_data[] = + + const float2 vertex_positions[] = { - -1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, 1.0f, - 1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, -1.0f + {-1.0f, 1.0f}, + {-1.0f, -1.0f}, + { 1.0f, 1.0f}, + { 1.0f, 1.0f}, + {-1.0f, -1.0f}, + { 1.0f, -1.0f} }; + const auto vertex_data = std::as_bytes(std::span{vertex_positions}); std::size_t vertex_size = 2; std::size_t vertex_stride = sizeof(float) * vertex_size; std::size_t vertex_count = 6; - - quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); - quad_vao = new gl::vertex_array(); + + quad_vbo = std::make_unique(gl::buffer_usage::static_draw, vertex_data.size(), vertex_data); + quad_vao = std::make_unique(); // Define position vertex attribute gl::vertex_attribute position_attribute; - position_attribute.buffer = quad_vbo; + position_attribute.buffer = quad_vbo.get(); position_attribute.offset = 0; position_attribute.stride = vertex_stride; position_attribute.type = gl::vertex_attribute_type::float_32; @@ -74,40 +74,61 @@ resample_pass::resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer* quad_vao->bind(render::vertex_attribute::position, position_attribute); } -resample_pass::~resample_pass() +void resample_pass::render(const render::context& ctx, render::queue& queue) { - delete quad_vao; - delete quad_vbo; - - delete shader; - - /// @TODO - // resource_manager->unload("resample.glsl"); + for (const auto& command: command_buffer) + { + command(); + } } -void resample_pass::render(const render::context& ctx, render::queue& queue) const +void resample_pass::set_source_texture(const gl::texture_2d* texture) { - if (!source_texture) - return; - - // Set rasterizer state - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glDisable(GL_BLEND); + source_texture = texture; - // Render FXAA - rasterizer->use_framebuffer(*framebuffer); - rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]); - rasterizer->use_program(*shader); - source_texture_input->upload(source_texture); - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + rebuild_command_buffer(); } -void resample_pass::set_source_texture(const gl::texture_2d* texture) +void resample_pass::rebuild_command_buffer() { - source_texture = texture; + command_buffer.clear(); + + if (!source_texture || !shader) + { + return; + } + + // Setup resample state + command_buffer.emplace_back + ( + [&]() + { + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glDisable(GL_BLEND); + + rasterizer->use_framebuffer(*framebuffer); + rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]); + rasterizer->use_program(*shader); + } + ); + + // Update shader variables + if (auto source_texture_var = shader->variable("source_texture")) + { + command_buffer.emplace_back([&, source_texture_var](){source_texture_var->update(*source_texture);}); + } + + // Draw quad + command_buffer.emplace_back + ( + [&]() + { + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + } + ); } } // namespace render diff --git a/src/engine/render/passes/resample-pass.hpp b/src/engine/render/passes/resample-pass.hpp index b816df6..67e08f1 100644 --- a/src/engine/render/passes/resample-pass.hpp +++ b/src/engine/render/passes/resample-pass.hpp @@ -21,12 +21,14 @@ #define ANTKEEPER_RENDER_RESAMPLE_PASS_HPP #include -#include +#include #include -#include +#include #include #include #include +#include +#include class resource_manager; @@ -47,18 +49,13 @@ public: */ resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - /** - * Destructs a resample pass. - */ - virtual ~resample_pass(); - /** * Resamples a texture. * * @param ctx Render context. * @param queue Render queue. */ - virtual void render(const render::context& ctx, render::queue& queue) const final; + void render(const render::context& ctx, render::queue& queue) override; /** * Sets the resample source texture. @@ -68,14 +65,15 @@ public: void set_source_texture(const gl::texture_2d* texture); private: - const gl::texture_2d* source_texture; + void rebuild_command_buffer(); + + std::unique_ptr shader; + std::unique_ptr quad_vbo; + std::unique_ptr quad_vao; - render::shader_template* shader_template; - gl::shader_program* shader; - const gl::shader_input* source_texture_input; + const gl::texture_2d* source_texture; - gl::vertex_buffer* quad_vbo; - gl::vertex_array* quad_vao; + std::vector> command_buffer; }; } // namespace render diff --git a/src/engine/render/passes/shadow-map-pass.cpp b/src/engine/render/passes/shadow-map-pass.cpp index 021108e..72b72eb 100644 --- a/src/engine/render/passes/shadow-map-pass.cpp +++ b/src/engine/render/passes/shadow-map-pass.cpp @@ -22,10 +22,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -47,13 +47,19 @@ static bool operation_compare(const render::operation& a, const render::operatio 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("depth-unskinned.glsl"); - unskinned_model_view_projection_input = unskinned_shader_program->get_input("model_view_projection"); + // Load unskinned shader template + auto unskinned_shader_template = resource_manager->load("depth-unskinned.glsl"); - // Load unskinned shader program - skinned_shader_program = resource_manager->load("depth-skinned.glsl"); - skinned_model_view_projection_input = skinned_shader_program->get_input("model_view_projection"); + // Build unskinned shader program + unskinned_shader_program = unskinned_shader_template->build({}); + unskinned_model_view_projection_var = unskinned_shader_program->variable("model_view_projection"); + + // Load skinned shader template + auto skinned_shader_template = resource_manager->load("depth-skinned.glsl"); + + // Build skinned shader program + skinned_shader_program = skinned_shader_template->build({}); + skinned_model_view_projection_var = skinned_shader_program->variable("model_view_projection"); // Calculate bias-tile matrices float4x4 bias_matrix = math::translate(math::matrix4::identity(), float3{0.5f, 0.5f, 0.5f}) * math::scale(math::matrix4::identity(), float3{0.5f, 0.5f, 0.5f}); @@ -67,35 +73,40 @@ shadow_map_pass::shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* r } } -shadow_map_pass::~shadow_map_pass() -{} - -void shadow_map_pass::render(const render::context& ctx, render::queue& queue) const +void shadow_map_pass::render(const render::context& ctx, render::queue& queue) { - // Collect lights + // For each light const std::list* lights = ctx.collection->get_objects(scene::light::object_type_id); for (const scene::object_base* object: *lights) { // Ignore inactive lights if (!object->is_active()) - continue; + { + continue; + } // Ignore non-directional lights - const scene::light* light = static_cast(object); - if (light->get_light_type() != scene::light_type::directional) + const scene::light& light = static_cast(*object); + if (light.get_light_type() != scene::light_type::directional) + { continue; + } // Ignore non-shadow casters - const scene::directional_light* directional_light = static_cast(light); - if (!directional_light->is_shadow_caster()) + const scene::directional_light& directional_light = static_cast(light); + if (!directional_light.is_shadow_caster()) + { continue; + } // Ignore improperly-configured lights - if (!directional_light->get_shadow_cascade_count() || !directional_light->get_shadow_framebuffer()) + 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); + // Render cascaded shadow maps + render_csm(directional_light, ctx, queue); } } @@ -132,8 +143,10 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, const re 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(); + + /// @TODO: don't const_cast + auto& cascade_distances = const_cast&>(light.get_shadow_cascade_distances()); + auto& cascade_matrices = const_cast&>(light.get_shadow_cascade_matrices()); // Calculate cascade far clipping plane distances cascade_distances[cascade_count - 1] = shadow_clip_far; @@ -235,7 +248,7 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, const re if (material) { // Skip materials which don't cast shadows - if (material->get_shadow_mode() == shadow_mode::none) + if (material->get_shadow_mode() == material_shadow_mode::none) continue; if (material->is_two_sided() != two_sided) @@ -254,7 +267,7 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, const re } // Switch shader programs if necessary - gl::shader_program* shader_program = (operation.bone_count) ? skinned_shader_program : unskinned_shader_program; + gl::shader_program* shader_program = (operation.bone_count) ? skinned_shader_program.get() : unskinned_shader_program.get(); if (active_shader_program != shader_program) { active_shader_program = shader_program; @@ -265,13 +278,13 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, const re model_view_projection = cropped_view_projection * operation.transform; // Upload operation-dependent parameters to shader program - if (active_shader_program == unskinned_shader_program) + if (active_shader_program == unskinned_shader_program.get()) { - unskinned_model_view_projection_input->upload(model_view_projection); + unskinned_model_view_projection_var->update(model_view_projection); } - else if (active_shader_program == skinned_shader_program) + else if (active_shader_program == skinned_shader_program.get()) { - skinned_model_view_projection_input->upload(model_view_projection); + skinned_model_view_projection_var->update(model_view_projection); } // Draw geometry diff --git a/src/engine/render/passes/shadow-map-pass.hpp b/src/engine/render/passes/shadow-map-pass.hpp index 8f979cc..2ec1adf 100644 --- a/src/engine/render/passes/shadow-map-pass.hpp +++ b/src/engine/render/passes/shadow-map-pass.hpp @@ -24,7 +24,8 @@ #include #include #include -#include +#include +#include class resource_manager; @@ -45,18 +46,13 @@ public: */ shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager); - /** - * Destructs a shadow map pass. - */ - virtual ~shadow_map_pass(); - /** * 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; + void render(const render::context& ctx, render::queue& queue) override; private: /** @@ -68,11 +64,11 @@ private: */ 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; + std::unique_ptr unskinned_shader_program; + const gl::shader_variable* unskinned_model_view_projection_var; - gl::shader_program* skinned_shader_program; - const gl::shader_input* skinned_model_view_projection_input; + std::unique_ptr skinned_shader_program; + const gl::shader_variable* skinned_model_view_projection_var; float4x4 bias_tile_matrices[4]; }; diff --git a/src/engine/render/passes/sky-pass.cpp b/src/engine/render/passes/sky-pass.cpp index 18f3756..1e5c7c3 100644 --- a/src/engine/render/passes/sky-pass.cpp +++ b/src/engine/render/passes/sky-pass.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -54,7 +54,7 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe sky_model(nullptr), sky_material(nullptr), sky_model_vao(nullptr), - sky_shader_program(nullptr), + sky_lut_shader_program(nullptr), moon_model(nullptr), moon_model_vao(nullptr), moon_material(nullptr), @@ -81,97 +81,95 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe magnification(1.0f) { // Build quad VBO and VAO - const float quad_vertex_data[] = - { - -1.0f, 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, - 1.0f, 1.0f, 0.0f, - 1.0f, 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, - 1.0f, -1.0f, 0.0f + const float2 vertex_positions[] = + { + {-1.0f, 1.0f}, + {-1.0f, -1.0f}, + { 1.0f, 1.0f}, + { 1.0f, 1.0f}, + {-1.0f, -1.0f}, + { 1.0f, -1.0f} }; - std::size_t quad_vertex_size = 3; - std::size_t quad_vertex_stride = sizeof(float) * quad_vertex_size; - std::size_t quad_vertex_count = 6; - quad_vbo = new gl::vertex_buffer(sizeof(float) * quad_vertex_size * quad_vertex_count, quad_vertex_data); - quad_vao = new gl::vertex_array(); - gl::vertex_attribute quad_position_attribute; - quad_position_attribute.buffer = quad_vbo; - quad_position_attribute.offset = 0; - quad_position_attribute.stride = quad_vertex_stride; - quad_position_attribute.type = gl::vertex_attribute_type::float_32; - quad_position_attribute.components = 3; - quad_vao->bind(render::vertex_attribute::position, quad_position_attribute); + + const auto vertex_data = std::as_bytes(std::span{vertex_positions}); + std::size_t vertex_size = 2; + std::size_t vertex_stride = sizeof(float) * vertex_size; + std::size_t vertex_count = 6; + + quad_vbo = std::make_unique(gl::buffer_usage::static_draw, vertex_data.size(), vertex_data); + quad_vao = std::make_unique(); + + // Define position vertex attribute + gl::vertex_attribute position_attribute; + position_attribute.buffer = quad_vbo.get(); + position_attribute.offset = 0; + position_attribute.stride = vertex_stride; + position_attribute.type = gl::vertex_attribute_type::float_32; + position_attribute.components = 2; + + // Bind vertex attributes to VAO + quad_vao->bind(render::vertex_attribute::position, position_attribute); // Create transmittance LUT texture and framebuffer (32F color, no depth) - transmittance_lut_texture = new gl::texture_2d(256, 64, gl::pixel_type::float_32, gl::pixel_format::rgb); + transmittance_lut_texture = std::make_unique(256, 64, gl::pixel_type::float_32, gl::pixel_format::rgb); transmittance_lut_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); transmittance_lut_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); transmittance_lut_texture->set_max_anisotropy(0.0f); - transmittance_lut_framebuffer = new gl::framebuffer({transmittance_lut_texture->get_width(), transmittance_lut_texture->get_height()}); - transmittance_lut_framebuffer->attach(gl::framebuffer_attachment_type::color, transmittance_lut_texture); + transmittance_lut_framebuffer = std::make_unique(transmittance_lut_texture->get_width(), transmittance_lut_texture->get_height()); + transmittance_lut_framebuffer->attach(gl::framebuffer_attachment_type::color, transmittance_lut_texture.get()); transmittance_lut_resolution = {static_cast(transmittance_lut_texture->get_width()), static_cast(transmittance_lut_texture->get_height())}; // Load transmittance LUT shader template - transmittance_shader_template = resource_manager->load("transmittance-lut.glsl"); + transmittance_lut_shader_template = resource_manager->load("transmittance-lut.glsl"); // Build transmittance LUT shader program - transmittance_shader_program = transmittance_shader_template->build(); - transmittance_atmosphere_radii_input = transmittance_shader_program->get_input("atmosphere_radii"); - transmittance_rayleigh_parameters_input = transmittance_shader_program->get_input("rayleigh_parameters"); - transmittance_mie_parameters_input = transmittance_shader_program->get_input("mie_parameters"); - transmittance_ozone_distribution_input = transmittance_shader_program->get_input("ozone_distribution"); - transmittance_ozone_absorption_input = transmittance_shader_program->get_input("ozone_absorption"); - transmittance_resolution_input = transmittance_shader_program->get_input("resolution"); + transmittance_lut_shader_program = transmittance_lut_shader_template->build + ( + { + {"TRANSMITTANCE_LUT_SAMPLES", std::to_string(40)} + } + ); + if (!transmittance_lut_shader_program->linked()) + { + debug::log::error("Failed to build transmittance LUT shader program: {}", transmittance_lut_shader_program->info()); + debug::log::warning("{}", transmittance_lut_shader_template->configure(gl::shader_stage::vertex)); + } + + // Build transmittance LUT command buffer + rebuild_transmittance_lut_command_buffer(); // Create sky LUT texture and framebuffer (32F color, no depth) int sky_lut_width = 200; int sky_lut_height = 100; sky_lut_resolution = {static_cast(sky_lut_width), static_cast(sky_lut_height)}; - sky_lut_texture = new gl::texture_2d(sky_lut_width, sky_lut_height, gl::pixel_type::float_32, gl::pixel_format::rgb); + sky_lut_texture = std::make_unique(sky_lut_width, sky_lut_height, gl::pixel_type::float_32, gl::pixel_format::rgb); sky_lut_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); sky_lut_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); sky_lut_texture->set_max_anisotropy(0.0f); - sky_lut_framebuffer = new gl::framebuffer(sky_lut_width, sky_lut_height); - sky_lut_framebuffer->attach(gl::framebuffer_attachment_type::color, sky_lut_texture); + sky_lut_framebuffer = std::make_unique(sky_lut_width, sky_lut_height); + sky_lut_framebuffer->attach(gl::framebuffer_attachment_type::color, sky_lut_texture.get()); // Load sky LUT shader template - sky_lut_shader_template = resource_manager->load("sky-illuminance-lut.glsl"); + sky_lut_shader_template = resource_manager->load("sky-illuminance-lut.glsl"); // Build sky LUT shader program - sky_lut_shader_program = sky_lut_shader_template->build(); - sky_lut_light_direction_input = sky_lut_shader_program->get_input("light_direction"); - sky_lut_light_illuminance_input = sky_lut_shader_program->get_input("light_illuminance"); - sky_lut_atmosphere_radii_input = sky_lut_shader_program->get_input("atmosphere_radii"); - sky_lut_observer_position_input = sky_lut_shader_program->get_input("observer_position"); - sky_lut_rayleigh_parameters_input = sky_lut_shader_program->get_input("rayleigh_parameters"); - sky_lut_mie_parameters_input = sky_lut_shader_program->get_input("mie_parameters"); - sky_lut_ozone_distribution_input = sky_lut_shader_program->get_input("ozone_distribution"); - sky_lut_ozone_absorption_input = sky_lut_shader_program->get_input("ozone_absorption"); - sky_lut_airglow_illuminance_input = sky_lut_shader_program->get_input("airglow_illuminance"); - sky_lut_resolution_input = sky_lut_shader_program->get_input("resolution"); - sky_lut_transmittance_lut_input = sky_lut_shader_program->get_input("transmittance_lut"); - sky_lut_transmittance_lut_resolution_input = sky_lut_shader_program->get_input("transmittance_lut_resolution"); -} - -sky_pass::~sky_pass() -{ - delete sky_lut_framebuffer; - delete sky_lut_texture; - delete transmittance_lut_framebuffer; - delete transmittance_lut_texture; - delete quad_vao; - delete quad_vbo; - - delete transmittance_shader_program; - delete sky_lut_shader_program; - - /// @TODO - // resource_maanger->unload("transmittance-lut.glsl"); - // resource_maanger->unload("sky-illuminance-lut.glsl"); + sky_lut_shader_program = sky_lut_shader_template->build + ( + { + {"SKY_ILLUMINANCE_SAMPLES", std::to_string(30)} + } + ); + if (!sky_lut_shader_program->linked()) + { + debug::log::error("Failed to by sky LUT shader program: {}", sky_lut_shader_program->info()); + debug::log::warning("{}", sky_lut_shader_template->configure(gl::shader_stage::vertex)); + } + + // Build sky LUT command buffer + rebuild_sky_lut_command_buffer(); } -void sky_pass::render(const render::context& ctx, render::queue& queue) const +void sky_pass::render(const render::context& ctx, render::queue& queue) { glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); @@ -183,17 +181,10 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const if (render_transmittance_lut) { // Render transmittance LUT - rasterizer->set_viewport(0, 0, transmittance_lut_texture->get_width(), transmittance_lut_texture->get_height()); - rasterizer->use_framebuffer(*transmittance_lut_framebuffer); - rasterizer->use_program(*transmittance_shader_program); - transmittance_atmosphere_radii_input->upload(atmosphere_radii); - transmittance_rayleigh_parameters_input->upload(rayleigh_parameters); - transmittance_mie_parameters_input->upload(mie_parameters); - transmittance_ozone_distribution_input->upload(ozone_distribution); - transmittance_ozone_absorption_input->upload(ozone_absorption); - if (transmittance_resolution_input) - transmittance_resolution_input->upload(transmittance_lut_resolution); - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + for (const auto& command: transmittance_lut_command_buffer) + { + command(); + } // Don't render transmittance LUT next frame unless parameters have changed. render_transmittance_lut = false; @@ -212,7 +203,7 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const float4x4 model_view_projection = projection * model_view; // Interpolate observer position - float3 observer_position = observer_position_tween.interpolate(ctx.alpha); + observer_position = observer_position_tween.interpolate(ctx.alpha); // Construct tweened ICRF to EUS transformation math::transformation::se3 icrf_to_eus = @@ -236,34 +227,21 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const float sun_y = color::aces::ap1.luminance(sun_transmitted_illuminance); float moon_y = color::aces::ap1.luminance(moon_transmitted_illuminance); - float3 dominant_light_direction = (sun_y > moon_y) ? sun_direction : moon_direction; - float3 dominant_light_illuminance = (sun_y > moon_y) ? sun_illuminance : moon_illuminance; + dominant_light_direction = (sun_y > moon_y) ? sun_direction : moon_direction; + dominant_light_illuminance = (sun_y > moon_y) ? sun_illuminance : moon_illuminance; if (moon_y > sun_y) + { sun_luminance *= 0.0f; + } - // Render sky illuminance LUT - auto sky_lut_viewport = sky_lut_framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(sky_lut_viewport), std::get<1>(sky_lut_viewport)); - rasterizer->use_framebuffer(*sky_lut_framebuffer); - rasterizer->use_program(*sky_lut_shader_program); - sky_lut_light_direction_input->upload(dominant_light_direction); - sky_lut_light_illuminance_input->upload(dominant_light_illuminance); - sky_lut_atmosphere_radii_input->upload(atmosphere_radii); - sky_lut_observer_position_input->upload(observer_position); - sky_lut_rayleigh_parameters_input->upload(rayleigh_parameters); - sky_lut_mie_parameters_input->upload(mie_parameters); - sky_lut_ozone_distribution_input->upload(ozone_distribution); - sky_lut_ozone_absorption_input->upload(ozone_absorption); - sky_lut_airglow_illuminance_input->upload(airglow_illuminance * ctx.exposure); - if (sky_lut_resolution_input) - sky_lut_resolution_input->upload(sky_lut_resolution); - sky_lut_transmittance_lut_input->upload(transmittance_lut_texture); - if (sky_lut_transmittance_lut_resolution_input) - sky_lut_transmittance_lut_resolution_input->upload(transmittance_lut_resolution); - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); - + camera_exposure = ctx.exposure; + // Render sky illuminance LUT + for (const auto& command: sky_lut_command_buffer) + { + command(); + } rasterizer->use_framebuffer(*framebuffer); auto viewport = framebuffer->get_dimensions(); @@ -271,33 +249,33 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const float2 resolution = {static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}; // Draw atmosphere - if (sky_model) + if (sky_model && sky_shader_program) { rasterizer->use_program(*sky_shader_program); // Upload shader parameters - if (model_view_projection_input) - model_view_projection_input->upload(model_view_projection); - if (mouse_input) - mouse_input->upload(mouse_position); - if (resolution_input) - resolution_input->upload(resolution); - if (light_direction_input) - light_direction_input->upload(dominant_light_direction); - if (sun_luminance_input) - sun_luminance_input->upload(sun_luminance); - if (sun_angular_radius_input) - sun_angular_radius_input->upload(sun_angular_radius * magnification); - if (atmosphere_radii_input) - atmosphere_radii_input->upload(atmosphere_radii); - if (observer_position_input) - observer_position_input->upload(observer_position); - if (sky_illuminance_lut_input) - sky_illuminance_lut_input->upload(sky_lut_texture); - if (sky_illuminance_lut_resolution_input) - sky_illuminance_lut_resolution_input->upload(sky_lut_resolution); + if (model_view_projection_var) + model_view_projection_var->update(model_view_projection); + if (mouse_var) + mouse_var->update(mouse_position); + if (resolution_var) + resolution_var->update(resolution); + if (light_direction_var) + light_direction_var->update(dominant_light_direction); + if (sun_luminance_var) + sun_luminance_var->update(sun_luminance); + if (sun_angular_radius_var) + sun_angular_radius_var->update(sun_angular_radius * magnification); + if (atmosphere_radii_var) + atmosphere_radii_var->update(atmosphere_radii); + if (observer_position_var) + observer_position_var->update(observer_position); + if (sky_illuminance_lut_var) + sky_illuminance_lut_var->update(*sky_lut_texture); + if (sky_illuminance_lut_resolution_var) + sky_illuminance_lut_resolution_var->update(sky_lut_resolution); - sky_material->upload(ctx.alpha); + //sky_material->update(ctx.alpha); rasterizer->draw_arrays(*sky_model_vao, sky_model_drawing_mode, sky_model_start_index, sky_model_index_count); } @@ -307,7 +285,7 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const //glBlendFunc(GL_ONE, GL_ONE); // Draw stars - if (stars_model) + if (star_shader_program) { float star_distance = (clip_near + clip_far) * 0.5f; @@ -317,22 +295,23 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const model_view = view * model; rasterizer->use_program(*star_shader_program); - if (star_model_view_input) - star_model_view_input->upload(model_view); - if (star_projection_input) - star_projection_input->upload(projection); - if (star_distance_input) - star_distance_input->upload(star_distance); - if (star_exposure_input) - star_exposure_input->upload(ctx.exposure); + if (star_model_view_var) + star_model_view_var->update(model_view); + if (star_projection_var) + star_projection_var->update(projection); + if (star_distance_var) + star_distance_var->update(star_distance); + if (star_exposure_var) + star_exposure_var->update(ctx.exposure); - star_material->upload(ctx.alpha); + //star_material->update(ctx.alpha); rasterizer->draw_arrays(*stars_model_vao, stars_model_drawing_mode, stars_model_start_index, stars_model_index_count); } // Draw moon model //if (moon_position.y() >= -moon_angular_radius) + if (moon_shader_program) { float moon_distance = (clip_near + clip_far) * 0.5f; float moon_radius = moon_angular_radius * moon_distance; @@ -346,61 +325,62 @@ void sky_pass::render(const render::context& ctx, render::queue& queue) const float3x3 normal_model = math::transpose(math::inverse(float3x3(model))); rasterizer->use_program(*moon_shader_program); - if (moon_model_input) - moon_model_input->upload(model); - if (moon_view_projection_input) - moon_view_projection_input->upload(view_projection); - if (moon_normal_model_input) - moon_normal_model_input->upload(normal_model); - if (moon_camera_position_input) - moon_camera_position_input->upload(ctx.camera_transform.translation); - if (moon_sunlight_direction_input) - moon_sunlight_direction_input->upload(math::normalize(moon_sunlight_direction_tween.interpolate(ctx.alpha))); - if (moon_sunlight_illuminance_input) - moon_sunlight_illuminance_input->upload(moon_sunlight_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure); - if (moon_planetlight_direction_input) - moon_planetlight_direction_input->upload(math::normalize(moon_planetlight_direction_tween.interpolate(ctx.alpha))); - if (moon_planetlight_illuminance_input) - moon_planetlight_illuminance_input->upload(moon_planetlight_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure); + if (moon_model_var) + moon_model_var->update(model); + if (moon_view_projection_var) + moon_view_projection_var->update(view_projection); + if (moon_normal_model_var) + moon_normal_model_var->update(normal_model); + if (moon_camera_position_var) + moon_camera_position_var->update(ctx.camera_transform.translation); + if (moon_sunlight_direction_var) + moon_sunlight_direction_var->update(math::normalize(moon_sunlight_direction_tween.interpolate(ctx.alpha))); + if (moon_sunlight_illuminance_var) + moon_sunlight_illuminance_var->update(moon_sunlight_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure); + if (moon_planetlight_direction_var) + moon_planetlight_direction_var->update(math::normalize(moon_planetlight_direction_tween.interpolate(ctx.alpha))); + if (moon_planetlight_illuminance_var) + moon_planetlight_illuminance_var->update(moon_planetlight_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure); - moon_material->upload(ctx.alpha); + //moon_material->update(ctx.alpha); rasterizer->draw_arrays(*moon_model_vao, moon_model_drawing_mode, moon_model_start_index, moon_model_index_count); } } -void sky_pass::set_sky_model(const model* model) +void sky_pass::set_sky_model(std::shared_ptr model) { sky_model = model; + sky_shader_program = nullptr; if (sky_model) { - sky_model_vao = model->get_vertex_array(); - - const std::vector& groups = *model->get_groups(); - for (model_group* group: groups) + sky_model_vao = model->get_vertex_array().get(); + + for (const auto& group: model->get_groups()) { - sky_material = group->get_material(); - sky_model_drawing_mode = group->get_drawing_mode(); - sky_model_start_index = group->get_start_index(); - sky_model_index_count = group->get_index_count(); + sky_material = group.material.get(); + sky_model_drawing_mode = group.drawing_mode; + sky_model_start_index = group.start_index; + sky_model_index_count = group.index_count; } + if (sky_material) { - sky_shader_program = sky_material->get_shader_program(); + sky_shader_program = sky_material->get_shader_template()->build(); if (sky_shader_program) { - model_view_projection_input = sky_shader_program->get_input("model_view_projection"); - mouse_input = sky_shader_program->get_input("mouse"); - resolution_input = sky_shader_program->get_input("resolution"); - light_direction_input = sky_shader_program->get_input("light_direction"); - sun_luminance_input = sky_shader_program->get_input("sun_luminance"); - sun_angular_radius_input = sky_shader_program->get_input("sun_angular_radius"); - atmosphere_radii_input = sky_shader_program->get_input("atmosphere_radii"); - observer_position_input = sky_shader_program->get_input("observer_position"); - sky_illuminance_lut_input = sky_shader_program->get_input("sky_illuminance_lut"); - sky_illuminance_lut_resolution_input = sky_shader_program->get_input("sky_illuminance_lut_resolution"); + model_view_projection_var = sky_shader_program->variable("model_view_projection"); + mouse_var = sky_shader_program->variable("mouse"); + resolution_var = sky_shader_program->variable("resolution"); + light_direction_var = sky_shader_program->variable("light_direction"); + sun_luminance_var = sky_shader_program->variable("sun_luminance"); + sun_angular_radius_var = sky_shader_program->variable("sun_angular_radius"); + atmosphere_radii_var = sky_shader_program->variable("atmosphere_radii"); + observer_position_var = sky_shader_program->variable("observer_position"); + sky_illuminance_lut_var = sky_shader_program->variable("sky_illuminance_lut"); + sky_illuminance_lut_resolution_var = sky_shader_program->variable("sky_illuminance_lut_resolution"); } } } @@ -410,37 +390,37 @@ void sky_pass::set_sky_model(const model* model) } } -void sky_pass::set_moon_model(const model* model) +void sky_pass::set_moon_model(std::shared_ptr model) { moon_model = model; + moon_shader_program = nullptr; if (moon_model) { - moon_model_vao = model->get_vertex_array(); - - const std::vector& groups = *model->get_groups(); - for (model_group* group: groups) + moon_model_vao = model->get_vertex_array().get(); + + for (const auto& group: model->get_groups()) { - moon_material = group->get_material(); - moon_model_drawing_mode = group->get_drawing_mode(); - moon_model_start_index = group->get_start_index(); - moon_model_index_count = group->get_index_count(); + moon_material = group.material.get(); + moon_model_drawing_mode = group.drawing_mode; + moon_model_start_index = group.start_index; + moon_model_index_count = group.index_count; } if (moon_material) { - moon_shader_program = moon_material->get_shader_program(); + moon_shader_program = moon_material->get_shader_template()->build(); if (moon_shader_program) { - moon_model_input = moon_shader_program->get_input("model"); - moon_view_projection_input = moon_shader_program->get_input("view_projection"); - moon_normal_model_input = moon_shader_program->get_input("normal_model"); - moon_camera_position_input = moon_shader_program->get_input("camera_position"); - moon_sunlight_direction_input = moon_shader_program->get_input("sunlight_direction"); - moon_sunlight_illuminance_input = moon_shader_program->get_input("sunlight_illuminance"); - moon_planetlight_direction_input = moon_shader_program->get_input("planetlight_direction"); - moon_planetlight_illuminance_input = moon_shader_program->get_input("planetlight_illuminance"); + moon_model_var = moon_shader_program->variable("model"); + moon_view_projection_var = moon_shader_program->variable("view_projection"); + moon_normal_model_var = moon_shader_program->variable("normal_model"); + moon_camera_position_var = moon_shader_program->variable("camera_position"); + moon_sunlight_direction_var = moon_shader_program->variable("sunlight_direction"); + moon_sunlight_illuminance_var = moon_shader_program->variable("sunlight_illuminance"); + moon_planetlight_direction_var = moon_shader_program->variable("planetlight_direction"); + moon_planetlight_illuminance_var = moon_shader_program->variable("planetlight_illuminance"); } } } @@ -450,33 +430,32 @@ void sky_pass::set_moon_model(const model* model) } } -void sky_pass::set_stars_model(const model* model) +void sky_pass::set_stars_model(std::shared_ptr model) { stars_model = model; + star_shader_program = nullptr; if (stars_model) { - stars_model_vao = model->get_vertex_array(); - - const std::vector& groups = *model->get_groups(); - for (model_group* group: groups) + stars_model_vao = model->get_vertex_array().get(); + + for (const auto& group: model->get_groups()) { - star_material = group->get_material(); - stars_model_drawing_mode = group->get_drawing_mode(); - stars_model_start_index = group->get_start_index(); - stars_model_index_count = group->get_index_count(); + stars_model_drawing_mode = group.drawing_mode; + stars_model_start_index = group.start_index; + stars_model_index_count = group.index_count; } if (star_material) { - star_shader_program = star_material->get_shader_program(); + star_shader_program = star_material->get_shader_template()->build(); if (star_shader_program) { - star_model_view_input = star_shader_program->get_input("model_view"); - star_projection_input = star_shader_program->get_input("projection"); - star_distance_input = star_shader_program->get_input("star_distance"); - star_exposure_input = star_shader_program->get_input("camera.exposure"); + star_model_view_var = star_shader_program->variable("model_view"); + star_projection_var = star_shader_program->variable("projection"); + star_distance_var = star_shader_program->variable("star_distance"); + star_exposure_var = star_shader_program->variable("camera.exposure"); } } } @@ -662,4 +641,140 @@ void sky_pass::set_transmittance_lut_resolution(std::uint16_t width, std::uint16 render_transmittance_lut = true; } +void sky_pass::rebuild_transmittance_lut_command_buffer() +{ + transmittance_lut_command_buffer.clear(); + + if (!transmittance_lut_shader_program->linked() || !transmittance_lut_texture) + { + return; + } + + // Bind framebuffer and shader program + transmittance_lut_command_buffer.emplace_back + ( + [&]() + { + rasterizer->set_viewport(0, 0, transmittance_lut_texture->get_width(), transmittance_lut_texture->get_height()); + rasterizer->use_framebuffer(*transmittance_lut_framebuffer); + rasterizer->use_program(*transmittance_lut_shader_program); + } + ); + + // Update shader variables + if (auto atmosphere_radii_var = transmittance_lut_shader_program->variable("atmosphere_radii")) + { + transmittance_lut_command_buffer.emplace_back([&, atmosphere_radii_var](){atmosphere_radii_var->update(atmosphere_radii);}); + } + if (auto rayleigh_parameters_var = transmittance_lut_shader_program->variable("rayleigh_parameters")) + { + transmittance_lut_command_buffer.emplace_back([&, rayleigh_parameters_var](){rayleigh_parameters_var->update(rayleigh_parameters);}); + } + if (auto mie_parameters_var = transmittance_lut_shader_program->variable("mie_parameters")) + { + transmittance_lut_command_buffer.emplace_back([&, mie_parameters_var](){mie_parameters_var->update(mie_parameters);}); + } + if (auto ozone_distribution_var = transmittance_lut_shader_program->variable("ozone_distribution")) + { + transmittance_lut_command_buffer.emplace_back([&, ozone_distribution_var](){ozone_distribution_var->update(ozone_distribution);}); + } + if (auto ozone_absorption_var = transmittance_lut_shader_program->variable("ozone_absorption")) + { + transmittance_lut_command_buffer.emplace_back([&, ozone_absorption_var](){ozone_absorption_var->update(ozone_absorption);}); + } + if (auto resolution_var = transmittance_lut_shader_program->variable("resolution")) + { + transmittance_lut_command_buffer.emplace_back([&, resolution_var](){resolution_var->update(transmittance_lut_resolution);}); + } + + // Draw quad + transmittance_lut_command_buffer.emplace_back + ( + [&]() + { + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + } + ); +} + +void sky_pass::rebuild_sky_lut_command_buffer() +{ + sky_lut_command_buffer.clear(); + + if (!sky_lut_shader_program->linked() || !sky_lut_texture) + { + return; + } + + // Bind framebuffer and shader program + sky_lut_command_buffer.emplace_back + ( + [&]() + { + rasterizer->set_viewport(0, 0, sky_lut_texture->get_width(), sky_lut_texture->get_height()); + rasterizer->use_framebuffer(*sky_lut_framebuffer); + rasterizer->use_program(*sky_lut_shader_program); + } + ); + + // Update shader variables + if (auto light_direction_var = sky_lut_shader_program->variable("light_direction")) + { + sky_lut_command_buffer.emplace_back([&, light_direction_var](){light_direction_var->update(dominant_light_direction);}); + } + if (auto light_illuminance_var = sky_lut_shader_program->variable("light_illuminance")) + { + sky_lut_command_buffer.emplace_back([&, light_illuminance_var](){light_illuminance_var->update(dominant_light_illuminance);}); + } + if (auto atmosphere_radii_var = sky_lut_shader_program->variable("atmosphere_radii")) + { + sky_lut_command_buffer.emplace_back([&, atmosphere_radii_var](){atmosphere_radii_var->update(atmosphere_radii);}); + } + if (auto observer_position_var = sky_lut_shader_program->variable("observer_position")) + { + sky_lut_command_buffer.emplace_back([&, observer_position_var](){observer_position_var->update(observer_position);}); + } + if (auto rayleigh_parameters_var = sky_lut_shader_program->variable("rayleigh_parameters")) + { + sky_lut_command_buffer.emplace_back([&, rayleigh_parameters_var](){rayleigh_parameters_var->update(rayleigh_parameters);}); + } + if (auto mie_parameters_var = sky_lut_shader_program->variable("mie_parameters")) + { + sky_lut_command_buffer.emplace_back([&, mie_parameters_var](){mie_parameters_var->update(mie_parameters);}); + } + if (auto ozone_distribution_var = sky_lut_shader_program->variable("ozone_distribution")) + { + sky_lut_command_buffer.emplace_back([&, ozone_distribution_var](){ozone_distribution_var->update(ozone_distribution);}); + } + if (auto ozone_absorption_var = sky_lut_shader_program->variable("ozone_absorption")) + { + sky_lut_command_buffer.emplace_back([&, ozone_absorption_var](){ozone_absorption_var->update(ozone_absorption);}); + } + if (auto airglow_illuminance_var = sky_lut_shader_program->variable("airglow_illuminance")) + { + sky_lut_command_buffer.emplace_back([&, airglow_illuminance_var](){airglow_illuminance_var->update(airglow_illuminance * camera_exposure);}); + } + if (auto resolution_var = sky_lut_shader_program->variable("resolution")) + { + sky_lut_command_buffer.emplace_back([&, resolution_var](){resolution_var->update(sky_lut_resolution);}); + } + if (auto transmittance_lut_var = sky_lut_shader_program->variable("transmittance_lut")) + { + sky_lut_command_buffer.emplace_back([&, transmittance_lut_var](){transmittance_lut_var->update(*transmittance_lut_texture);}); + } + if (auto transmittance_lut_resolution_var = sky_lut_shader_program->variable("transmittance_lut_resolution")) + { + sky_lut_command_buffer.emplace_back([&, transmittance_lut_resolution_var](){transmittance_lut_resolution_var->update(transmittance_lut_resolution);}); + } + + // Draw quad + sky_lut_command_buffer.emplace_back + ( + [&]() + { + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + } + ); +} + } // namespace render diff --git a/src/engine/render/passes/sky-pass.hpp b/src/engine/render/passes/sky-pass.hpp index 417a450..4dae84e 100644 --- a/src/engine/render/passes/sky-pass.hpp +++ b/src/engine/render/passes/sky-pass.hpp @@ -21,12 +21,12 @@ #define ANTKEEPER_RENDER_SKY_PASS_HPP #include -#include +#include #include #include #include #include -#include +#include #include #include #include @@ -48,16 +48,16 @@ class sky_pass: public pass { public: sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - virtual ~sky_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; + virtual ~sky_pass() = default; + void render(const render::context& ctx, render::queue& queue) override; void update_tweens(); void set_magnification(float scale); - void set_sky_model(const model* model); - void set_moon_model(const model* model); - void set_stars_model(const model* model); + void set_sky_model(std::shared_ptr model); + void set_moon_model(std::shared_ptr model); + void set_stars_model(std::shared_ptr model); void set_icrf_to_eus(const math::transformation::se3& transformation); @@ -91,87 +91,79 @@ public: void set_transmittance_lut_resolution(std::uint16_t width, std::uint16_t height); private: - gl::vertex_buffer* quad_vbo; - gl::vertex_array* quad_vao; + void rebuild_transmittance_lut_command_buffer(); + void rebuild_sky_lut_command_buffer(); - gl::texture_2d* transmittance_lut_texture; - gl::framebuffer* transmittance_lut_framebuffer; + std::unique_ptr quad_vbo; + std::unique_ptr quad_vao; + + std::unique_ptr transmittance_lut_texture; + std::unique_ptr transmittance_lut_framebuffer; float2 transmittance_lut_resolution; - render::shader_template* transmittance_shader_template; - gl::shader_program* transmittance_shader_program; - const gl::shader_input* transmittance_atmosphere_radii_input; - const gl::shader_input* transmittance_rayleigh_parameters_input; - const gl::shader_input* transmittance_mie_parameters_input; - const gl::shader_input* transmittance_ozone_distribution_input; - const gl::shader_input* transmittance_ozone_absorption_input; - const gl::shader_input* transmittance_resolution_input; - mutable bool render_transmittance_lut; - - gl::texture_2d* sky_lut_texture; - gl::framebuffer* sky_lut_framebuffer; - render::shader_template* sky_lut_shader_template; - gl::shader_program* sky_lut_shader_program; + std::shared_ptr transmittance_lut_shader_template; + std::unique_ptr transmittance_lut_shader_program; + bool render_transmittance_lut; + std::vector> transmittance_lut_command_buffer; + + std::unique_ptr sky_lut_texture; + std::unique_ptr sky_lut_framebuffer; + std::shared_ptr sky_lut_shader_template; + std::unique_ptr sky_lut_shader_program; float2 sky_lut_resolution; - const gl::shader_input* sky_lut_light_direction_input; - const gl::shader_input* sky_lut_light_illuminance_input; - const gl::shader_input* sky_lut_atmosphere_radii_input; - const gl::shader_input* sky_lut_observer_position_input; - const gl::shader_input* sky_lut_rayleigh_parameters_input; - const gl::shader_input* sky_lut_mie_parameters_input; - const gl::shader_input* sky_lut_ozone_distribution_input; - const gl::shader_input* sky_lut_ozone_absorption_input; - const gl::shader_input* sky_lut_airglow_illuminance_input; - const gl::shader_input* sky_lut_resolution_input; - const gl::shader_input* sky_lut_transmittance_lut_input; - const gl::shader_input* sky_lut_transmittance_lut_resolution_input; + std::vector> sky_lut_command_buffer; + + float3 dominant_light_direction; + float3 dominant_light_illuminance; + float3 observer_position; + float camera_exposure; - gl::shader_program* sky_shader_program; - const gl::shader_input* model_view_projection_input; - const gl::shader_input* mouse_input; - const gl::shader_input* resolution_input; - const gl::shader_input* light_direction_input; - const gl::shader_input* sun_luminance_input; - const gl::shader_input* sun_angular_radius_input; - const gl::shader_input* atmosphere_radii_input; - const gl::shader_input* observer_position_input; - const gl::shader_input* sky_illuminance_lut_input; - const gl::shader_input* sky_illuminance_lut_resolution_input; - - gl::shader_program* moon_shader_program; - const gl::shader_input* moon_model_input; - const gl::shader_input* moon_view_projection_input; - const gl::shader_input* moon_normal_model_input; - const gl::shader_input* moon_camera_position_input; - const gl::shader_input* moon_sunlight_direction_input; - const gl::shader_input* moon_sunlight_illuminance_input; - const gl::shader_input* moon_planetlight_direction_input; - const gl::shader_input* moon_planetlight_illuminance_input; - - const model* sky_model; + std::shared_ptr sky_shader_program; + const gl::shader_variable* model_view_projection_var; + const gl::shader_variable* mouse_var; + const gl::shader_variable* resolution_var; + const gl::shader_variable* light_direction_var; + const gl::shader_variable* sun_luminance_var; + const gl::shader_variable* sun_angular_radius_var; + const gl::shader_variable* atmosphere_radii_var; + const gl::shader_variable* observer_position_var; + const gl::shader_variable* sky_illuminance_lut_var; + const gl::shader_variable* sky_illuminance_lut_resolution_var; + + std::shared_ptr moon_shader_program; + const gl::shader_variable* moon_model_var; + const gl::shader_variable* moon_view_projection_var; + const gl::shader_variable* moon_normal_model_var; + const gl::shader_variable* moon_camera_position_var; + const gl::shader_variable* moon_sunlight_direction_var; + const gl::shader_variable* moon_sunlight_illuminance_var; + const gl::shader_variable* moon_planetlight_direction_var; + const gl::shader_variable* moon_planetlight_illuminance_var; + + std::shared_ptr sky_model; const material* sky_material; const gl::vertex_array* sky_model_vao; gl::drawing_mode sky_model_drawing_mode; std::size_t sky_model_start_index; std::size_t sky_model_index_count; - const model* moon_model; + std::shared_ptr moon_model; const material* moon_material; const gl::vertex_array* moon_model_vao; gl::drawing_mode moon_model_drawing_mode; std::size_t moon_model_start_index; std::size_t moon_model_index_count; - const model* stars_model; + std::shared_ptr stars_model; const material* star_material; const gl::vertex_array* stars_model_vao; gl::drawing_mode stars_model_drawing_mode; std::size_t stars_model_start_index; std::size_t stars_model_index_count; - gl::shader_program* star_shader_program; - const gl::shader_input* star_model_view_input; - const gl::shader_input* star_projection_input; - const gl::shader_input* star_exposure_input; - const gl::shader_input* star_distance_input; + std::unique_ptr star_shader_program; + const gl::shader_variable* star_model_view_var; + const gl::shader_variable* star_projection_var; + const gl::shader_variable* star_exposure_var; + const gl::shader_variable* star_distance_var; float2 mouse_position; diff --git a/src/engine/render/passes/ui-pass.cpp b/src/engine/render/passes/ui-pass.cpp deleted file mode 100644 index 973ed5e..0000000 --- a/src/engine/render/passes/ui-pass.cpp +++ /dev/null @@ -1,91 +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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace render { - -ui_pass::ui_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): - pass(rasterizer, framebuffer) -{} - -ui_pass::~ui_pass() -{} - -void ui_pass::render(const render::context& ctx, render::queue& queue) const -{ - glEnable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glCullFace(GL_BACK); - - auto viewport = framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); - - 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 = *ctx.collection->get_objects(scene::billboard::object_type_id); - - // Sort billboards - - // Rebuild vertex buffer -} - -const ui_pass::parameter_set* ui_pass::load_parameter_set(const gl::shader_program* program) const -{ - // Allocate a new parameter set - parameter_set* parameters = new parameter_set(); - - // Connect inputs - parameters->time = program->get_input("time"); - parameters->model_view_projection = program->get_input("model_view_projection"); - - // Add parameter set to map of parameter sets - parameter_sets[program] = parameters; - - return parameters; -} - -} // namespace render diff --git a/src/engine/render/passes/ui-pass.hpp b/src/engine/render/passes/ui-pass.hpp deleted file mode 100644 index fde9829..0000000 --- a/src/engine/render/passes/ui-pass.hpp +++ /dev/null @@ -1,61 +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 . - */ - -#ifndef ANTKEEPER_RENDER_UI_PASS_HPP -#define ANTKEEPER_RENDER_UI_PASS_HPP - -#include -#include -#include -#include -#include -#include - -class resource_manager; - -namespace render { - -/** - * - */ -class ui_pass: public pass -{ -public: - ui_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - virtual ~ui_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; - -private: - /** - * Sets of known shader input parameters. Each time a new shader is encountered, a parameter set will be created and its inputs connected to the shader program. A null input indiciates that the shader doesn't have that parameter. - */ - struct parameter_set - { - const gl::shader_input* time; - const gl::shader_input* model_view_projection; - }; - - const parameter_set* load_parameter_set(const gl::shader_program* program) const; - - mutable std::unordered_map parameter_sets; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_UI_PASS_HPP diff --git a/src/engine/render/renderer.cpp b/src/engine/render/renderer.cpp index d99f520..7d942cc 100644 --- a/src/engine/render/renderer.cpp +++ b/src/engine/render/renderer.cpp @@ -50,19 +50,13 @@ renderer::renderer() billboard_op.instance_count = 0; // Allocate skinning palette - skinning_palette = new float4x4[MATERIAL_PASS_MAX_BONE_COUNT]; + skinning_palette.resize(64); // Construct culling stage - culling_stage = new render::culling_stage(); + culling_stage = std::make_unique(); } -renderer::~renderer() -{ - delete[] skinning_palette; - delete culling_stage; -} - -void renderer::render(float t, float dt, float alpha, const scene::collection& collection) const +void renderer::render(float t, float dt, float alpha, const scene::collection& collection) { // Get list of all objects in the collection const std::list* objects = collection.get_objects(); @@ -92,7 +86,7 @@ void renderer::render(float t, float dt, float alpha, const scene::collection& c ctx.alpha = alpha; // Process cameras in order - for (const scene::camera* camera: sorted_cameras) + for (scene::camera* camera: sorted_cameras) { // Skip inactive cameras if (!camera->is_active()) @@ -101,7 +95,7 @@ void renderer::render(float t, float dt, float alpha, const scene::collection& c } // Skip cameras with no compositors - const compositor* compositor = camera->get_compositor(); + compositor* compositor = camera->get_compositor(); if (!compositor) { continue; @@ -157,44 +151,52 @@ void renderer::process_object(const render::context& ctx, render::queue& queue, 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(); + const model* model = model_instance->get_model().get(); if (!model) return; - const std::vector* instance_materials = model_instance->get_materials(); - const std::vector* groups = model->get_groups(); + const auto& instance_materials = model_instance->get_materials(); + const auto& material_groups = model->get_groups(); render::operation operation; operation.transform = math::matrix_cast(model_instance->get_transform_tween().interpolate(ctx.alpha)); operation.depth = ctx.clip_near.signed_distance(float3(operation.transform[3])); - operation.vertex_array = model->get_vertex_array(); + operation.vertex_array = model->get_vertex_array().get(); operation.instance_count = model_instance->get_instance_count(); // Skinning parameters operation.bone_count = model_instance->get_pose().size(); if (operation.bone_count) { - operation.skinning_palette = skinning_palette; - ::matrix_palette(model->get_skeleton().inverse_bind_pose, model_instance->get_pose(), skinning_palette); + /// @TODO skinning palette should be model instance-specific + //operation.skinning_palette = skinning_palette.data(); + //::matrix_palette(model->get_skeleton().inverse_bind_pose, model_instance->get_pose(), skinning_palette.data()); + operation.skinning_palette = nullptr; + } else { operation.skinning_palette = nullptr; } - for (model_group* group: *groups) + for (std::size_t i = 0; i < material_groups.size(); ++i) { - // Determine operation material - operation.material = group->get_material(); - if ((*instance_materials)[group->get_index()]) + const auto& group = material_groups[i]; + + if (instance_materials[i]) { // Override model group material with the instance's material - operation.material = (*instance_materials)[group->get_index()]; + operation.material = instance_materials[i].get(); + } + else + { + // Use model material + operation.material = group.material.get(); } - operation.drawing_mode = group->get_drawing_mode(); - operation.start_index = group->get_start_index(); - operation.index_count = group->get_index_count(); + operation.drawing_mode = group.drawing_mode; + operation.start_index = group.start_index; + operation.index_count = group.index_count; queue.push_back(operation); } @@ -203,7 +205,7 @@ void renderer::process_model_instance(const render::context& ctx, render::queue& void renderer::process_billboard(const render::context& ctx, render::queue& queue, const scene::billboard* billboard) const { math::transform billboard_transform = billboard->get_transform_tween().interpolate(ctx.alpha); - billboard_op.material = billboard->get_material(); + billboard_op.material = billboard->get_material().get(); billboard_op.depth = ctx.clip_near.signed_distance(float3(billboard_transform.translation)); // Align billboard diff --git a/src/engine/render/renderer.hpp b/src/engine/render/renderer.hpp index eb6e6a1..b19fc25 100644 --- a/src/engine/render/renderer.hpp +++ b/src/engine/render/renderer.hpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace scene { @@ -44,8 +45,10 @@ namespace render { class renderer { public: + /** + * Constructs a renderer. + */ renderer(); - ~renderer(); /** * Renders a collection of scene objects. @@ -55,7 +58,7 @@ public: * @param alpha Subframe interpolation factor. * @param collection Collection of scene objects to render. */ - void render(float t, float dt, float alpha, const scene::collection& collection) const; + void render(float t, float dt, float alpha, const scene::collection& collection); /** * Sets the VAO to be used when generating render operations for billboards. @@ -70,9 +73,8 @@ private: void process_text(const render::context& ctx, render::queue& queue, const scene::text* text) const; mutable render::operation billboard_op; - float4x4* skinning_palette; - - render::culling_stage* culling_stage; + std::vector skinning_palette; + std::unique_ptr culling_stage; }; } // namespace render diff --git a/src/engine/render/shader-template.cpp b/src/engine/render/shader-template.cpp deleted file mode 100644 index 1dde337..0000000 --- a/src/engine/render/shader-template.cpp +++ /dev/null @@ -1,226 +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 . - */ - -#include -#include -#include - -namespace render { - -shader_template::shader_template(const std::string& source_code) -{ - source(source_code); -} - -shader_template::shader_template(): - hash(std::hash{}(std::string())) -{} - -void shader_template::source(const std::string& source) -{ - // Reset template - template_source.clear(); - vertex_directives.clear(); - fragment_directives.clear(); - geometry_directives.clear(); - define_directives.clear(); - - // Iterate through source line-by-line - std::istringstream source_stream(source); - std::string line; - while (std::getline(source_stream, line)) - { - std::string token; - std::istringstream line_stream(line); - - // Detect `#pragma` directives - if (line_stream >> token && token == "#pragma") - { - if (line_stream >> token) - { - // Map line numbers of supported directives - if (token == "define") - { - if (line_stream >> token) - define_directives.insert({token, template_source.size()}); - } - else if (token == "vertex") - vertex_directives.insert(template_source.size()); - else if (token == "fragment") - fragment_directives.insert(template_source.size()); - else if (token == "geometry") - geometry_directives.insert(template_source.size()); - } - } - - // Append line to template source - template_source.push_back(line); - } - - // Calculate hash of source - hash = std::hash{}(source); -} - -std::string shader_template::configure(gl::shader_stage stage, const dictionary_type& definitions) const -{ - replace_stage_directives(stage); - replace_define_directives(definitions); - - // Join vector of source lines into single string - std::ostringstream stream; - std::copy(template_source.begin(), template_source.end(), std::ostream_iterator(stream, "\n")); - return stream.str(); -} - -gl::shader_object* shader_template::compile(gl::shader_stage stage, const dictionary_type& definitions) const -{ - // Generate shader object source - std::string object_source = configure(stage, definitions); - - // Create new shader object - gl::shader_object* object = new gl::shader_object(stage); - - // Set shader object source - object->source(object_source); - - // Compile shader object - object->compile(); - - return object; -} - -gl::shader_program* shader_template::build(const dictionary_type& definitions) const -{ - gl::shader_object* vertex_object = nullptr; - gl::shader_object* fragment_object = nullptr; - gl::shader_object* geometry_object = nullptr; - - // Create shader program - gl::shader_program* program = new gl::shader_program(); - - if (has_vertex_directive()) - { - // Compile vertex shader object and attach to shader program - vertex_object = compile(gl::shader_stage::vertex, definitions); - program->attach(vertex_object); - } - - if (has_fragment_directive()) - { - // Compile fragment shader object and attach to shader program - fragment_object = compile(gl::shader_stage::fragment, definitions); - program->attach(fragment_object); - } - - if (has_geometry_directive()) - { - // Compile fragment shader object and attach to shader program - geometry_object = compile(gl::shader_stage::geometry, definitions); - program->attach(geometry_object); - } - - // Link attached shader objects into shader program - program->link(); - - if (vertex_object) - { - // Detach and delete vertex shader object - program->detach(vertex_object); - delete vertex_object; - } - - if (fragment_object) - { - // Detach and delete fragment shader object - program->detach(fragment_object); - delete fragment_object; - } - - if (geometry_object) - { - // Detach and delete geometry shader object - program->detach(geometry_object); - delete geometry_object; - } - - return program; -} - -void shader_template::replace_stage_directives(gl::shader_stage stage) const -{ - // Determine stage directives according to the shader stage being generated - const std::string vertex_directive = (stage == gl::shader_stage::vertex) ? "#define __VERTEX__" : "/* #undef __VERTEX__ */"; - const std::string fragment_directive = (stage == gl::shader_stage::fragment) ? "#define __FRAGMENT__" : "/* #undef __FRAGMENT__ */"; - const std::string geometry_directive = (stage == gl::shader_stage::geometry) ? "#define __GEOMETRY__" : "/* #undef __GEOMETRY__ */"; - - // Handle `#pragma ` directives - for (std::size_t i: vertex_directives) - template_source[i] = vertex_directive; - for (std::size_t i: fragment_directives) - template_source[i] = fragment_directive; - for (std::size_t i: geometry_directives) - template_source[i] = geometry_directive; -} - -void shader_template::replace_define_directives(const dictionary_type& definitions) const -{ - // For each `#pragma define ` directive - for (const auto& define_directive: define_directives) - { - // Get a reference to the directive line - std::string& line = template_source[define_directive.second]; - - // Check if the corresponding definition was given by the configuration - auto definitions_it = definitions.find(define_directive.first); - if (definitions_it != definitions.end()) - { - // Definition found, replace `#pragma define ` with `#define ` or `#define ` - line = "#define " + define_directive.first; - if (!definitions_it->second.empty()) - line += " " + definitions_it->second; - } - else - { - // Definition not found, replace `#pragma define ` with the comment `/* #undef */`. - line = "/* #undef " + define_directive.first + " */"; - } - } -} - -bool shader_template::has_vertex_directive() const -{ - return !vertex_directives.empty(); -} - -bool shader_template::has_fragment_directive() const -{ - return !fragment_directives.empty(); -} - -bool shader_template::has_geometry_directive() const -{ - return !geometry_directives.empty(); -} - -bool shader_template::has_define_directive(const std::string& key) const -{ - return (define_directives.find(key) != define_directives.end()); -} - -} // namespace render diff --git a/src/engine/render/stage.cpp b/src/engine/render/stage.cpp index cf6d1e8..4532775 100644 --- a/src/engine/render/stage.cpp +++ b/src/engine/render/stage.cpp @@ -21,8 +21,9 @@ namespace render { -stage::stage(): - priority(0) -{} +void stage::set_priority(int priority) +{ + this->priority = priority; +} } // namespace render diff --git a/src/engine/render/stage.hpp b/src/engine/render/stage.hpp index 0515390..5326d62 100644 --- a/src/engine/render/stage.hpp +++ b/src/engine/render/stage.hpp @@ -30,16 +30,6 @@ namespace render { class stage { public: - /** - * Constructs a render stage and sets it priority. - * - * @param priority Stage execution order priority. - */ - stage(int priority); - - /// Constructs a render stage. - stage(); - /// Destructs a render stage. virtual ~stage() = default; @@ -48,7 +38,7 @@ public: * * @param ctx Render context. */ - virtual void execute(render::context& ctx) const = 0; + virtual void execute(render::context& ctx) = 0; /** * Sets the priority of the stage's execution order in the render pipeline. @@ -58,17 +48,15 @@ public: void set_priority(int priority); /// Returns the priority of the stage's execution order in the render pipeline. - int get_priority() const noexcept; + [[nodiscard]] inline int get_priority() const noexcept + { + return priority; + } private: - int priority; + int priority{0}; }; -inline int stage::get_priority() const noexcept -{ - return priority; -} - } // namespace render #endif // ANTKEEPER_RENDER_STAGE_HPP diff --git a/src/engine/render/stage/culling-stage.cpp b/src/engine/render/stage/culling-stage.cpp index 0d06213..99191e4 100644 --- a/src/engine/render/stage/culling-stage.cpp +++ b/src/engine/render/stage/culling-stage.cpp @@ -26,7 +26,7 @@ namespace render { -void culling_stage::execute(render::context& ctx) const +void culling_stage::execute(render::context& ctx) { // Get list of all objects in the collection const std::list& objects = *(ctx.collection->get_objects()); @@ -34,7 +34,9 @@ void culling_stage::execute(render::context& ctx) const // Get camera culling volume ctx.camera_culling_volume = ctx.camera->get_culling_mask(); if (!ctx.camera_culling_volume) + { ctx.camera_culling_volume = &ctx.camera->get_world_bounds(); + } // Clear set of visible objects ctx.visible_objects.clear(); @@ -52,7 +54,9 @@ void culling_stage::execute(render::context& ctx) const { // Ignore inactive objects and cameras if (!object->is_active() || object->get_object_type_id() == scene::camera::object_type_id) + { return; + } // Cull object if it doesn't share any common layers with the camera //if (!(object->get_layer_mask() & camera_layer_mask)) @@ -61,11 +65,15 @@ void culling_stage::execute(render::context& ctx) const // Get object culling volume const geom::bounding_volume* object_culling_volume = object->get_culling_mask(); if (!object_culling_volume) + { object_culling_volume = &object->get_world_bounds(); + } // Cull object if it's outside of the camera culling volume if (!ctx.camera_culling_volume->intersects(*object_culling_volume)) + { return; + } // Insert object into set of visible objects std::lock_guard guard(mutex); diff --git a/src/engine/render/stage/culling-stage.hpp b/src/engine/render/stage/culling-stage.hpp index 68e5a2f..b8fce3f 100644 --- a/src/engine/render/stage/culling-stage.hpp +++ b/src/engine/render/stage/culling-stage.hpp @@ -30,14 +30,7 @@ namespace render { class culling_stage: public stage { public: - /// Constructs a culling stage. - culling_stage() = default; - - /// Destructs a culling stage. - virtual ~culling_stage() = default; - - /// @copydoc render::stage::execute(render::context&) - virtual void execute(render::context& ctx) const final; + void execute(render::context& ctx) override; }; } // namespace render diff --git a/src/engine/render/vertex-attribute.hpp b/src/engine/render/vertex-attribute.hpp index f91aa94..f94cc30 100644 --- a/src/engine/render/vertex-attribute.hpp +++ b/src/engine/render/vertex-attribute.hpp @@ -20,6 +20,8 @@ #ifndef ANTKEEPER_RENDER_VERTEX_ATTRIBUTE_HPP #define ANTKEEPER_RENDER_VERTEX_ATTRIBUTE_HPP +#include + namespace render { /** @@ -27,7 +29,7 @@ namespace render { */ namespace vertex_attribute { - enum + enum: std::uint8_t { /// Vertex position (vec3) position, @@ -44,7 +46,7 @@ namespace vertex_attribute /// Vertex color (vec4) color, - /// Vertex bone indices (vec4) + /// Vertex bone indices (uvec4) bone_index, /// Vertex bone weights (vec4) diff --git a/src/engine/resources/deserialize-context.cpp b/src/engine/resources/deserialize-context.cpp deleted file mode 100644 index fcf2bca..0000000 --- a/src/engine/resources/deserialize-context.cpp +++ /dev/null @@ -1,176 +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 . - */ - -#include -#include -#include - -deserialize_context::deserialize_context(void* handle): - handle(handle), - m_eof(false), - m_error(false) -{} - -std::size_t deserialize_context::read8(std::byte* data, std::size_t count) -{ - const PHYSFS_sint64 status = PHYSFS_readBytes(reinterpret_cast(handle), data, count); - - if (status < 0) - { - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return 0; - } - - if (status != count) - { - m_eof = true; - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return static_cast(count); - } - - return count; -} - -std::size_t deserialize_context::read16_le(std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - PHYSFS_uint16* data16 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_readULE16(file, data16)) - { - m_eof = (PHYSFS_eof(file) != 0); - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data16; - } - - return count; -} - -std::size_t deserialize_context::read16_be(std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - PHYSFS_uint16* data16 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_readUBE16(file, data16)) - { - m_eof = (PHYSFS_eof(file) != 0); - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data16; - } - - return count; -} - -std::size_t deserialize_context::read32_le(std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - PHYSFS_uint32* data32 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_readULE32(file, data32)) - { - m_eof = (PHYSFS_eof(file) != 0); - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data32; - } - - return count; -} - -std::size_t deserialize_context::read32_be(std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - PHYSFS_uint32* data32 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_readUBE32(file, data32)) - { - m_eof = (PHYSFS_eof(file) != 0); - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data32; - } - - return count; -} - -std::size_t deserialize_context::read64_le(std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - PHYSFS_uint64* data64 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_readULE64(file, data64)) - { - m_eof = (PHYSFS_eof(file) != 0); - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data64; - } - - return count; -} - -std::size_t deserialize_context::read64_be(std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - PHYSFS_uint64* data64 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_readUBE64(file, data64)) - { - m_eof = (PHYSFS_eof(file) != 0); - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data64; - } - - return count; -} diff --git a/src/engine/resources/deserialize-context.hpp b/src/engine/resources/deserialize-context.hpp index 6ba847c..590d8ec 100644 --- a/src/engine/resources/deserialize-context.hpp +++ b/src/engine/resources/deserialize-context.hpp @@ -22,6 +22,7 @@ #include #include +#include /** * Provides access to a deserialization state. @@ -29,6 +30,42 @@ struct deserialize_context { public: + /** + * Returns the path associated with this deserialize context. + */ + [[nodiscard]] virtual const std::filesystem::path& path() const noexcept = 0; + + /** + * Returns `true` if an error occured during a read operation or initialization, `false` otherwise. + */ + [[nodiscard]] virtual bool error() const noexcept = 0; + + /** + * Returns `true` if the end of a file was reached. + */ + [[nodiscard]] virtual bool eof() const noexcept = 0; + + /** + * Returns the size of the file, in bytes. + */ + [[nodiscard]] virtual std::size_t size() const noexcept = 0; + + /** + * Returns the offsets from the start of the file to the current position, in bytes. + * + * @throw deserialize_error Tell error. + */ + [[nodiscard]] virtual std::size_t tell() const = 0; + + /** + * Seeks to a position in the file. + * + * @param offset Offset from the start of the file, in bytes. + * + * @throw deserialize_error Seek error. + */ + virtual void seek(std::size_t offset) = 0; + /** * Reads 8-bit (byte) data. * @@ -39,7 +76,7 @@ public: * * @throw deserialize_error Read error. */ - std::size_t read8(std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t read8(std::byte* data, std::size_t count) noexcept(false) = 0; /** * Reads 16-bit (word) little-endian data. @@ -51,7 +88,7 @@ public: * * @throw deserialize_error Read error. */ - std::size_t read16_le(std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t read16_le(std::byte* data, std::size_t count) noexcept(false) = 0; /** * Reads 16-bit (word) big-endian data. @@ -63,7 +100,7 @@ public: * * @throw deserialize_error Read error. */ - std::size_t read16_be(std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t read16_be(std::byte* data, std::size_t count) noexcept(false) = 0; /** * Reads 16-bit (word) data. @@ -100,7 +137,7 @@ public: * * @throw deserialize_error Read error. */ - std::size_t read32_le(std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t read32_le(std::byte* data, std::size_t count) noexcept(false) = 0; /** * Reads 32-bit (double word) big-endian data. @@ -112,7 +149,7 @@ public: * * @throw deserialize_error Read error. */ - std::size_t read32_be(std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t read32_be(std::byte* data, std::size_t count) noexcept(false) = 0; /** * Reads 32-bit (double word) data. @@ -149,7 +186,7 @@ public: * * @throw deserialize_error Read error. */ - std::size_t read64_le(std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t read64_le(std::byte* data, std::size_t count) noexcept(false) = 0; /** * Reads 64-bit (quad word) big-endian data. @@ -161,7 +198,7 @@ public: * * @throw deserialize_error Read error. */ - std::size_t read64_be(std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t read64_be(std::byte* data, std::size_t count) noexcept(false) = 0; /** * Reads 64-bit (quad word) data. @@ -187,32 +224,6 @@ public: return read64_be(data, count); } } - - /** - * Returns `true` if the end of a file was reached. - */ - [[nodiscard]] inline bool eof() const noexcept - { - return m_eof; - } - - /** - * Returns `true` if an error occured during a read operation, `false` otherwise. - */ - [[nodiscard]] inline bool error() const noexcept - { - return m_error; - } - -private: - template - friend class resource_loader; - - deserialize_context(void* handle); - - void* handle; - bool m_eof; - bool m_error; }; #endif // ANTKEEPER_RESOURCES_DESERIALIZE_CONTEXT_HPP diff --git a/src/engine/resources/file-buffer-loader.cpp b/src/engine/resources/file-buffer-loader.cpp deleted file mode 100644 index 4fa2dba..0000000 --- a/src/engine/resources/file-buffer-loader.cpp +++ /dev/null @@ -1,35 +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 . - */ - -#include -#include -#include - -template <> -file_buffer* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - file_buffer* buffer = new file_buffer(); - - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - buffer->resize(size); - PHYSFS_readBytes(file, buffer->data(), size); - - return buffer; -} diff --git a/src/engine/resources/image-loader.cpp b/src/engine/resources/image-loader.cpp deleted file mode 100644 index 22442f7..0000000 --- a/src/engine/resources/image-loader.cpp +++ /dev/null @@ -1,169 +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 . - */ - -#include -#include -#include -#include -#include -#include -#include - -template <> -image* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - unsigned char* buffer; - int size; - ::image* image = nullptr; - - // Read input stream into buffer - size = static_cast(PHYSFS_fileLength(file)); - buffer = new unsigned char[size]; - PHYSFS_readBytes(file, buffer, size); - - // Select loader according to file extension - if (path.extension() == ".exr") - { - // Load OpenEXR with TinyEXR - int status = TINYEXR_SUCCESS; - const char* error = nullptr; - - // Read EXR version - EXRVersion exr_version; - status = ParseEXRVersionFromMemory(&exr_version, buffer, size); - if (status != TINYEXR_SUCCESS) - { - delete[] buffer; - throw std::runtime_error("TinyEXR parse version error (" + std::to_string(status) + "): invalid EXR file"); - } - - // Check if image is multipart - if (exr_version.multipart) - { - throw std::runtime_error("OpenEXR multipart images not supported"); - } - - // Read EXR header - EXRHeader exr_header; - InitEXRHeader(&exr_header); - status = ParseEXRHeaderFromMemory(&exr_header, &exr_version, buffer, size, &error); - if (status != TINYEXR_SUCCESS) - { - std::string error_string(error); - FreeEXRErrorMessage(error); - delete[] buffer; - throw std::runtime_error("TinyEXR parse header error (" + std::to_string(status) + "): " + error_string); - } - - // Check if image is tiled - if (exr_header.tiled) - { - FreeEXRHeader(&exr_header); - delete[] buffer; - throw std::runtime_error("OpenEXR tiled images not supported"); - } - - // Read half channels as float - for (int i = 0; i < exr_header.num_channels; ++i) - { - if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) - { - exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; - } - } - - // Read EXR data - EXRImage exr_image; - InitEXRImage(&exr_image); - status = LoadEXRImageFromMemory(&exr_image, &exr_header, buffer, size, &error); - if (status != TINYEXR_SUCCESS) - { - std::string error_string(error); - FreeEXRErrorMessage(error); - FreeEXRHeader(&exr_header); - delete[] buffer; - throw std::runtime_error("TinyEXR load error (" + std::to_string(status) + "): " + error_string); - } - - // Free file buffer - delete[] buffer; - - // Create image - image = new ::image(); - image->format(sizeof(float), exr_image.num_channels); - image->resize(static_cast(exr_image.width), static_cast(exr_image.height)); - - // Fill image pixels - float* component = static_cast(image->data()); - for (int y = exr_image.height - 1; y >= 0; --y) - { - int row_offset = y * exr_image.width; - - for (int x = 0; x < exr_image.width; ++x) - { - int pixel_index = row_offset + x; - - for (int c = exr_image.num_channels - 1; c >= 0; --c) - { - *(component++) = reinterpret_cast(exr_image.images)[c][pixel_index]; - } - } - } - - // Free EXR data - FreeEXRImage(&exr_image); - FreeEXRHeader(&exr_header); - } - else - { - // Load all other formats with stb_image - - // Set vertical flip on load in order to upload pixels correctly to OpenGL - stbi_set_flip_vertically_on_load(true); - - // Load image data - void* pixels = nullptr; - int width = 0; - int height = 0; - int channels = 0; - pixels = stbi_load_from_memory(buffer, size, &width, &height, &channels, 0); - - // Free file buffer - delete[] buffer; - - // Check if image was loaded - if (!pixels) - { - throw std::runtime_error("STBI failed to load image from memory."); - } - - // Create image - std::size_t component_size = sizeof(unsigned char); - image = new ::image(); - image->format(component_size, channels); - image->resize(static_cast(width), static_cast(height)); - std::memcpy(image->data(), pixels, image->get_size()); - - // Free loaded image data - stbi_image_free(pixels); - } - - return image; -} - diff --git a/src/engine/resources/image.cpp b/src/engine/resources/image.cpp deleted file mode 100644 index 8b660ec..0000000 --- a/src/engine/resources/image.cpp +++ /dev/null @@ -1,141 +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 . - */ - -#include -#include -#include - -image::image(const image& source) -{ - *this = source; -} - -image::image(): - width(0), - height(0), - component_size(0), - channel_count(0), - pixel_size(0), - pixels(nullptr), - size(0) -{} - -image::~image() -{ - free_pixels(); -} - -image& image::operator=(const image& source) -{ - format(source.component_size, source.channel_count); - resize(source.width, source.height); - std::memcpy(pixels, source.pixels, size); - - return *this; -} - -bool image::compatible(const image& other) const -{ - return (other.component_size == component_size && other.channel_count == channel_count); -} - -void image::copy(const image& source, unsigned int w, unsigned int h, unsigned int from_x, int unsigned from_y, unsigned int to_x, unsigned int to_y) -{ - if (!compatible(source)) - { - throw std::runtime_error("Cannot copy image with mismatched format"); - } - - const unsigned char* from_pixels = static_cast(source.pixels); - unsigned char* to_pixels = static_cast(pixels); - - for (unsigned int i = 0; i < h; ++i) - { - // Calculate vertical pixel offset - unsigned int from_i = from_y + i; - unsigned int to_i = to_y + i; - - // Bounds check - if (from_i >= source.height || to_i >= height) - break; - - for (unsigned int j = 0; j < w; ++j) - { - // Calculate horizontal pixel offsets - unsigned int from_j = from_x + j; - unsigned int to_j = to_x + j; - - // Bounds check - if (from_j >= source.width || to_j >= width) - continue; - - // Calculate pixel data offset (in bytes) - std::size_t from_offset = (from_i * source.width + from_j) * pixel_size; - std::size_t to_offset = (to_i * width + to_j) * pixel_size; - - // Copy single pixel - std::memcpy(to_pixels + to_offset, from_pixels + from_offset, pixel_size); - } - } -} - -void image::format(std::size_t component_size, std::size_t channel_count) -{ - if (this->component_size == component_size && this->channel_count == channel_count) - return; - - free_pixels(); - this->component_size = component_size; - this->channel_count = channel_count; - pixel_size = component_size * channel_count; - size = width * height * pixel_size; - allocate_pixels(); -} - -void image::resize(unsigned int width, unsigned int height) -{ - if (this->width == width && this->height == height) - { - return; - } - - free_pixels(); - this->width = width; - this->height = height; - size = width * height * pixel_size; - allocate_pixels(); -} - -void image::allocate_pixels() -{ - if (size != 0) - { - pixels = new unsigned char[size]; - } -} - -void image::free_pixels() -{ - if (pixels != nullptr) - { - delete[] reinterpret_cast(pixels); - pixels = nullptr; - size = 0; - } -} diff --git a/src/engine/resources/json-loader.cpp b/src/engine/resources/json-loader.cpp deleted file mode 100644 index 2f09d44..0000000 --- a/src/engine/resources/json-loader.cpp +++ /dev/null @@ -1,38 +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 . - */ - -#include -#include -#include -#include - -template <> -json* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - std::string buffer; - buffer.resize(size); - PHYSFS_readBytes(file, &buffer[0], size); - - // Parse json from file buffer - json* data = new json(json::parse(buffer, nullptr, true, true)); - - return data; -} diff --git a/src/engine/resources/material-loader.cpp b/src/engine/resources/material-loader.cpp deleted file mode 100644 index 93f1505..0000000 --- a/src/engine/resources/material-loader.cpp +++ /dev/null @@ -1,457 +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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -template -static bool read_value(T* value, const nlohmann::json& json, const std::string& name) -{ - if (auto element = json.find(name); element != json.end()) - { - *value = element.value().get(); - return true; - } - - return false; -} - -static bool load_texture_1d_property(resource_manager* resource_manager, render::material* material, const std::string& name, const nlohmann::json& json) -{ - // If JSON element is an array - if (json.is_array()) - { - // Determine size of the array - std::size_t array_size = json.size(); - - // Create property - render::material_property* property = material->add_property(name, array_size); - - // Load textures - std::size_t i = 0; - for (const auto& element: json) - { - std::string filename = element.get(); - const gl::texture_1d* texture = resource_manager->load(filename); - property->set_value(i++, texture); - } - } - else - { - // Create property - render::material_property* property = material->add_property(name); - - // Load texture - std::string filename = json.get(); - const gl::texture_1d* texture = resource_manager->load(filename); - property->set_value(texture); - } - - return true; -} - -static bool load_texture_2d_property(resource_manager* resource_manager, render::material* material, const std::string& name, const nlohmann::json& json) -{ - // If JSON element is an array - if (json.is_array()) - { - // Determine size of the array - std::size_t array_size = json.size(); - - // Create property - render::material_property* property = material->add_property(name, array_size); - - // Load textures - std::size_t i = 0; - for (const auto& element: json) - { - std::string filename = element.get(); - const gl::texture_2d* texture = resource_manager->load(filename); - property->set_value(i++, texture); - } - } - else - { - // Create property - render::material_property* property = material->add_property(name); - - // Load texture - std::string filename = json.get(); - const gl::texture_2d* texture = resource_manager->load(filename); - property->set_value(texture); - } - - return true; -} - -static bool load_texture_cube_property(resource_manager* resource_manager, render::material* material, const std::string& name, const nlohmann::json& json) -{ - return false; -} - -template -static bool load_scalar_property(render::material* material, const std::string& name, const nlohmann::json& json) -{ - // If JSON element is an array - if (json.is_array()) - { - // Determine size of the array - std::size_t array_size = json.size(); - - // Create property - render::material_property* property = material->add_property(name, array_size); - - // Set property values - std::size_t i = 0; - for (const auto& element: json) - property->set_value(i++, element.get()); - } - else - { - // Create property - render::material_property* property = material->add_property(name); - - // Set property value - property->set_value(json.get()); - } - - return true; -} - -template -static bool load_vector_property(render::material* material, const std::string& name, std::size_t vector_size, const nlohmann::json& json) -{ - // If JSON element is an array of arrays - if (json.is_array() && json.begin().value().is_array()) - { - // Determine size of the array - std::size_t array_size = json.size(); - - // Create property - render::material_property* property = material->add_property(name, array_size); - - // For each vector in the array - std::size_t i = 0; - for (const auto& vector_element: json) - { - // Read vector elements - T value; - std::size_t j = 0; - for (const auto& value_element: vector_element) - value[j++] = value_element.get(); - - // Set property values - property->set_value(i++, value); - } - } - else - { - // Create property - render::material_property* property = material->add_property(name); - - // Read vector elements - T value; - std::size_t i = 0; - for (const auto& value_element: json) - value[i++] = value_element.get(); - - // Set property values - property->set_value(value); - } - - return true; -} - -template -static bool load_matrix_property(render::material* material, const std::string& name, std::size_t column_count, std::size_t row_count, const nlohmann::json& json) -{ - // If JSON element is an array of arrays of arrays - if (json.is_array() && json.begin().value().is_array()) - { - if (json.begin().value().begin().value().is_array()) - { - // Determine size of the array - std::size_t array_size = json.size(); - - // Create property - render::material_property* property = material->add_property(name, array_size); - - // For each matrix in the array - std::size_t i = 0; - for (const auto& matrix_element: json) - { - // Read vector elements - T value; - std::size_t j = 0; - for (const auto& column_element: matrix_element) - { - std::size_t k = 0; - for (const auto& row_element: column_element) - { - value[j][k] = row_element.get(); - ++k; - } - - ++j; - } - - // Set property values - property->set_value(i, value); - - ++i; - } - - return true; - } - else - { - // Create property - render::material_property* property = material->add_property(name); - - // Read matrix elements - T value; - std::size_t i = 0; - for (const auto& column_element: json) - { - std::size_t j = 0; - for (const auto& row_element: column_element) - { - value[i][j] = row_element.get(); - ++j; - } - - ++i; - } - - // Set property values - property->set_value(value); - - return true; - } - } - - return false; -} - -template <> -render::material* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - std::string buffer; - buffer.resize(size); - PHYSFS_readBytes(file, &buffer[0], size); - - // Parse json from file buffer - nlohmann::json json = nlohmann::json::parse(buffer, nullptr, true, true); - - // Allocate material - render::material* material = new render::material(); - - // Read shader filename - std::string shader_filename; - if (read_value(&shader_filename, json, "shader")) - { - // Load shader program - gl::shader_program* program = resource_manager->load(shader_filename); - material->set_shader_program(program); - } - - // Init material flags - std::uint32_t flags = 0; - - // Read blend mode - std::string blend_mode; - read_value(&blend_mode, json, "blend_mode"); - 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 == "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; - read_value(&depth_mode, json, "depth_mode"); - if (depth_mode == "in_front") - flags |= MATERIAL_FLAG_X_RAY; - - // Read decal mode - std::string decal_mode; - read_value(&decal_mode, json, "decal_mode"); - if (decal_mode == "decal") - flags |= MATERIAL_FLAG_DECAL; - else if (decal_mode == "surface") - flags |= MATERIAL_FLAG_DECAL_SURFACE; - - // Set material flags - material->set_flags(flags); - - // Read material properties - if (auto properties_element = json.find("properties"); properties_element != json.end()) - { - for (const auto& property_element: properties_element.value()) - { - // Read property name - std::string name; - if (!read_value(&name, property_element, "name")) - // Ignore nameless properties - continue; - - // Read property type - std::string type; - if (!read_value(&type, property_element, "type")) - // Ignore typeless properties - continue; - - // Find value element - auto value_element = property_element.find("value"); - if (value_element == property_element.end()) - // Ignore valueless properties - continue; - - if (type == "texture_1d") - { - load_texture_1d_property(resource_manager, material, name, value_element.value()); - } - else if (type == "texture_2d") - { - load_texture_2d_property(resource_manager, material, name, value_element.value()); - } - else if (type == "texture_cube") - { - load_texture_cube_property(resource_manager, material, name, value_element.value()); - } - // If property type is a matrix - else if (type[type.size() - 2] == 'x' && - std::isdigit(type[type.size() - 3]) && - std::isdigit(type.back())) - { - std::size_t columns = std::stoul(type.substr(type.size() - 3, 1)); - std::size_t rows = std::stoul(type.substr(type.size() - 1, 1)); - - if (type.find("float") != std::string::npos) - { - if (size == 2) - load_matrix_property(material, name, columns, rows, value_element.value()); - else if (size == 3) - load_matrix_property(material, name, columns, rows, value_element.value()); - else if (size == 4) - load_matrix_property(material, name, columns, rows, value_element.value()); - } - } - // If property type is a vector - else if (std::isdigit(type.back())) - { - std::size_t size = std::stoul(type.substr(type.size() - 1, 1)); - - if (type.find("float") != std::string::npos) - { - if (size == 2) - load_vector_property(material, name, size, value_element.value()); - else if (size == 3) - load_vector_property(material, name, size, value_element.value()); - else if (size == 4) - load_vector_property(material, name, size, value_element.value()); - } - else if (type.find("uint") != std::string::npos) - { - if (size == 2) - load_vector_property(material, name, size, value_element.value()); - else if (size == 3) - load_vector_property(material, name, size, value_element.value()); - else if (size == 4) - load_vector_property(material, name, size, value_element.value()); - } - else if (type.find("int") != std::string::npos) - { - if (size == 2) - load_vector_property(material, name, size, value_element.value()); - else if (size == 3) - load_vector_property(material, name, size, value_element.value()); - else if (size == 4) - load_vector_property(material, name, size, value_element.value()); - } - else if (type.find("bool") != std::string::npos) - { - if (size == 2) - load_vector_property(material, name, size, value_element.value()); - else if (size == 3) - load_vector_property(material, name, size, value_element.value()); - else if (size == 4) - load_vector_property(material, name, size, value_element.value()); - } - } - // If property type is a scalar - else - { - if (type.find("float") != std::string::npos) - load_scalar_property(material, name, value_element.value()); - else if (type.find("uint") != std::string::npos) - load_scalar_property(material, name, value_element.value()); - else if (type.find("int") != std::string::npos) - load_scalar_property(material, name, value_element.value()); - else if (type.find("bool") != std::string::npos) - load_scalar_property(material, name, value_element.value()); - } - } - } - - // Update material tweens - material->update_tweens(); - - return material; -} diff --git a/src/engine/resources/mesh-loader.cpp b/src/engine/resources/mesh-loader.cpp deleted file mode 100644 index f31952d..0000000 --- a/src/engine/resources/mesh-loader.cpp +++ /dev/null @@ -1,91 +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 . - */ - -#include -#include -#include -#include -#include -#include -#include - -template <> -geom::mesh* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - std::string line; - std::vector vertices; - std::vector> triangles; - - while (!PHYSFS_eof(file)) - { - // Read line - physfs_getline(file, line); - - // Tokenize line - std::vector tokens; - std::string token; - std::istringstream linestream(line); - while (linestream >> token) - tokens.push_back(token); - - // Skip empty lines and comments - if (tokens.empty() || tokens[0][0] == '#') - continue; - - if (tokens[0] == "v") - { - if (tokens.size() != 4) - { - std::stringstream stream; - stream << "resource_loader::load(): Invalid line \"" << line << "\"" << std::endl; - throw std::runtime_error(stream.str()); - } - - float3 vertex; - - std::stringstream(tokens[1]) >> vertex[0]; - std::stringstream(tokens[2]) >> vertex[1]; - std::stringstream(tokens[3]) >> vertex[2]; - - vertices.push_back(vertex); - } - else if (tokens[0] == "f") - { - if (tokens.size() != 4) - { - std::stringstream stream; - stream << "resource_loader::load(): Invalid line \"" << line << "\"" << std::endl; - throw std::runtime_error(stream.str()); - - } - - std::uint_fast32_t a, b, c; - std::stringstream(tokens[1]) >> a; - std::stringstream(tokens[2]) >> b; - std::stringstream(tokens[3]) >> c; - triangles.push_back({a - 1, b - 1, c - 1}); - } - } - - geom::mesh* mesh = new geom::mesh(); - geom::create_triangle_mesh(*mesh, vertices, triangles); - - return mesh; -} - diff --git a/src/engine/resources/model-loader.cpp b/src/engine/resources/model-loader.cpp deleted file mode 100644 index 58a6034..0000000 --- a/src/engine/resources/model-loader.cpp +++ /dev/null @@ -1,334 +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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -inline constexpr std::uint16_t vertex_attribute_position = 0b0000000000000001; -inline constexpr std::uint16_t vertex_attribute_uv = 0b0000000000000010; -inline constexpr std::uint16_t vertex_attribute_normal = 0b0000000000000100; -inline constexpr std::uint16_t vertex_attribute_tangent = 0b0000000000001000; -inline constexpr std::uint16_t vertex_attribute_color = 0b0000000000010000; -inline constexpr std::uint16_t vertex_attribute_bone = 0b0000000000100000; -inline constexpr std::uint16_t vertex_attribute_barycentric = 0b0000000001000000; -inline constexpr std::uint16_t vertex_attribute_morph_target = 0b0000000010000000; -inline constexpr std::uint16_t vertex_attribute_index = 0b0000000100000000; - -template <> -render::model* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - deserialize_context ctx(file); - - // Read vertex format - std::uint16_t vertex_format_flags = 0; - ctx.read16(reinterpret_cast(&vertex_format_flags), 1); - - // Read bone per vertex (if any) - std::uint8_t bones_per_vertex = 0; - if (vertex_format_flags & vertex_attribute_bone) - { - ctx.read8(reinterpret_cast(&bones_per_vertex), 1); - } - - // Read vertex count - std::uint32_t vertex_count = 0; - ctx.read32(reinterpret_cast(&vertex_count), 1); - - // Determine vertex size - std::size_t vertex_size = 0; - if (vertex_format_flags & vertex_attribute_position) - { - vertex_size += sizeof(float) * 3; - } - if (vertex_format_flags & vertex_attribute_uv) - { - vertex_size += sizeof(float) * 2; - } - if (vertex_format_flags & vertex_attribute_normal) - { - vertex_size += sizeof(float) * 3; - } - if (vertex_format_flags & vertex_attribute_tangent) - { - vertex_size += sizeof(float) * 4; - } - if (vertex_format_flags & vertex_attribute_color) - { - vertex_size += sizeof(float) * 4; - } - if (vertex_format_flags & vertex_attribute_bone) - { - vertex_size += sizeof(std::uint32_t) * bones_per_vertex; - vertex_size += sizeof(float) * bones_per_vertex; - } - if (vertex_format_flags & vertex_attribute_barycentric) - { - vertex_size += sizeof(float) * 3; - } - if (vertex_format_flags & vertex_attribute_morph_target) - { - vertex_size += sizeof(float) * 3; - } - - // Allocate vertex data - std::byte* vertex_data = new std::byte[vertex_count * vertex_size]; - - // Read vertices - if constexpr (std::endian::native == std::endian::little) - { - ctx.read8(vertex_data, vertex_count * vertex_size); - } - else - { - std::byte* vertex_data_offset = vertex_data; - for (std::size_t i = 0; i < vertex_count; ++i) - { - if (vertex_format_flags & vertex_attribute_position) - { - ctx.read32(vertex_data_offset, 3); - vertex_data_offset += sizeof(float) * 3; - } - if (vertex_format_flags & vertex_attribute_uv) - { - ctx.read32(vertex_data_offset, 2); - vertex_data_offset += sizeof(float) * 2; - } - if (vertex_format_flags & vertex_attribute_normal) - { - ctx.read32(vertex_data_offset, 3); - vertex_data_offset += sizeof(float) * 3; - } - if (vertex_format_flags & vertex_attribute_tangent) - { - ctx.read32(vertex_data_offset, 4); - vertex_data_offset += sizeof(float) * 4; - } - if (vertex_format_flags & vertex_attribute_color) - { - ctx.read32(vertex_data_offset, 4); - vertex_data_offset += sizeof(float) * 4; - } - if (vertex_format_flags & vertex_attribute_bone) - { - ctx.read32(vertex_data_offset, bones_per_vertex); - ctx.read32(vertex_data_offset, bones_per_vertex); - - vertex_data_offset += sizeof(std::uint32_t) * bones_per_vertex; - vertex_data_offset += sizeof(float) * bones_per_vertex; - } - if (vertex_format_flags & vertex_attribute_barycentric) - { - ctx.read32(vertex_data_offset, 3); - vertex_data_offset += sizeof(float) * 3; - } - if (vertex_format_flags & vertex_attribute_morph_target) - { - ctx.read32(vertex_data_offset, 3); - vertex_data_offset += sizeof(float) * 3; - } - } - } - - // Read geometry bounds - geom::aabb bounds; - ctx.read32(reinterpret_cast(bounds.min_point.data()), 3); - ctx.read32(reinterpret_cast(bounds.max_point.data()), 3); - - // Allocate a model - render::model* model = new render::model(); - - // Set the model bounds - model->set_bounds(bounds); - - // Resize model VBO and upload vertex data - gl::vertex_buffer* vbo = model->get_vertex_buffer(); - vbo->resize(vertex_count * vertex_size, vertex_data); - - // Free vertex data - delete[] vertex_data; - - // Bind vertex attributes to VAO - gl::vertex_array* vao = model->get_vertex_array(); - gl::vertex_attribute attribute; - attribute.buffer = vbo; - attribute.offset = 0; - attribute.stride = vertex_size; - if (vertex_format_flags & vertex_attribute_position) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 3; - vao->bind(render::vertex_attribute::position, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_uv) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 2; - vao->bind(render::vertex_attribute::uv, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_normal) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 3; - vao->bind(render::vertex_attribute::normal, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_tangent) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 4; - vao->bind(render::vertex_attribute::tangent, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_color) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 4; - vao->bind(render::vertex_attribute::color, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_bone) - { - attribute.type = gl::vertex_attribute_type::uint_16; - attribute.components = bones_per_vertex; - vao->bind(render::vertex_attribute::bone_index, attribute); - attribute.offset += sizeof(std::uint32_t) * attribute.components; - - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = bones_per_vertex; - vao->bind(render::vertex_attribute::bone_weight, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_barycentric) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 3; - vao->bind(render::vertex_attribute::barycentric, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_morph_target) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 3; - vao->bind(render::vertex_attribute::target, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - - // Read material count - std::uint16_t material_count = 0; - ctx.read16(reinterpret_cast(&material_count), 1); - - // Read materials - for (std::uint16_t i = 0; i < material_count; ++i) - { - // Read material name length - std::uint8_t material_name_length = 0; - ctx.read8(reinterpret_cast(&material_name_length), 1); - - // Read material name - std::string material_name(static_cast(material_name_length), '\0'); - ctx.read8(reinterpret_cast(material_name.data()), material_name_length); - - // Read offset to index of first vertex - std::uint32_t material_vertex_offset = 0; - ctx.read32(reinterpret_cast(&material_vertex_offset), 1); - - // Read vertex count - std::uint32_t material_vertex_count = 0; - ctx.read32(reinterpret_cast(&material_vertex_count), 1); - - // Slugify material filename - std::string material_filename = material_name + ".mtl"; - std::replace(material_filename.begin(), material_filename.end(), '_', '-'); - - // Load material from file - render::material* material = resource_manager->load(material_filename); - - // Create model material group - render::model_group* material_group = model->add_group(material_name); - material_group->set_drawing_mode(gl::drawing_mode::triangles); - material_group->set_start_index(material_vertex_offset); - material_group->set_index_count(material_vertex_count); - material_group->set_material(material); - } - - // Read skeleton - if (vertex_format_flags & vertex_attribute_bone) - { - ::skeleton& skeleton = model->get_skeleton(); - pose& bind_pose = skeleton.bind_pose; - - // Read bone count - std::uint16_t bone_count = 0; - ctx.read16(reinterpret_cast(&bone_count), 1); - - // Read bones - for (std::uint16_t i = 0; i < bone_count; ++i) - { - // Read bone name length - std::uint8_t bone_name_length = 0; - ctx.read8(reinterpret_cast(&bone_name_length), 1); - - // Read bone name - std::string bone_name(static_cast(bone_name_length), '\0'); - ctx.read8(reinterpret_cast(bone_name.data()), bone_name_length); - - // Read parent bone index - std::uint16_t parent_bone_index = i; - ctx.read16(reinterpret_cast(&parent_bone_index), 1); - - // Construct bone identifier - ::bone bone = make_bone(i, parent_bone_index); - - // Add bone to bone map - skeleton.bone_map[bone_name] = bone; - - // Get reference to the bone's bind pose transform - auto& bone_transform = bind_pose[bone]; - - // Read bone translation - ctx.read32(reinterpret_cast(bone_transform.translation.data()), 3); - - // Read bone rotation - ctx.read32(reinterpret_cast(&bone_transform.rotation.r), 1); - ctx.read32(reinterpret_cast(bone_transform.rotation.i.data()), 3); - - // Set bone scale - bone_transform.scale = {1, 1, 1}; - - // Read bone length - float bone_length = 0.0f; - ctx.read32(reinterpret_cast(&bone_length), 1); - } - - // Calculate inverse skeleton-space bind pose - ::concatenate(skeleton.bind_pose, skeleton.inverse_bind_pose); - ::inverse(skeleton.inverse_bind_pose, skeleton.inverse_bind_pose); - } - - return model; -} diff --git a/src/engine/resources/physfs/physfs-deserialize-context.cpp b/src/engine/resources/physfs/physfs-deserialize-context.cpp new file mode 100644 index 0000000..1189873 --- /dev/null +++ b/src/engine/resources/physfs/physfs-deserialize-context.cpp @@ -0,0 +1,270 @@ +/* + * 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 . + */ + +#include +#include + +physfs_deserialize_context::physfs_deserialize_context(const std::filesystem::path& path) +{ + // Open file for reading using PhysicsFS + file = PHYSFS_openRead(path.string().c_str()); + if (!file) + { + throw deserialize_error(PHYSFS_getLastError()); + } + + // Store file path + m_path = path; + + // Set EOF and error flags if file not open. + m_eof = !file; + m_error = !file; +} + +physfs_deserialize_context::~physfs_deserialize_context() +{ + if (file) + { + PHYSFS_close(file); + } +} + +void physfs_deserialize_context::open(const std::filesystem::path& path) +{ + // Close file, if open + if (file) + { + PHYSFS_close(file); + } + + // Open file for reading using PhysicsFS + file = PHYSFS_openRead(path.string().c_str()); + if (!file) + { + throw deserialize_error(PHYSFS_getLastError()); + } + + // Store file path + m_path = path; + + // Set EOF and error flags if file not open. + m_eof = !file; + m_error = !file; +} + +void physfs_deserialize_context::close() noexcept +{ + if (file) + { + m_error = !PHYSFS_close(file); + file = nullptr; + m_path.clear(); + m_eof = true; + } +} + +bool physfs_deserialize_context::is_open() const noexcept +{ + return file; +} + +const std::filesystem::path& physfs_deserialize_context::path() const noexcept +{ + return m_path; +} + +bool physfs_deserialize_context::error() const noexcept +{ + return m_error; +} + +bool physfs_deserialize_context::eof() const noexcept +{ + return m_eof; +} + +std::size_t physfs_deserialize_context::size() const noexcept +{ + PHYSFS_sint64 length = PHYSFS_fileLength(file); + if (length >= 0) + { + return static_cast(length); + } + + return 0; +}; + +std::size_t physfs_deserialize_context::tell() const +{ + PHYSFS_sint64 offset = PHYSFS_fileLength(file); + if (offset < 0) + { + //m_error = true; + throw deserialize_error(PHYSFS_getLastError()); + } + + return static_cast(offset); +} + +void physfs_deserialize_context::seek(std::size_t offset) +{ + if (!PHYSFS_seek(file, static_cast(offset))) + { + m_error = true; + throw deserialize_error(PHYSFS_getLastError()); + } + + m_eof = (PHYSFS_eof(file) != 0); +} + +std::size_t physfs_deserialize_context::read8(std::byte* data, std::size_t count) +{ + const PHYSFS_sint64 status = PHYSFS_readBytes(file, data, count); + + if (status != count) + { + if (status < 0 || !PHYSFS_eof(file)) + { + m_error = true; + throw deserialize_error(PHYSFS_getLastError()); + } + + m_eof = true; + + return static_cast(status); + } + else + { + return count; + } +} + +std::size_t physfs_deserialize_context::read16_le(std::byte* data, std::size_t count) +{ + PHYSFS_uint16* data16 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_readULE16(file, data16)) + { + m_error = true; + m_eof = (PHYSFS_eof(file) != 0); + throw deserialize_error(PHYSFS_getLastError()); + } + + ++data16; + } + + return count; +} + +std::size_t physfs_deserialize_context::read16_be(std::byte* data, std::size_t count) +{ + PHYSFS_uint16* data16 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_readUBE16(file, data16)) + { + m_error = true; + m_eof = (PHYSFS_eof(file) != 0); + throw deserialize_error(PHYSFS_getLastError()); + } + + ++data16; + } + + return count; +} + +std::size_t physfs_deserialize_context::read32_le(std::byte* data, std::size_t count) +{ + PHYSFS_uint32* data32 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_readULE32(file, data32)) + { + m_error = true; + m_eof = (PHYSFS_eof(file) != 0); + throw deserialize_error(PHYSFS_getLastError()); + } + + ++data32; + } + + return count; +} + +std::size_t physfs_deserialize_context::read32_be(std::byte* data, std::size_t count) +{ + PHYSFS_uint32* data32 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_readUBE32(file, data32)) + { + m_error = true; + m_eof = (PHYSFS_eof(file) != 0); + throw deserialize_error(PHYSFS_getLastError()); + } + + ++data32; + } + + return count; +} + +std::size_t physfs_deserialize_context::read64_le(std::byte* data, std::size_t count) +{ + PHYSFS_uint64* data64 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_readULE64(file, data64)) + { + m_error = true; + m_eof = (PHYSFS_eof(file) != 0); + throw deserialize_error(PHYSFS_getLastError()); + } + + ++data64; + } + + return count; +} + +std::size_t physfs_deserialize_context::read64_be(std::byte* data, std::size_t count) +{ + PHYSFS_uint64* data64 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_readUBE64(file, data64)) + { + m_error = true; + m_eof = (PHYSFS_eof(file) != 0); + throw deserialize_error(PHYSFS_getLastError()); + } + + ++data64; + } + + return count; +} diff --git a/src/engine/resources/physfs/physfs-deserialize-context.hpp b/src/engine/resources/physfs/physfs-deserialize-context.hpp new file mode 100644 index 0000000..43d86bf --- /dev/null +++ b/src/engine/resources/physfs/physfs-deserialize-context.hpp @@ -0,0 +1,92 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_RESOURCES_PHYSFS_DESERIALIZE_CONTEXT_HPP +#define ANTKEEPER_RESOURCES_PHYSFS_DESERIALIZE_CONTEXT_HPP + +#include +#include +#include + +/** + * Deserialize context implementation using PhysicsFS. + */ +class physfs_deserialize_context: public deserialize_context +{ +public: + /** + * Constructs a PhysicsFS deserialize context, opening a file using PhysicsFS and associating it with this deserialize context. + * + * @param path Path to a file to open. + * + * @throw deserialize_error File open error. + */ + physfs_deserialize_context(const std::filesystem::path& path) noexcept(false); + + /** + * Constructs a PhysicsFS deserialize context. + */ + physfs_deserialize_context() noexcept = default; + + /** + * Destructs a PhysicsFS deserialize context, internally closing a file using PhysicsFS. + */ + virtual ~physfs_deserialize_context(); + + /** + * Opens a file using PhysicsFS and associates it with the deserialize context. + * + * @param path Path to a file to open. + * + * @throw deserialize_error File open error. + */ + void open(const std::filesystem::path& path) noexcept(false); + + /** + * Closes the associated file using PhysicsFS. + */ + void close() noexcept; + + /** + * Returns `true` if the PhysicsFS file associated with this deserialize context is open, `false` otherwise. + */ + [[nodiscard]] bool is_open() const noexcept; + + [[nodiscard]] const std::filesystem::path& path() const noexcept override; + [[nodiscard]] bool error() const noexcept override; + [[nodiscard]] bool eof() const noexcept override; + [[nodiscard]] std::size_t size() const noexcept override; + [[nodiscard]] std::size_t tell() const override; + void seek(std::size_t offset) override; + std::size_t read8(std::byte* data, std::size_t count) noexcept(false) override; + std::size_t read16_le(std::byte* data, std::size_t count) noexcept(false) override; + std::size_t read16_be(std::byte* data, std::size_t count) noexcept(false) override; + std::size_t read32_le(std::byte* data, std::size_t count) noexcept(false) override; + std::size_t read32_be(std::byte* data, std::size_t count) noexcept(false) override; + std::size_t read64_le(std::byte* data, std::size_t count) noexcept(false) override; + std::size_t read64_be(std::byte* data, std::size_t count) noexcept(false) override; + +private: + PHYSFS_File* file{nullptr}; + std::filesystem::path m_path; + bool m_eof{true}; + bool m_error{false}; +}; + +#endif // ANTKEEPER_RESOURCES_PHYSFS_DESERIALIZE_CONTEXT_HPP diff --git a/src/engine/resources/serialize-context.cpp b/src/engine/resources/physfs/physfs-serialize-context.cpp similarity index 59% rename from src/engine/resources/serialize-context.cpp rename to src/engine/resources/physfs/physfs-serialize-context.cpp index 13c731e..51d745d 100644 --- a/src/engine/resources/serialize-context.cpp +++ b/src/engine/resources/physfs/physfs-serialize-context.cpp @@ -17,18 +17,83 @@ * along with Antkeeper source code. If not, see . */ -#include +#include #include -#include -serialize_context::serialize_context(void* handle): - handle(handle), - m_error(false) -{} +physfs_serialize_context::physfs_serialize_context(const std::filesystem::path& path) +{ + // Open file for writing using PhysicsFS + file = PHYSFS_openWrite(path.string().c_str()); + if (!file) + { + throw serialize_error(PHYSFS_getLastError()); + } + + // Store file path + m_path = path; + + // Set error flag if file not open. + m_error = !file; +} + +physfs_serialize_context::~physfs_serialize_context() +{ + if (file) + { + PHYSFS_close(file); + } +} + +void physfs_serialize_context::open(const std::filesystem::path& path) +{ + // Close file, if open + if (file) + { + PHYSFS_close(file); + } + + // Open file for writing using PhysicsFS + file = PHYSFS_openWrite(path.string().c_str()); + if (!file) + { + throw serialize_error(PHYSFS_getLastError()); + } + + // Store file path + m_path = path; + + // Set error flag if file not open. + m_error = !file; +} + +void physfs_serialize_context::close() noexcept +{ + if (file) + { + m_error = !PHYSFS_close(file); + file = nullptr; + m_path.clear(); + } +} + +bool physfs_serialize_context::is_open() const noexcept +{ + return file; +} + +const std::filesystem::path& physfs_serialize_context::path() const noexcept +{ + return m_path; +} + +bool physfs_serialize_context::error() const noexcept +{ + return m_error; +} -std::size_t serialize_context::write8(const std::byte* data, std::size_t count) +std::size_t physfs_serialize_context::write8(const std::byte* data, std::size_t count) { - const PHYSFS_sint64 status = PHYSFS_writeBytes(reinterpret_cast(handle), data, count); + const PHYSFS_sint64 status = PHYSFS_writeBytes(file, data, count); if (status < 0) { @@ -47,9 +112,8 @@ std::size_t serialize_context::write8(const std::byte* data, std::size_t count) return count; } -std::size_t serialize_context::write16_le(const std::byte* data, std::size_t count) +std::size_t physfs_serialize_context::write16_le(const std::byte* data, std::size_t count) { - PHYSFS_File* file = reinterpret_cast(handle); const PHYSFS_uint16* data16 = reinterpret_cast(data); for (std::size_t i = 0; i < count; ++i) @@ -67,9 +131,8 @@ std::size_t serialize_context::write16_le(const std::byte* data, std::size_t cou return count; } -std::size_t serialize_context::write16_be(const std::byte* data, std::size_t count) +std::size_t physfs_serialize_context::write16_be(const std::byte* data, std::size_t count) { - PHYSFS_File* file = reinterpret_cast(handle); const PHYSFS_uint16* data16 = reinterpret_cast(data); for (std::size_t i = 0; i < count; ++i) @@ -87,9 +150,8 @@ std::size_t serialize_context::write16_be(const std::byte* data, std::size_t cou return count; } -std::size_t serialize_context::write32_le(const std::byte* data, std::size_t count) +std::size_t physfs_serialize_context::write32_le(const std::byte* data, std::size_t count) { - PHYSFS_File* file = reinterpret_cast(handle); const PHYSFS_uint32* data32 = reinterpret_cast(data); for (std::size_t i = 0; i < count; ++i) @@ -107,9 +169,8 @@ std::size_t serialize_context::write32_le(const std::byte* data, std::size_t cou return count; } -std::size_t serialize_context::write32_be(const std::byte* data, std::size_t count) +std::size_t physfs_serialize_context::write32_be(const std::byte* data, std::size_t count) { - PHYSFS_File* file = reinterpret_cast(handle); const PHYSFS_uint32* data32 = reinterpret_cast(data); for (std::size_t i = 0; i < count; ++i) @@ -127,9 +188,8 @@ std::size_t serialize_context::write32_be(const std::byte* data, std::size_t cou return count; } -std::size_t serialize_context::write64_le(const std::byte* data, std::size_t count) +std::size_t physfs_serialize_context::write64_le(const std::byte* data, std::size_t count) { - PHYSFS_File* file = reinterpret_cast(handle); const PHYSFS_uint64* data64 = reinterpret_cast(data); for (std::size_t i = 0; i < count; ++i) @@ -147,9 +207,8 @@ std::size_t serialize_context::write64_le(const std::byte* data, std::size_t cou return count; } -std::size_t serialize_context::write64_be(const std::byte* data, std::size_t count) +std::size_t physfs_serialize_context::write64_be(const std::byte* data, std::size_t count) { - PHYSFS_File* file = reinterpret_cast(handle); const PHYSFS_uint64* data64 = reinterpret_cast(data); for (std::size_t i = 0; i < count; ++i) diff --git a/src/engine/resources/physfs/physfs-serialize-context.hpp b/src/engine/resources/physfs/physfs-serialize-context.hpp new file mode 100644 index 0000000..fcc5a39 --- /dev/null +++ b/src/engine/resources/physfs/physfs-serialize-context.hpp @@ -0,0 +1,87 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_RESOURCES_PHYSFS_SERIALIZE_CONTEXT_HPP +#define ANTKEEPER_RESOURCES_PHYSFS_SERIALIZE_CONTEXT_HPP + +#include +#include +#include + +/** + * Provides access to a serialization state. + */ +class physfs_serialize_context: public serialize_context +{ +public: + /** + * Constructs a PhysicsFS serialize context, opening a file using PhysicsFS and associating it with this serialize context. + * + * @param path Path to a file to open. + * + * @throw serialize_error File open error. + */ + physfs_serialize_context(const std::filesystem::path& path) noexcept(false); + + /** + * Constructs a PhysicsFS serialize context. + */ + physfs_serialize_context() noexcept = default; + + /** + * Destructs a PhysicsFS serialize context, internally closing a file using PhysicsFS. + */ + virtual ~physfs_serialize_context(); + + /** + * Opens a file using PhysicsFS and associates it with the serialize context. + * + * @param path Path to a file to open. + * + * @throw serialize_error File open error. + */ + void open(const std::filesystem::path& path) noexcept(false); + + /** + * Closes the associated file using PhysicsFS. + */ + void close() noexcept; + + /** + * Returns `true` if the PhysicsFS file associated with this serialize context is open, `false` otherwise. + */ + [[nodiscard]] bool is_open() const noexcept; + + [[nodiscard]] const std::filesystem::path& path() const noexcept override; + [[nodiscard]] bool error() const noexcept override; + std::size_t write8(const std::byte* data, std::size_t count) noexcept(false) override; + std::size_t write16_le(const std::byte* data, std::size_t count) noexcept(false) override; + std::size_t write16_be(const std::byte* data, std::size_t count) noexcept(false) override; + std::size_t write32_le(const std::byte* data, std::size_t count) noexcept(false) override; + std::size_t write32_be(const std::byte* data, std::size_t count) noexcept(false) override; + std::size_t write64_le(const std::byte* data, std::size_t count) noexcept(false) override; + std::size_t write64_be(const std::byte* data, std::size_t count) noexcept(false) override; + +private: + PHYSFS_File* file{nullptr}; + std::filesystem::path m_path; + bool m_error{false}; +}; + +#endif // ANTKEEPER_RESOURCES_PHYSFS_SERIALIZE_CONTEXT_HPP diff --git a/src/engine/resources/resource-handle.hpp b/src/engine/resources/resource-handle.hpp deleted file mode 100644 index 1619171..0000000 --- a/src/engine/resources/resource-handle.hpp +++ /dev/null @@ -1,70 +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 . - */ - -#ifndef ANTKEEPER_RESOURCE_HANDLE_HPP -#define ANTKEEPER_RESOURCE_HANDLE_HPP - -#include - -/** - * Base class for resource handles. - */ -class resource_handle_base -{ -public: - /// Creates a resource handle base. - resource_handle_base(); - - /// Destroys a resource handle base. - virtual ~resource_handle_base() = default; - - /// Number of times the handle is referenced. - std::size_t reference_count; -}; - -/** - * Templated resource handle class. - */ -template -class resource_handle: public resource_handle_base -{ -public: - /// Creates a resource handle - resource_handle(); - - /// Destroys a resource handle and deletes its data. - virtual ~resource_handle(); - - /// Pointer to resource data - T* data; -}; - -template -resource_handle::resource_handle(): - data(nullptr) -{} - -template -resource_handle::~resource_handle() -{ - delete data; -} - -#endif // ANTKEEPER_RESOURCE_HANDLE_HPP - diff --git a/src/engine/resources/resource-loader.cpp b/src/engine/resources/resource-loader.cpp deleted file mode 100644 index a4916e9..0000000 --- a/src/engine/resources/resource-loader.cpp +++ /dev/null @@ -1,61 +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 . - */ - -#include -#include -#include - -void physfs_getline(PHYSFS_File* file, std::string& line) -{ - line.clear(); - - for (;;) - { - char c; - const PHYSFS_sint64 status = PHYSFS_readBytes(file, &c, 1); - - if (status == 1) - { - if (c == '\r') - { - continue; - } - else if (c == '\n') - { - break; - } - else - { - line.append(1, c); - } - - } - else - { - if (PHYSFS_eof(file)) - { - break; - } - else - { - throw deserialize_error(PHYSFS_getLastError()); - } - } - } -} diff --git a/src/engine/resources/resource-loader.hpp b/src/engine/resources/resource-loader.hpp index 1774ae2..786178c 100644 --- a/src/engine/resources/resource-loader.hpp +++ b/src/engine/resources/resource-loader.hpp @@ -20,41 +20,29 @@ #ifndef ANTKEEPER_RESOURCES_RESOURCE_LOADER_HPP #define ANTKEEPER_RESOURCES_RESOURCE_LOADER_HPP -#include -#include +#include +#include class resource_manager; -struct PHYSFS_File; /** * Templated resource loader. * - * @tparam Type of resource which this loader handles. + * @tparam T Resource type. */ template class resource_loader { public: /** - * Loads resource data. + * Loads a resource. * - * @param resourceManager Pointer to a resource manager which will manage this resource. - * @param file PhysicsFS file handle. - * @return Pointer to the loaded resource. - */ - static T* load(resource_manager* resourceManager, PHYSFS_File* file, const std::filesystem::path& path); - - /** - * Saves resource data. + * @param ctx Deserialize context. + * @param resource_manager Resource manager to load resource dependencies. * - * @param resourceManager Pointer to a resource manager. - * @param file PhysicsFS file handle. - * @param resource Pointer to the resource data. + * @return Unique pointer to the loaded resource. */ - static void save(resource_manager* resourceManager, PHYSFS_File* file, const std::filesystem::path& path, const T* resource); + [[nodiscard]] static std::unique_ptr load(::resource_manager& resource_manager, deserialize_context& ctx); }; -/// getline function for PhysicsFS file handles -void physfs_getline(PHYSFS_File* file, std::string& line); - #endif // ANTKEEPER_RESOURCES_RESOURCE_LOADER_HPP diff --git a/src/engine/resources/resource-manager.cpp b/src/engine/resources/resource-manager.cpp index 8736a25..ac276df 100644 --- a/src/engine/resources/resource-manager.cpp +++ b/src/engine/resources/resource-manager.cpp @@ -19,6 +19,9 @@ #include #include +#include +#include +#include #include resource_manager::resource_manager() @@ -54,16 +57,6 @@ resource_manager::resource_manager() resource_manager::~resource_manager() { - debug::log::trace("Deleting cached resources..."); - - // Delete cached resources - for (auto it = resource_cache.begin(); it != resource_cache.end(); ++it) - { - delete it->second; - } - - debug::log::trace("Deleted cached resources"); - // Deinit PhysicsFS debug::log::trace("Deinitializing PhysicsFS..."); if (!PHYSFS_deinit()) @@ -78,56 +71,92 @@ resource_manager::~resource_manager() bool resource_manager::mount(const std::filesystem::path& path) { - debug::log::trace("Mounting path \"{}\"...", path.string()); - if (!PHYSFS_mount(path.string().c_str(), nullptr, 1)) + const std::string path_string = path.string(); + + debug::log::trace("Mounting path \"{}\"...", path_string); + + if (!PHYSFS_mount(path_string.c_str(), nullptr, 1)) { - debug::log::error("Failed to mount path \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + debug::log::error("Failed to mount path \"{}\": {}", path_string, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); return false; } - else - { - debug::log::trace("Mounted path \"{}\"", path.string()); - return true; - } + + debug::log::trace("Mounted path \"{}\"", path_string); + + return true; } -void resource_manager::set_write_dir(const std::filesystem::path& path) +bool resource_manager::unmount(const std::filesystem::path& path) { - if (!PHYSFS_setWriteDir(path.string().c_str())) + const std::string path_string = path.string(); + + debug::log::trace("Unmounting path \"{}\"...", path_string); + + if (!PHYSFS_unmount(path_string.c_str())) { - debug::log::error("Failed set write directory to \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + debug::log::error("Failed to unmount path \"{}\": {}", path_string, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return false; } - else + + debug::log::trace("Unmounted path \"{}\"", path_string); + + return true; +} + +bool resource_manager::set_write_path(const std::filesystem::path& path) +{ + const std::string path_string = path.string(); + + if (!PHYSFS_setWriteDir(path_string.c_str())) { - debug::log::trace("Set write directory to \"{}\"", path.string()); + debug::log::error("Failed set write path to \"{}\": {}", path_string, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return false; } + + write_path = path; + + debug::log::trace("Set write path to \"{}\"", path_string); + + return true; } -void resource_manager::unload(const std::filesystem::path& path) +std::shared_ptr resource_manager::fetch(const std::filesystem::path& path) const { - // Check if resource is in the cache - auto it = resource_cache.find(path); - if (it != resource_cache.end()) + if (auto i = resource_cache.find(path); i != resource_cache.end()) { - // Decrement the resource handle reference count - --it->second->reference_count; - - // Free the resource if the resource handle is unreferenced - if (it->second->reference_count <= 0) + if (!i->second.expired()) { - debug::log::trace("Unloading resource \"{}\"...", path.string()); - - delete it->second; - - debug::log::trace("Unloaded resource \"{}\"", path.string()); + return i->second.lock(); + } + else + { + debug::log::trace("Fetched expired resource from cache \"{}\"", path.string()); } - - // Remove resource from the cache - resource_cache.erase(it); } + + return nullptr; } -void resource_manager::include(const std::filesystem::path& search_path) +std::unique_ptr resource_manager::open_read(const std::filesystem::path& path) const { - search_paths.push_back(search_path); + auto ctx = std::make_unique(path); + if (!ctx->is_open()) + { + debug::log::error("Failed to open file \"{}\" for reading: {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return nullptr; + } + + return ctx; +} + +std::unique_ptr resource_manager::open_write(const std::filesystem::path& path) const +{ + auto ctx = std::make_unique(path); + if (!ctx->is_open()) + { + debug::log::error("Failed to open file \"{}\" for writing: {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return nullptr; + } + + return ctx; } diff --git a/src/engine/resources/resource-manager.hpp b/src/engine/resources/resource-manager.hpp index 3aaef76..68f88e3 100644 --- a/src/engine/resources/resource-manager.hpp +++ b/src/engine/resources/resource-manager.hpp @@ -20,191 +20,185 @@ #ifndef ANTKEEPER_RESOURCES_RESOURCE_MANAGER_HPP #define ANTKEEPER_RESOURCES_RESOURCE_MANAGER_HPP -#include -#include #include +#include +#include +#include +#include +#include #include -#include -#include -#include +#include #include -#include +#include /** - * Loads resources. + * Manages the loading, caching, and saving of resources. */ class resource_manager { public: /** - * Creates a resource manager. + * Constructs a resource manager. + * + * @throw std::runtime_error Failed to initialize PhysicsFS. */ resource_manager(); - + /** - * Destroys a resource manager and frees all of its resources. + * Destructs a resource manager. */ ~resource_manager(); + /** + * Adds a directory or archive to the search path. + * + * @param path Path to the directory or archive to mount. + * + * @return `true` if the directory or archive was successfully mounted, `false` otherwise. + */ bool mount(const std::filesystem::path& path); - void set_write_dir(const std::filesystem::path& path); - /** - * Adds a path to be searched when a resource is requested. + * Removes a directory or archive from the search path. + * + * @param path Path to the directory or archive to unmount. * - * @param path Search path. + * @return `true` if the directory or archive was successfully unmounted, `false` otherwise. */ - void include(const std::filesystem::path& path); - + bool unmount(const std::filesystem::path& path); + /** - * Loads the requested resource. If the resource has already been loaded it will be retrieved from the resource cache and its reference count incremented. + * Loads and caches a resource. If the resource has already been loaded the cached resource will be returned. * * @tparam T Resource type. - * @param path Path to the resource, relative to the search paths. - * @return Pointer to the requested resource, or nullptr if the resource could not be found nor loaded. + * + * @param path Path to the resource to load. + * + * @return Pointer to the loaded resource, or `nullptr` if the resource could not be loaded. */ - template - T* load(const std::filesystem::path& path); - + template + std::shared_ptr load(const std::filesystem::path& path); + /** - * Decrements a resource's reference count and unloads the resource if it's unreferenced. + * Saves a resource to a file. * - * @param path Path to the resource, relative to the search paths. + * @tparam T Resource type. + * + * @param resource Resource to save. + * @param path Path to where the resource file should be written. + * + * @return `true` if the resource was successfully saved, `false` otherwise. */ - void unload(const std::filesystem::path& path); - + template + bool save(const T& resource, const std::filesystem::path& path) const; + /** - * Saves the specified resource. + * Sets the path to a directory or archive where files can be written. * - * @tparam T Resource type. - * @param resource Pointer to the resource. - * @param path Path to the resource. + * @param path Path to the directory or archive to which files should be written. + * + * @return `true` if the write path was set successfully, `false` otherwise. + */ + bool set_write_path(const std::filesystem::path& path); + + /** + * Returns the path to the directory or archive to which files are written. */ - template - void save(const T* resource, const std::filesystem::path& path); + [[nodiscard]] inline const std::filesystem::path& get_write_path() const noexcept + { + return write_path; + } private: - std::map resource_cache; - std::list search_paths; + /** + * Fetches a resource from the resource cache. + * + * @param path Path to a resource. + * + * @return Shared pointer to the cached resource, or `nullptr` if the resource was not found or has expired. + */ + [[nodiscard]] std::shared_ptr fetch(const std::filesystem::path& path) const; + + /** + * Constructs a deserialize context from a file path. + * + * @param path Path to the file to open for reading. + * + * @return Unique pointer to a deserialize context, or `nullptr` if the file could not be opened for reading. + */ + [[nodiscard]] std::unique_ptr open_read(const std::filesystem::path& path) const; + + /** + * Constructs a serialize context from a file path. + * + * @param path Path to a file to open for writing. + * + * @return Unique pointer to a serialize context, or `nullptr` if the file could not be opened for writing. + */ + [[nodiscard]] std::unique_ptr open_write(const std::filesystem::path& path) const; + + std::unordered_map> resource_cache; + std::filesystem::path write_path; }; -template -T* resource_manager::load(const std::filesystem::path& path) +template +std::shared_ptr resource_manager::load(const std::filesystem::path& path) { - // Check if resource is in the cache - auto it = resource_cache.find(path); - if (it != resource_cache.end()) + // Fetch cached resource, if any + if (auto resource = fetch(path)) { - //debug::log::trace("Fetched cached resource \"{}\"". path.string()); - - // Resource found - resource_handle* resource = static_cast*>(it->second); - - // Increment resource handle reference count - ++resource->reference_count; - - // Return resource data - return resource->data; + return std::static_pointer_cast(resource); } - debug::log::trace("Loading resource \"{}\"...", path.string()); + const auto path_string = path.string(); - // Resource not cached, look for file in search paths - T* data = nullptr; - bool found = false; - for (const std::filesystem::path& search_path: search_paths) + try { - std::filesystem::path full_path = search_path / path; - - // Check if file exists - if (!PHYSFS_exists(full_path.string().c_str())) - { - continue; - } - - // File found - found = true; + debug::log::trace("Loading resource \"{}\"...", path_string); // Open file for reading - PHYSFS_File* file = PHYSFS_openRead(full_path.string().c_str()); - if (!file) - { - debug::log::error("Failed to load resource \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - break; - } + auto deserialize_ctx = open_read(path); - // Load opened file - try - { - data = resource_loader::load(this, file, full_path); - } - catch (const std::exception& e) - { - debug::log::error("Failed to load resource \"{}\": {}", path.string(), e.what()); - } + // Load and cache resource + std::shared_ptr resource = resource_loader::load(*this, *deserialize_ctx); + resource_cache[path] = resource; - // Close opened file - if (!PHYSFS_close(file)) - { - debug::log::error("Failed to close resource file \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - } + debug::log::trace("Loaded resource \"{}\"", path_string); - break; + return resource; } - - if (!data) + catch (const std::exception& e) { - if (!found) - { - debug::log::error("Failed to load resource \"{}\": file not found", path.string()); - } - - return nullptr; + debug::log::error("Failed to load resource \"{}\": {}", path_string, e.what()); } - - // Create a resource handle for the resource data - resource_handle* resource = new resource_handle(); - resource->data = data; - resource->reference_count = 1; - - // Add resource to the cache - resource_cache[path] = resource; - debug::log::trace("Loaded resource \"{}\"", path.string()); - - return resource->data; + return nullptr; } -template -void resource_manager::save(const T* resource, const std::filesystem::path& path) +template +bool resource_manager::save(const T& resource, const std::filesystem::path& path) const { - debug::log::trace("Saving resource to \"{}\"", path.string()); - - // Open file for writing - PHYSFS_File* file = PHYSFS_openWrite(path.string().c_str()); - if (!file) - { - debug::log::error("Failed to save resource to \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - return; - } + const auto path_string = path.string(); - // Save to opened file try { - resource_loader::save(this, file, path, resource); - debug::log::trace("Saved resource to \"{}\"", path.string()); + debug::log::trace("Saving resource to \"{}\"...", path_string); + + // Open file for writing + auto serialize_ctx = open_write(path); + + serializer().serialize(resource, *serialize_ctx); + + debug::log::trace("Saved resource to \"{}\"", path_string); + + return true; } catch (const std::exception& e) { - debug::log::error("Failed to save resource to \"{}\": {}", e.what()); + debug::log::error("Failed to save resource to \"{}\": {}", path_string, e.what()); } - // Close opened file - if (!PHYSFS_close(file)) - { - debug::log::error("Failed to close file \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - } + return false; } #endif // ANTKEEPER_RESOURCES_RESOURCE_MANAGER_HPP diff --git a/src/engine/resources/serialize-context.hpp b/src/engine/resources/serialize-context.hpp index 1d64bc1..340ebc6 100644 --- a/src/engine/resources/serialize-context.hpp +++ b/src/engine/resources/serialize-context.hpp @@ -22,6 +22,7 @@ #include #include +#include /** * Provides access to a serialization state. @@ -29,6 +30,16 @@ struct serialize_context { public: + /** + * Returns the path associated with this serialize context. + */ + [[nodiscard]] virtual const std::filesystem::path& path() const noexcept = 0; + + /** + * Returns `true` if an error occured during a write operation or initialization, `false` otherwise. + */ + [[nodiscard]] virtual bool error() const noexcept = 0; + /** * Writes 8-bit (byte) data. * @@ -39,7 +50,7 @@ public: * * @throw serialize_error Write error. */ - std::size_t write8(const std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t write8(const std::byte* data, std::size_t count) noexcept(false) = 0; /** * Writes 16-bit (word) little-endian data. @@ -51,7 +62,7 @@ public: * * @throw serialize_error Write error. */ - std::size_t write16_le(const std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t write16_le(const std::byte* data, std::size_t count) noexcept(false) = 0; /** * Writes 16-bit (word) big-endian data. @@ -63,7 +74,7 @@ public: * * @throw serialize_error Write error. */ - std::size_t write16_be(const std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t write16_be(const std::byte* data, std::size_t count) noexcept(false) = 0; /** * Writes 16-bit (word) data. @@ -100,7 +111,7 @@ public: * * @throw serialize_error Write error. */ - std::size_t write32_le(const std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t write32_le(const std::byte* data, std::size_t count) noexcept(false) = 0; /** * Writes 32-bit (double word) big-endian data. @@ -112,7 +123,7 @@ public: * * @throw serialize_error Write error. */ - std::size_t write32_be(const std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t write32_be(const std::byte* data, std::size_t count) noexcept(false) = 0; /** * Writes 32-bit (double word) data. @@ -149,7 +160,7 @@ public: * * @throw serialize_error Write error. */ - std::size_t write64_le(const std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t write64_le(const std::byte* data, std::size_t count) noexcept(false) = 0; /** * Writes 64-bit (quad word) big-endian data. @@ -161,7 +172,7 @@ public: * * @throw serialize_error Write error. */ - std::size_t write64_be(const std::byte* data, std::size_t count) noexcept(false); + virtual std::size_t write64_be(const std::byte* data, std::size_t count) noexcept(false) = 0; /** * Writes 64-bit (quad word) data. @@ -187,23 +198,6 @@ public: return write64_be(data, count); } } - - /** - * Returns `true` if an error occured during a write operation, `false` otherwise. - */ - [[nodiscard]] inline bool error() const noexcept - { - return m_error; - } - -private: - template - friend class resource_loader; - - serialize_context(void* handle); - - void* handle; - bool m_error; }; #endif // ANTKEEPER_RESOURCES_SERIALIZE_CONTEXT_HPP diff --git a/src/engine/resources/shader-loader.cpp b/src/engine/resources/shader-loader.cpp deleted file mode 100644 index b24e9dd..0000000 --- a/src/engine/resources/shader-loader.cpp +++ /dev/null @@ -1,174 +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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * Scans a text file for the presence of a `#pragma once` directive. - * - * @param source Text file to scan. - * - * @return `true` if the file contains a `#pragma once` directive, `false` otherwise. - */ -static bool has_pragma_once(const text_file& source) -{ - for (const auto& line: source) - { - std::istringstream line_stream(line); - std::string token; - - // If line contains a `#pragma once` directive - if (line_stream >> token && token == "#pragma" && - line_stream >> token && token == "once") - { - return true; - } - } - - return false; -} - -/** - * Handles `#pragma include` directives by loading the specified text files and inserting them in place. - */ -static void handle_includes(text_file& source, std::unordered_set& include_once, resource_manager* resource_manager) -{ - // For each line in the source - for (std::size_t i = 0; i < source.size(); ++i) - { - std::string token; - std::istringstream line_stream(source[i]); - - // If line contains a `#pragma include` directive - if (line_stream >> token && token == "#pragma" && - line_stream >> token && token == "include") - { - // If third token is enclosed in quotes or angled brackets - if (line_stream >> token && token.size() > 2 && - ((token.front() == '\"' && token.back() == '\"') || - (token.front() == '<' && token.back() == '>'))) - { - // Extract include path - std::string path = token.substr(1, token.length() - 2); - - // Load include file - const text_file* include_file = resource_manager->load(path); - if (!include_file) - { - source[i] = "#error file not found (" + path + ")"; - } - else - { - // If file has not been included or has no `#pragma once` directive - if (!include_once.contains(include_file)) - { - // If file has `#pragma once` directive - if (has_pragma_once(*include_file)) - { - // Add file to set of files to include once - include_once.insert(include_file); - } - - // Create a copy of the include file - text_file include_file_copy = *include_file; - - // Handle `#pragma include` directives inside include file - handle_includes(include_file_copy, include_once, resource_manager); - - // Replace #pragma include directive with include file contents - source.erase(source.begin() + i); - source.insert(source.begin() + i, include_file_copy.begin(), include_file_copy.end()); - i += include_file_copy.size() - 1; - } - } - } - else - { - source[i] = "#error malformed include directive (" + source[i] + ")"; - } - } - } -} - -template <> -gl::shader_program* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Load shader source file - const text_file* source_file = resource_loader::load(resource_manager, file, path); - - // Make a copy of the shader source file - text_file source_file_copy = *source_file; - - // Handle `#pragma include` directives - std::unordered_set include_once; - include_once.insert(source_file); - handle_includes(source_file_copy, include_once, resource_manager); - - // Join vector of source lines into single string - std::ostringstream stream; - std::copy(source_file_copy.begin(), source_file_copy.end(), std::ostream_iterator(stream, "\n")); - - // Create shader template - render::shader_template* shader = new render::shader_template(stream.str()); - - // Build shader program - gl::shader_program* program = shader->build(render::shader_template::dictionary_type()); - - // Check if shader program was linked successfully - if (!program->was_linked()) - { - throw std::runtime_error("Shader program linking failed: " + program->get_info_log()); - } - - // Destroy shader template - delete shader; - - return program; -} - -template <> -render::shader_template* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Load shader template source file - const text_file* source_file = resource_loader::load(resource_manager, file, path); - - // Make a copy of the shader template source file - text_file source_file_copy = *source_file; - - // Handle `#pragma include` directives - std::unordered_set include_once; - include_once.insert(source_file); - handle_includes(source_file_copy, include_once, resource_manager); - - // Join vector of source lines into single string - std::ostringstream stream; - std::copy(source_file_copy.begin(), source_file_copy.end(), std::ostream_iterator(stream, "\n")); - - // Create shader template - render::shader_template* shader = new render::shader_template(stream.str()); - - return shader; -} diff --git a/src/engine/resources/string-table-loader.cpp b/src/engine/resources/string-table-loader.cpp deleted file mode 100644 index fe57f94..0000000 --- a/src/engine/resources/string-table-loader.cpp +++ /dev/null @@ -1,79 +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 . - */ - -#include -#include -#include -#include - -template <> -i18n::string_table* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - i18n::string_table* table = new i18n::string_table(); - - i18n::string_table_row row; - std::string entry; - - for (;;) - { - char c; - const PHYSFS_sint64 status = PHYSFS_readBytes(file, &c, 1); - - if (status == 1) - { - if (c == '\t') - { - row.push_back(entry); - entry.clear(); - } - else if (c == '\n') - { - row.push_back(entry); - entry.clear(); - table->push_back(row); - row.clear(); - } - else if (c != '\r') - { - entry.push_back(c); - } - } - else - { - if (PHYSFS_eof(file)) - { - if (!entry.empty()) - { - row.push_back(entry); - } - if (!row.empty()) - { - table->push_back(row); - } - break; - } - else - { - throw deserialize_error(PHYSFS_getLastError()); - } - } - } - - return table; -} diff --git a/src/engine/resources/text-file-loader.cpp b/src/engine/resources/text-file-loader.cpp deleted file mode 100644 index c00f297..0000000 --- a/src/engine/resources/text-file-loader.cpp +++ /dev/null @@ -1,37 +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 . - */ - -#include -#include -#include - -template <> -text_file* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - text_file* text = new text_file(); - std::string line; - - while (!PHYSFS_eof(file)) - { - physfs_getline(file, line); - text->push_back(line); - } - - return text; -} diff --git a/src/engine/resources/texture-loader.cpp b/src/engine/resources/texture-loader.cpp deleted file mode 100644 index 5ca939b..0000000 --- a/src/engine/resources/texture-loader.cpp +++ /dev/null @@ -1,259 +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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -template <> -gl::texture_1d* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - std::string buffer; - buffer.resize(size); - PHYSFS_readBytes(file, &buffer[0], size); - - // Parse json from file buffer - nlohmann::json json = nlohmann::json::parse(buffer); - - // Read image filename - std::string image_filename; - if (auto element = json.find("image"); element != json.end()) - image_filename = element.value().get(); - - // Read color space - gl::color_space color_space = gl::color_space::linear; - if (auto element = json.find("color_space"); element != json.end()) - { - std::string value = element.value().get(); - if (value == "linear") - color_space = gl::color_space::linear; - else if (value == "srgb") - color_space = gl::color_space::srgb; - } - - // Read extension mode - gl::texture_wrapping wrapping = gl::texture_wrapping::repeat; - if (auto element = json.find("extension"); element != json.end()) - { - std::string value = element.value().get(); - if (value == "clip") - wrapping = gl::texture_wrapping::clip; - else if (value == "extend") - wrapping = gl::texture_wrapping::extend; - else if (value == "repeat") - wrapping = gl::texture_wrapping::repeat; - else if (value == "mirrored_repeat") - wrapping = gl::texture_wrapping::mirrored_repeat; - } - - // Read interpolation mode - gl::texture_min_filter min_filter = gl::texture_min_filter::linear_mipmap_linear; - gl::texture_mag_filter mag_filter = gl::texture_mag_filter::linear; - if (auto element = json.find("interpolation"); element != json.end()) - { - std::string value = element.value().get(); - if (value == "linear") - { - min_filter = gl::texture_min_filter::linear_mipmap_linear; - mag_filter = gl::texture_mag_filter::linear; - } - else if (value == "closest") - { - min_filter = gl::texture_min_filter::nearest_mipmap_nearest; - mag_filter = gl::texture_mag_filter::nearest; - } - } - - // Read max anisotropy - float max_anisotropy = 0.0f; - if (auto element = json.find("max_anisotropy"); element != json.end()) - max_anisotropy = element.value().get(); - - // Load image - ::image* image = resource_manager->load<::image>(image_filename); - - // Determine pixel type - gl::pixel_type type = (image->get_component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8; - - // Determine pixel format - gl::pixel_format format; - if (image->get_channel_count() == 1) - { - format = gl::pixel_format::r; - } - else if (image->get_channel_count() == 2) - { - format = gl::pixel_format::rg; - } - else if (image->get_channel_count() == 3) - { - format = gl::pixel_format::rgb; - } - else if (image->get_channel_count() == 4) - { - format = gl::pixel_format::rgba; - } - else - { - std::stringstream stream; - stream << std::string("Texture cannot be created from an image with an unsupported number of channels (") << image->get_channel_count() << std::string(")."); - delete image; - throw std::runtime_error(stream.str().c_str()); - } - - // Create texture - gl::texture_1d* texture = new gl::texture_1d(image->get_width(), type, format, color_space, image->data()); - - // Set wrapping and filtering - texture->set_wrapping(wrapping); - texture->set_filters(min_filter, mag_filter); - texture->set_max_anisotropy(max_anisotropy); - - // Free loaded image - resource_manager->unload(image_filename); - - return texture; -} - -template <> -gl::texture_2d* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - std::string buffer; - buffer.resize(size); - PHYSFS_readBytes(file, &buffer[0], size); - - // Parse json from file buffer - nlohmann::json json = nlohmann::json::parse(buffer); - - // Read image filename - std::string image_filename; - if (auto element = json.find("image"); element != json.end()) - image_filename = element.value().get(); - - // Read color space - gl::color_space color_space = gl::color_space::linear; - if (auto element = json.find("color_space"); element != json.end()) - { - std::string value = element.value().get(); - if (value == "linear") - color_space = gl::color_space::linear; - else if (value == "srgb") - color_space = gl::color_space::srgb; - } - - // Read extension mode - gl::texture_wrapping wrapping = gl::texture_wrapping::repeat; - if (auto element = json.find("extension"); element != json.end()) - { - std::string value = element.value().get(); - if (value == "clip") - wrapping = gl::texture_wrapping::clip; - else if (value == "extend") - wrapping = gl::texture_wrapping::extend; - else if (value == "repeat") - wrapping = gl::texture_wrapping::repeat; - else if (value == "mirrored_repeat") - wrapping = gl::texture_wrapping::mirrored_repeat; - } - - // Read interpolation mode - gl::texture_min_filter min_filter = gl::texture_min_filter::linear_mipmap_linear; - gl::texture_mag_filter mag_filter = gl::texture_mag_filter::linear; - if (auto element = json.find("interpolation"); element != json.end()) - { - std::string value = element.value().get(); - if (value == "linear") - { - min_filter = gl::texture_min_filter::linear_mipmap_linear; - mag_filter = gl::texture_mag_filter::linear; - } - else if (value == "closest") - { - min_filter = gl::texture_min_filter::nearest_mipmap_nearest; - mag_filter = gl::texture_mag_filter::nearest; - } - } - - // Read max anisotropy - float max_anisotropy = 0.0f; - if (auto element = json.find("max_anisotropy"); element != json.end()) - max_anisotropy = element.value().get(); - - // Load image - ::image* image = resource_manager->load<::image>(image_filename); - - // Determine pixel type - gl::pixel_type type = (image->get_component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8; - - // Determine pixel format - gl::pixel_format format; - if (image->get_channel_count() == 1) - { - format = gl::pixel_format::r; - } - else if (image->get_channel_count() == 2) - { - format = gl::pixel_format::rg; - } - else if (image->get_channel_count() == 3) - { - format = gl::pixel_format::rgb; - } - else if (image->get_channel_count() == 4) - { - format = gl::pixel_format::rgba; - } - else - { - std::stringstream stream; - stream << std::string("Texture cannot be created from an image with an unsupported number of channels (") << image->get_channel_count() << std::string(")."); - delete image; - throw std::runtime_error(stream.str().c_str()); - } - - // Create texture - gl::texture_2d* texture = new gl::texture_2d(image->get_width(), image->get_height(), type, format, color_space, image->data()); - - // Set wrapping and filtering - texture->set_wrapping(wrapping, wrapping); - texture->set_filters(min_filter, mag_filter); - texture->set_max_anisotropy(max_anisotropy); - - // Free loaded image - resource_manager->unload(image_filename); - - return texture; -} - - diff --git a/src/engine/resources/typeface-loader.cpp b/src/engine/resources/typeface-loader.cpp deleted file mode 100644 index 546f84b..0000000 --- a/src/engine/resources/typeface-loader.cpp +++ /dev/null @@ -1,56 +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 . - */ - -#include -#include -#include -#include -#include -#include FT_FREETYPE_H - -template <> -type::typeface* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - FT_Error error = 0; - - // Init FreeType library object - FT_Library library; - error = FT_Init_FreeType(&library); - if (error) - { - throw std::runtime_error("Failed to init FreeType library (error code " + std::to_string(error) + ")"); - } - - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - unsigned char* buffer = new unsigned char[size]; - PHYSFS_readBytes(file, buffer, size); - - // Load FreeType face from buffer - FT_Face face; - error = FT_New_Memory_Face(library, buffer, size, 0, &face); - if (error) - { - delete[] buffer; - FT_Done_FreeType(library); - throw std::runtime_error("Failed to load FreeType face (error code " + std::to_string(error) + ")"); - } - - return new type::freetype::typeface(library, face, buffer); -} diff --git a/src/engine/scene/billboard.cpp b/src/engine/scene/billboard.cpp index 0611d5a..118a537 100644 --- a/src/engine/scene/billboard.cpp +++ b/src/engine/scene/billboard.cpp @@ -47,7 +47,7 @@ billboard& billboard::operator=(const billboard& other) return *this; } -void billboard::set_material(render::material* material) +void billboard::set_material(std::shared_ptr material) { this->material = material; } @@ -72,7 +72,7 @@ void billboard::update_tweens() object_base::update_tweens(); if (material) { - material->update_tweens(); + //material->update_tweens(); } } diff --git a/src/engine/scene/billboard.hpp b/src/engine/scene/billboard.hpp index c92ea27..72a3f9c 100644 --- a/src/engine/scene/billboard.hpp +++ b/src/engine/scene/billboard.hpp @@ -24,11 +24,13 @@ #include #include #include +#include +#include namespace scene { /// Enumerates billboard types. -enum class billboard_type +enum class billboard_type: std::uint8_t { // No alignment flat, @@ -52,7 +54,7 @@ public: billboard(const billboard& other); billboard& operator=(const billboard& other); - void set_material(render::material* material); + void set_material(std::shared_ptr material); /// Sets the billboard alignment mode. void set_billboard_type(billboard_type type); @@ -60,12 +62,30 @@ public: /// Sets the axis around which the billboard will be rotated when the alignment is set to billboard_alignment::cylindrical. void set_alignment_axis(const float3& axis); - virtual const bounding_volume_type& get_local_bounds() const; - virtual const bounding_volume_type& get_world_bounds() const; - - render::material* get_material() const; - billboard_type get_billboard_type() const; - const float3& get_alignment_axis() const; + [[nodiscard]] inline virtual const bounding_volume_type& get_local_bounds() const noexcept + { + return local_bounds; + } + + [[nodiscard]] inline virtual const bounding_volume_type& get_world_bounds() const noexcept + { + return world_bounds; + } + + [[nodiscard]] inline const std::shared_ptr& get_material() const noexcept + { + return material; + } + + [[nodiscard]] inline billboard_type get_billboard_type() const noexcept + { + return type; + } + + [[nodiscard]] inline const float3& get_alignment_axis() const noexcept + { + return alignment_axis; + } virtual void update_tweens(); @@ -73,40 +93,13 @@ private: static const aabb_type local_bounds; virtual void transformed(); - - + aabb_type world_bounds; - render::material* material; + std::shared_ptr material; billboard_type type; float3 alignment_axis; }; -inline const typename object_base::bounding_volume_type& billboard::get_local_bounds() const -{ - return local_bounds; -} - -inline const typename object_base::bounding_volume_type& billboard::get_world_bounds() const -{ - return world_bounds; -} - -inline render::material* billboard::get_material() const -{ - return material; -} - -inline billboard_type billboard::get_billboard_type() const -{ - return type; -} - -inline const float3& billboard::get_alignment_axis() const -{ - return alignment_axis; -} - } // namespace scene #endif // ANTKEEPER_SCENE_BILLBOARD_HPP - diff --git a/src/engine/scene/directional-light.cpp b/src/engine/scene/directional-light.cpp index 35aba40..a4b4da0 100644 --- a/src/engine/scene/directional-light.cpp +++ b/src/engine/scene/directional-light.cpp @@ -38,10 +38,7 @@ directional_light::directional_light(): shadow_bias(0.005f), shadow_cascade_count(4), shadow_cascade_coverage(1.0f), - shadow_cascade_distribution(0.8f), - light_texture(nullptr), - light_texture_opacity(1.0f, math::lerp), - light_texture_scale({1.0f, 1.0f}, math::lerp) + shadow_cascade_distribution(0.8f) { shadow_cascade_distances.resize(shadow_cascade_count); shadow_cascade_matrices.resize(shadow_cascade_count); @@ -79,31 +76,10 @@ 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) -{ - light_texture = texture; -} - -void directional_light::set_light_texture_opacity(float opacity) -{ - light_texture_opacity[1] = opacity; -} - -void directional_light::set_light_texture_scale(const float2& scale) -{ - light_texture_scale[1] = scale; -} - void directional_light::update_tweens() { light::update_tweens(); direction.update(); - - if (light_texture) - { - light_texture_opacity.update(); - light_texture_scale.update(); - } } void directional_light::transformed() diff --git a/src/engine/scene/directional-light.hpp b/src/engine/scene/directional-light.hpp index cb35f3e..7fd87bf 100644 --- a/src/engine/scene/directional-light.hpp +++ b/src/engine/scene/directional-light.hpp @@ -42,6 +42,11 @@ public: /// Returns the normalized direction vector of the light. const float3& get_direction() const; + inline const tween& get_direction_tween() const noexcept + { + return direction; + } + /// @copydoc object_base::update_tweens(); virtual void update_tweens(); @@ -109,54 +114,12 @@ public: float get_shadow_cascade_distribution() const noexcept; /// Returns the array of shadow cascade far clipping plane distances. - float* get_shadow_cascade_distances() const noexcept; + const std::vector& get_shadow_cascade_distances() const noexcept; + std::vector& get_shadow_cascade_distances() noexcept; /// Returns the array of world-space to cascade texture-space transformation matrices. - float4x4* get_shadow_cascade_matrices() const noexcept; - - /// @} - - /// @name Light texture - /// @{ - - /** - * Sets the light texture, also known as a gobo, cucoloris, or cookie. - * - * @param texture Light texture. - */ - void set_light_texture(const gl::texture_2d* texture); - - /** - * Sets the opacity of the light texture. - * - * @param opacity Light texture opacity, on `[0, 1]`. - */ - void set_light_texture_opacity(float opacity); - - /** - * Sets the scale of the light texture. - * - * @param scale Scale of the light texture. - */ - void set_light_texture_scale(const float2& scale); - - /// Returns the light texture for this light, or `nullptr` if no light texture is set. - const gl::texture_2d* get_light_texture() const; - - /// Returns the light texture opacity. - float get_light_texture_opacity() const; - - /// Returns the light texture scale. - const float2& get_light_texture_scale() const; - - /// Returns the light direction tween. - const tween& get_direction_tween() const; - - /// Returns the light texture opacity tween. - const tween& get_light_texture_opacity_tween() const; - - /// Returns the light texture scale tween. - const tween& get_light_texture_scale_tween() const; + const std::vector& get_shadow_cascade_matrices() const noexcept; + std::vector& get_shadow_cascade_matrices() noexcept; /// @} @@ -173,11 +136,6 @@ private: float shadow_cascade_distribution; mutable std::vector shadow_cascade_distances; mutable std::vector shadow_cascade_matrices; - - const gl::texture_2d* light_texture; - tween light_texture_opacity; - tween light_texture_scale; - }; inline light_type directional_light::get_light_type() const @@ -220,44 +178,24 @@ inline float directional_light::get_shadow_cascade_distribution() const noexcept return shadow_cascade_distribution; } -inline float* directional_light::get_shadow_cascade_distances() const noexcept +inline const std::vector& 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(); + return shadow_cascade_distances; } -inline const gl::texture_2d* directional_light::get_light_texture() const +inline std::vector& directional_light::get_shadow_cascade_distances() noexcept { - return light_texture; + return shadow_cascade_distances; } - -inline float directional_light::get_light_texture_opacity() const -{ - return light_texture_opacity[1]; -} - -inline const float2& directional_light::get_light_texture_scale() const -{ - return light_texture_scale[1]; -} - -inline const tween& directional_light::get_direction_tween() const -{ - return direction; -} - -inline const tween& directional_light::get_light_texture_opacity_tween() const + +inline const std::vector& directional_light::get_shadow_cascade_matrices() const noexcept { - return light_texture_opacity; + return shadow_cascade_matrices; } -inline const tween& directional_light::get_light_texture_scale_tween() const +inline std::vector& directional_light::get_shadow_cascade_matrices() noexcept { - return light_texture_scale; + return shadow_cascade_matrices; } } // namespace scene diff --git a/src/engine/scene/model-instance.cpp b/src/engine/scene/model-instance.cpp index d5456e2..d69af35 100644 --- a/src/engine/scene/model-instance.cpp +++ b/src/engine/scene/model-instance.cpp @@ -23,45 +23,18 @@ namespace scene { -model_instance::model_instance(render::model* model): - model(nullptr), - local_bounds{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}, - world_bounds{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}, - instanced(false), - instance_count(0) +model_instance::model_instance(std::shared_ptr model) { set_model(model); - update_bounds(); -} - -model_instance::model_instance(): - model_instance(nullptr) -{} - -model_instance::model_instance(const model_instance& other) -{ - *this = other; -} - -model_instance& model_instance::operator=(const model_instance& other) -{ - local_bounds = other.local_bounds; - world_bounds = other.world_bounds; - model = other.model; - pose = other.pose; - materials = other.materials; - instanced = other.instanced; - instance_count = other.instance_count; - return *this; } -void model_instance::set_model(render::model* model) +void model_instance::set_model(std::shared_ptr model) { this->model = model; - + if (model) { - materials.resize(model->get_groups()->size()); + materials.resize(model->get_groups().size()); reset_materials(); pose = model->get_skeleton().bind_pose; @@ -75,7 +48,7 @@ void model_instance::set_model(render::model* model) update_bounds(); } -void model_instance::set_material(std::size_t group_index, render::material* material) +void model_instance::set_material(std::size_t group_index, std::shared_ptr material) { materials[group_index] = material; } @@ -114,25 +87,12 @@ void model_instance::update_tweens() { object_base::update_tweens(); - // Update model material tweens - if (model) - { - for (render::model_group* group: *model->get_groups()) - { - render::material* material = group->get_material(); - if (material) - { - material->update_tweens(); - } - } - } - // Update material override tweens - for (render::material* material: materials) + for (auto& material: materials) { if (material) { - material->update_tweens(); + //material->update_tweens(); } } } diff --git a/src/engine/scene/model-instance.hpp b/src/engine/scene/model-instance.hpp index 30d2e72..668eb5c 100644 --- a/src/engine/scene/model-instance.hpp +++ b/src/engine/scene/model-instance.hpp @@ -33,15 +33,22 @@ class model_instance: public object public: typedef geom::aabb aabb_type; - explicit model_instance(render::model* model); - model_instance(); - model_instance(const model_instance& other); - model_instance& operator=(const model_instance& other); + /** + * Constructs a model instance and sets its model. + * + * @param model Model with which to associate the model instance. + */ + explicit model_instance(std::shared_ptr model); + + /** + * Constructs a model instance. + */ + model_instance() = default; /** * Sets the model with which this model instance is associated. This will reset the pose and all overwritten materials. */ - void set_model(render::model* model); + void set_model(std::shared_ptr model); /** * Overwrites the material of a model group for this model instance. @@ -49,31 +56,76 @@ public: * @param group_index Index of a model group. * @param material Pointer to the material which should overwrite the model group's material. A value of `nullptr` indicates the material will not be overwritten. */ - void set_material(std::size_t group_index, render::material* material); + void set_material(std::size_t group_index, std::shared_ptr material); void set_instanced(bool instanced, std::size_t instance_count = 1); - + /** * Resets all overwritten materials. */ void reset_materials(); - virtual const bounding_volume_type& get_local_bounds() const; - virtual const bounding_volume_type& get_world_bounds() const; - - const render::model* get_model() const; - render::model* get_model(); + /** + * Returns the local bounds of the model instance. + */ + [[nodiscard]] inline virtual const bounding_volume_type& get_local_bounds() const noexcept + { + return local_bounds; + } - /// Returns the skeletal animation pose of this model. - const pose& get_pose() const; + /** + * Returns the world bounds of the model instance. + */ + [[nodiscard]] inline virtual const bounding_volume_type& get_world_bounds() const noexcept + { + return world_bounds; + } - /// @copydoc model_instance::get_pose() const - pose& get_pose(); - - const std::vector* get_materials() const; + /** + * Returns the model of the model instance. + */ + [[nodiscard]] inline const std::shared_ptr& get_model() const noexcept + { + return model; + } - bool is_instanced() const; - std::size_t get_instance_count() const; + /** + * Returns the skeletal animation pose of this model instance. + */ + /// @{ + [[nodiscard]] inline const pose& get_pose() const noexcept + { + return pose; + } + [[nodiscard]] inline pose& get_pose() noexcept + { + return pose; + } + /// @} + + /** + * Returns the materials of this model instance. + */ + [[nodiscard]] inline const std::vector>& get_materials() const noexcept + { + return materials; + } + + /** + * Returns `true` if the model instance is instanced, `false` otherwise. + */ + [[nodiscard]] inline bool is_instanced() const noexcept + { + return instanced; + } + + /** + * Returns the number of instances, if the model is instanced. + */ + [[nodiscard]] inline std::size_t get_instance_count() const noexcept + { + return instance_count; + } virtual void update_tweens(); @@ -82,61 +134,15 @@ public: private: virtual void transformed(); - render::model* model; + std::shared_ptr model; ::pose pose; - std::vector materials; - aabb_type local_bounds; - aabb_type world_bounds; - bool instanced; - std::size_t instance_count; + std::vector> materials; + aabb_type local_bounds{{0, 0, 0}, {0, 0, 0}}; + aabb_type world_bounds{{0, 0, 0}, {0, 0, 0}}; + bool instanced{false}; + std::size_t instance_count{0}; }; -inline const typename object_base::bounding_volume_type& model_instance::get_local_bounds() const -{ - return local_bounds; -} - -inline const typename object_base::bounding_volume_type& model_instance::get_world_bounds() const -{ - return world_bounds; -} - -inline const render::model* model_instance::get_model() const -{ - return model; -} - -inline render::model* model_instance::get_model() -{ - return model; -} - -inline const pose& model_instance::get_pose() const -{ - return pose; -} - -inline pose&model_instance::get_pose() -{ - return pose; -} - -inline const std::vector* model_instance::get_materials() const -{ - return &materials; -} - -inline bool model_instance::is_instanced() const -{ - return instanced; -} - -inline std::size_t model_instance::get_instance_count() const -{ - return instance_count; -} - } // namespace scene #endif // ANTKEEPER_SCENE_MODEL_INSTANCE_HPP - diff --git a/src/engine/scene/text.cpp b/src/engine/scene/text.cpp index bfb75f4..1a5c981 100644 --- a/src/engine/scene/text.cpp +++ b/src/engine/scene/text.cpp @@ -39,8 +39,8 @@ text::text(): vbo(nullptr) { // Allocate VBO and VAO - vbo = new gl::vertex_buffer(0, nullptr, gl::buffer_usage::static_draw); - vao = new gl::vertex_array(); + vbo = std::make_unique(); + vao = std::make_unique(); // Calculate vertex stride vertex_stride = (3 + 2 + 4) * sizeof(float); @@ -50,7 +50,7 @@ text::text(): // Define vertex position attribute gl::vertex_attribute position_attribute; - position_attribute.buffer = vbo; + position_attribute.buffer = vbo.get(); position_attribute.offset = attribute_offset; position_attribute.stride = vertex_stride; position_attribute.type = gl::vertex_attribute_type::float_32; @@ -59,7 +59,7 @@ text::text(): // Define vertex UV attribute gl::vertex_attribute uv_attribute; - uv_attribute.buffer = vbo; + uv_attribute.buffer = vbo.get(); uv_attribute.offset = attribute_offset; uv_attribute.stride = vertex_stride; uv_attribute.type = gl::vertex_attribute_type::float_32; @@ -68,7 +68,7 @@ text::text(): // Define vertex color attribute gl::vertex_attribute color_attribute; - color_attribute.buffer = vbo; + color_attribute.buffer = vbo.get(); color_attribute.offset = attribute_offset; color_attribute.stride = vertex_stride; color_attribute.type = gl::vertex_attribute_type::float_32; @@ -84,24 +84,19 @@ text::text(): render_op.material = nullptr; render_op.bone_count = 0; render_op.skinning_palette = nullptr; - render_op.vertex_array = vao; + render_op.vertex_array = vao.get(); 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() -{ - // Free VAO and VBO - delete vao; - 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::vector(render_op.transform[3])); @@ -113,10 +108,10 @@ void text::refresh() update_content(); } -void text::set_material(render::material* material) +void text::set_material(std::shared_ptr material) { this->material = material; - render_op.material = material; + render_op.material = material.get(); } void text::set_font(const type::bitmap_font* font) @@ -175,7 +170,7 @@ void text::update_tweens() object_base::update_tweens(); if (material) { - material->update_tweens(); + //material->update_tweens(); } } @@ -197,7 +192,9 @@ void text::update_content() // Expand vertex data buffer to accommodate vertices if (vertex_data.size() < min_vertex_buffer_size) + { vertex_data.resize(min_vertex_buffer_size); + } // Get font metrics and bitmap const type::font_metrics& font_metrics = font->get_font_metrics(); @@ -250,8 +247,8 @@ void text::update_content() positions[i].y() = std::round(positions[i].y()); // Normalize UVs - uvs[i].x() = uvs[i].x() / static_cast(font_bitmap.get_width()); - uvs[i].y() = uvs[i].y() / static_cast(font_bitmap.get_height()); + uvs[i].x() = uvs[i].x() / static_cast(font_bitmap.width()); + uvs[i].y() = uvs[i].y() / static_cast(font_bitmap.height()); } // Add vertex to vertex data buffer @@ -304,11 +301,11 @@ void text::update_content() if (vertex_count > this->vertex_count) { this->vertex_count = vertex_count; - vbo->resize(min_vertex_buffer_size, vertex_data.data()); + vbo->resize(min_vertex_buffer_size, vertex_data); } else { - vbo->write(0, min_vertex_buffer_size, vertex_data.data()); + vbo->write({vertex_data.data(), min_vertex_buffer_size}); } // Update vertex count @@ -335,7 +332,7 @@ void text::update_color() } // Update VBO - vbo->write(0, vertex_count * vertex_stride, vertex_data.data()); + vbo->write({vertex_data.data(), vertex_count * vertex_stride}); } } // namespace scene diff --git a/src/engine/scene/text.hpp b/src/engine/scene/text.hpp index c66d036..5c24de1 100644 --- a/src/engine/scene/text.hpp +++ b/src/engine/scene/text.hpp @@ -42,9 +42,6 @@ public: /// Constructs a text object. text(); - /// 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; @@ -58,7 +55,7 @@ public: * * @param material Text material. */ - void set_material(render::material* material); + void set_material(std::shared_ptr material); /** * Sets the text font. @@ -91,7 +88,7 @@ public: void set_color(const float4& color); /// Returns the text material. - render::material* get_material() const; + const std::shared_ptr& get_material() const; /// Returns the text font. const type::bitmap_font* get_font() const; @@ -123,7 +120,7 @@ private: mutable render::operation render_op; aabb_type local_bounds; aabb_type world_bounds; - render::material* material; + std::shared_ptr material; const type::bitmap_font* font; type::text_direction direction; std::string content_u8; @@ -132,11 +129,11 @@ private: std::size_t vertex_stride; std::size_t vertex_count; std::vector vertex_data; - gl::vertex_array* vao; - gl::vertex_buffer* vbo; + std::unique_ptr vao; + std::unique_ptr vbo; }; -inline render::material* text::get_material() const +inline const std::shared_ptr& text::get_material() const { return material; } diff --git a/src/engine/type/bitmap-font.cpp b/src/engine/type/bitmap-font.cpp index f9d7c47..3adf0c7 100644 --- a/src/engine/type/bitmap-font.cpp +++ b/src/engine/type/bitmap-font.cpp @@ -54,11 +54,11 @@ void bitmap_font::clear() bool bitmap_font::pack(bool resize) { // Returns the smallest power of two that is not smaller than @p x. - auto ceil2 = [](unsigned int x) -> unsigned int + auto ceil2 = [](std::uint32_t x) -> std::uint32_t { if (x <= 1) return 1; - unsigned int y = 2; + std::uint32_t y = 2; --x; while (x >>= 1) y <<= 1; @@ -66,17 +66,17 @@ bool bitmap_font::pack(bool resize) }; // Calculate initial size of the font bitmap - unsigned int bitmap_w; - unsigned int bitmap_h; + std::uint32_t bitmap_w; + std::uint32_t bitmap_h; if (resize) { // Find the maximum glyph dimensions - unsigned int max_glyph_w = 0; - unsigned int max_glyph_h = 0; + std::uint32_t max_glyph_w = 0; + std::uint32_t max_glyph_h = 0; for (auto it = glyphs.begin(); it != glyphs.end(); ++it) { - max_glyph_w = std::max(max_glyph_w, it->second.bitmap.get_width()); - max_glyph_h = std::max(max_glyph_h, it->second.bitmap.get_height()); + max_glyph_w = std::max(max_glyph_w, it->second.bitmap.width()); + max_glyph_h = std::max(max_glyph_h, it->second.bitmap.height()); } // Find minimum power of two dimensions that can accommodate maximum glyph dimensions @@ -85,13 +85,13 @@ bool bitmap_font::pack(bool resize) } else { - bitmap_w = bitmap.get_width(); - bitmap_h = bitmap.get_height(); + bitmap_w = bitmap.width(); + bitmap_h = bitmap.height(); } bool packed = false; - geom::rect_pack glyph_pack(bitmap_w, bitmap_h); - std::unordered_map::node_type*> glyph_map; + geom::rect_pack glyph_pack(bitmap_w, bitmap_h); + std::unordered_map::node_type*> glyph_map; while (!packed) { @@ -99,7 +99,7 @@ bool bitmap_font::pack(bool resize) for (auto it = glyphs.begin(); it != glyphs.end(); ++it) { // Attempt to pack glyph bitmap - const auto* node = glyph_pack.pack(it->second.bitmap.get_width(), it->second.bitmap.get_height()); + const auto* node = glyph_pack.pack(it->second.bitmap.width(), it->second.bitmap.height()); // Abort if packing failed if (!node) @@ -152,7 +152,7 @@ bool bitmap_font::pack(bool resize) // Copy glyph bitmap data into font bitmap image& glyph_bitmap = it->second.bitmap; - bitmap.copy(glyph_bitmap, glyph_bitmap.get_width(), glyph_bitmap.get_height(), 0, 0, node->bounds.min.x(), node->bounds.min.y()); + bitmap.copy(glyph_bitmap, glyph_bitmap.width(), glyph_bitmap.height(), 0, 0, node->bounds.min.x(), node->bounds.min.y()); // Record coordinates of glyph bitmap within font bitmap it->second.position = {node->bounds.min.x(), node->bounds.min.y()}; @@ -173,16 +173,20 @@ void bitmap_font::unpack(bool resize) bitmap_glyph& glyph = it->second; // Get glyph dimensions - unsigned int glyph_width = static_cast(glyph.metrics.width + 0.5f); - unsigned int glyph_height = static_cast(glyph.metrics.height + 0.5f); + std::uint32_t glyph_width = static_cast(glyph.metrics.width + 0.5f); + std::uint32_t glyph_height = static_cast(glyph.metrics.height + 0.5f); // Reformat glyph bitmap if necessary if (!glyph.bitmap.compatible(bitmap)) - glyph.bitmap.format(bitmap.get_component_size(), bitmap.get_channel_count()); + { + glyph.bitmap.format(bitmap.component_size(), bitmap.channel_count()); + } // Resize glyph bitmap if necessary - if (glyph.bitmap.get_width() != glyph_width || glyph.bitmap.get_height() != glyph_height) + if (glyph.bitmap.width() != glyph_width || glyph.bitmap.height() != glyph_height) + { glyph.bitmap.resize(glyph_width, glyph_height); + } // Copy pixel data from font bitmap to glyph bitmap glyph.bitmap.copy(bitmap, glyph_width, glyph_height, glyph.position.x(), glyph.position.y()); diff --git a/src/engine/type/bitmap-font.hpp b/src/engine/type/bitmap-font.hpp index d615119..49f4307 100644 --- a/src/engine/type/bitmap-font.hpp +++ b/src/engine/type/bitmap-font.hpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include namespace type { diff --git a/src/engine/type/bitmap-glyph.hpp b/src/engine/type/bitmap-glyph.hpp index de2bf29..6b2a5e5 100644 --- a/src/engine/type/bitmap-glyph.hpp +++ b/src/engine/type/bitmap-glyph.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_TYPE_BITMAP_GLYPH_HPP #define ANTKEEPER_TYPE_BITMAP_GLYPH_HPP -#include +#include #include #include diff --git a/src/engine/type/freetype/typeface.cpp b/src/engine/type/freetype/ft-typeface.cpp similarity index 74% rename from src/engine/type/freetype/typeface.cpp rename to src/engine/type/freetype/ft-typeface.cpp index 465a556..c7ff015 100644 --- a/src/engine/type/freetype/typeface.cpp +++ b/src/engine/type/freetype/ft-typeface.cpp @@ -17,17 +17,16 @@ * along with Antkeeper source code. If not, see . */ -#include +#include #include #include namespace type { -namespace freetype { -typeface::typeface(FT_Library library, FT_Face face, unsigned char* buffer): +ft_typeface::ft_typeface(FT_Library library, FT_Face face, std::unique_ptr> file_buffer): library(library), face(face), - buffer(buffer), + file_buffer{std::move(file_buffer)}, height(-1.0f) { /// Build charset @@ -40,19 +39,18 @@ typeface::typeface(FT_Library library, FT_Face face, unsigned char* buffer): } } -typeface::~typeface() +ft_typeface::~ft_typeface() { FT_Done_Face(face); - delete[] buffer; FT_Done_FreeType(library); } -bool typeface::has_kerning() const +bool ft_typeface::has_kerning() const { return FT_HAS_KERNING(face); } -bool typeface::get_metrics(float height, font_metrics& metrics) const +bool ft_typeface::get_metrics(float height, font_metrics& metrics) const { // Set font size set_face_pixel_size(height); @@ -71,7 +69,7 @@ bool typeface::get_metrics(float height, font_metrics& metrics) const return true; } -bool typeface::get_metrics(float height, char32_t code, glyph_metrics& metrics) const +bool ft_typeface::get_metrics(float height, char32_t code, glyph_metrics& metrics) const { // Set font size set_face_pixel_size(height); @@ -80,8 +78,7 @@ bool typeface::get_metrics(float height, char32_t code, glyph_metrics& metrics) FT_UInt glyph_index = FT_Get_Char_Index(face, static_cast(code)); // Load glyph and render bitmap - FT_Error error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); - if (error) + if (FT_Error error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT)) { throw std::runtime_error("FreeType failed to load glyph (error code " + std::to_string(error) + ")"); } @@ -99,7 +96,7 @@ bool typeface::get_metrics(float height, char32_t code, glyph_metrics& metrics) return true; } -bool typeface::get_bitmap(float height, char32_t code, image& bitmap) const +bool ft_typeface::get_bitmap(float height, char32_t code, image& bitmap) const { // Set font size set_face_pixel_size(height); @@ -108,40 +105,40 @@ bool typeface::get_bitmap(float height, char32_t code, image& bitmap) const FT_UInt glyph_index = FT_Get_Char_Index(face, static_cast(code)); // Load glyph and render bitmap - FT_Error error = FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER | FT_LOAD_TARGET_(FT_RENDER_MODE_NORMAL)); - if (error) + if (FT_Error error = FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER | FT_LOAD_TARGET_(FT_RENDER_MODE_NORMAL))) { throw std::runtime_error("FreeType failed to load glyph (error code " + std::to_string(error) + ")"); } // Format and resize bitmap bitmap.resize(0, 0); - bitmap.format(sizeof(unsigned char), 1); + bitmap.format(sizeof(FT_Byte), 1); bitmap.resize(face->glyph->bitmap.width, face->glyph->bitmap.rows); // Copy glyph bitmap data in bitmap - std::memcpy(bitmap.data(), face->glyph->bitmap.buffer, bitmap.get_size()); + std::memcpy(bitmap.data(), face->glyph->bitmap.buffer, bitmap.size()); return true; } -bool typeface::get_kerning(float height, char32_t first, char32_t second, float2& offset) const +bool ft_typeface::get_kerning(float height, char32_t first, char32_t second, float2& offset) const { // Check if typeface has kerning information if (!has_kerning()) + { return false; + } // Set font size set_face_pixel_size(height); // Get indices of the two glyphs - FT_UInt first_index = FT_Get_Char_Index(face, static_cast(first)); - FT_UInt second_index = FT_Get_Char_Index(face, static_cast(second)); + const FT_UInt first_index = FT_Get_Char_Index(face, static_cast(first)); + const FT_UInt second_index = FT_Get_Char_Index(face, static_cast(second)); // Get kerning vector FT_Vector kerning; - FT_Error error = FT_Get_Kerning(face, first_index, second_index, FT_KERNING_DEFAULT, &kerning); - if (error) + if (FT_Error error = FT_Get_Kerning(face, first_index, second_index, FT_KERNING_DEFAULT, &kerning)) { throw std::runtime_error("FreeType failed to get kerning vector (error code " + std::to_string(error) + ")"); } @@ -151,13 +148,14 @@ bool typeface::get_kerning(float height, char32_t first, char32_t second, float2 return true; } -void typeface::set_face_pixel_size(float height) const +void ft_typeface::set_face_pixel_size(float height) const { if (this->height == height) + { return; + } - FT_Error error = FT_Set_Pixel_Sizes(face, 0, static_cast(height)); - if (error) + if (FT_Error error = FT_Set_Pixel_Sizes(face, 0, static_cast(height))) { throw std::runtime_error("FreeType failed to set face size (error code " + std::to_string(error) + ")"); } @@ -165,5 +163,4 @@ void typeface::set_face_pixel_size(float height) const this->height = height; } -} // namespace freetype } // namespace type diff --git a/src/engine/type/freetype/typeface.hpp b/src/engine/type/freetype/ft-typeface.hpp similarity index 68% rename from src/engine/type/freetype/typeface.hpp rename to src/engine/type/freetype/ft-typeface.hpp index b2a84fb..37b5b98 100644 --- a/src/engine/type/freetype/typeface.hpp +++ b/src/engine/type/freetype/ft-typeface.hpp @@ -17,22 +17,23 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_TYPE_FREETYPE_TYPEFACE_HPP -#define ANTKEEPER_TYPE_FREETYPE_TYPEFACE_HPP +#ifndef ANTKEEPER_TYPE_FT_TYPEFACE_HPP +#define ANTKEEPER_TYPE_FT_TYPEFACE_HPP #include #include #include FT_FREETYPE_H +#include +#include namespace type { -namespace freetype { /** * Typeface implementation using the FreeType library. * * @see type::typeface */ -class typeface: public type::typeface +class ft_typeface: public typeface { public: /** @@ -40,26 +41,17 @@ public: * * @param library Pointer to a FreeType library instance. * @param face Pointer to the FreeType object instance. - * @param buffer Pointer to file buffer containing FreeType face data. + * @param file_buffer File buffer containing FreeType face data. */ - typeface(FT_Library library, FT_Face face, unsigned char* buffer); + ft_typeface(FT_Library library, FT_Face face, std::unique_ptr> file_buffer); /// Destroys a FreeType typeface. - virtual ~typeface(); + virtual ~ft_typeface(); - /// @copydoc type::typeface::has_kerning() const virtual bool has_kerning() const; - - /// @copydoc type::typeface::get_metrics(float, font_metrics&) const virtual bool get_metrics(float height, font_metrics& metrics) const; - - /// @copydoc type::typeface::get_metrics(float, char32_t, glyph_metrics&) const virtual bool get_metrics(float height, char32_t code, glyph_metrics& metrics) const; - - /// @copydoc type::typeface::get_bitmap(float, char32_t, image&) const virtual bool get_bitmap(float height, char32_t code, image& bitmap) const; - - /// @copydoc type::typeface::get_kerning(float, char32_t, char32_t, float2&) const virtual bool get_kerning(float height, char32_t first, char32_t second, float2& offset) const; private: @@ -67,11 +59,10 @@ private: FT_Library library; FT_Face face; - unsigned char* buffer; + std::unique_ptr> file_buffer; mutable float height; }; -} // namespace freetype } // namespace type -#endif // ANTKEEPER_TYPE_FREETYPE_TYPEFACE_HPP +#endif // ANTKEEPER_TYPE_FT_TYPEFACE_HPP diff --git a/src/engine/type/typeface.cpp b/src/engine/type/typeface.cpp index 3eebb70..f2ea360 100644 --- a/src/engine/type/typeface.cpp +++ b/src/engine/type/typeface.cpp @@ -18,6 +18,12 @@ */ #include +#include +#include +#include +#include +#include +#include FT_FREETYPE_H namespace type { @@ -42,3 +48,28 @@ void typeface::set_weight(int weight) } } // namespace type + +template <> +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) +{ + // Init FreeType library object + FT_Library library; + if (FT_Error error = FT_Init_FreeType(&library)) + { + throw std::runtime_error("Failed to init FreeType library (error code " + std::to_string(error) + ")"); + } + + // Read file into file buffer + std::unique_ptr> file_buffer = std::make_unique>(ctx.size()); + ctx.read8(reinterpret_cast(file_buffer->data()), ctx.size()); + + // Load FreeType face from file buffer + FT_Face face; + if (FT_Error error = FT_New_Memory_Face(library, file_buffer->data(), static_cast(file_buffer->size()), 0, &face)) + { + FT_Done_FreeType(library); + throw deserialize_error("Failed to load FreeType face (error code " + std::to_string(error) + ")"); + } + + return std::make_unique(library, face, std::move(file_buffer)); +} diff --git a/src/engine/type/typeface.hpp b/src/engine/type/typeface.hpp index 4554c32..f747cf9 100644 --- a/src/engine/type/typeface.hpp +++ b/src/engine/type/typeface.hpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include diff --git a/src/engine/utility/dict.cpp b/src/engine/utility/dict.cpp index 1389ed0..e0b11ae 100644 --- a/src/engine/utility/dict.cpp +++ b/src/engine/utility/dict.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -46,7 +47,7 @@ static void deserialize_any(std::any& any, deserialize_context& ctx) } /** - * Serializes a dict with an unsigned 32-bit key. + * Serializes a dict with a 32-bit FNV-1a hash key. * * @param[in] dict Dict to serialize. * @param[in,out] ctx Serialize context. @@ -55,7 +56,7 @@ static void deserialize_any(std::any& any, deserialize_context& ctx) * @throw serialize_error Unsupported dict value type. */ template <> -void serializer>::serialize(const dict& dict, serialize_context& ctx) +void serializer>::serialize(const dict& dict, serialize_context& ctx) { // Map type indices to tuples containing a type hash and serialize function pointer static const std::unordered_map @@ -63,26 +64,26 @@ void serializer>::serialize(const dict& dict, std::type_index, std::tuple < - std::uint32_t, + hash::fnv1a32_t, void (*)(const std::any&, serialize_context&) > > type_map { - {std::type_index(typeid(bool)), {"bool"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::uint8_t)), {"uint8"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::uint16_t)), {"uint16"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::uint32_t)), {"uint32"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::uint64_t)), {"uint64"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::int8_t)), {"int8"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::int16_t)), {"int16"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::int32_t)), {"int32"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::int64_t)), {"int64"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(float)), {"float"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(double)), {"double"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::string)), {"string"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::u8string)), {"u8string"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::u16string)), {"u16string"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::u32string)), {"u32string"_fnv1a32, &serialize_any}} + {std::type_index(typeid(bool)), {"bool", &serialize_any}}, + {std::type_index(typeid(std::uint8_t)), {"uint8", &serialize_any}}, + {std::type_index(typeid(std::uint16_t)), {"uint16", &serialize_any}}, + {std::type_index(typeid(std::uint32_t)), {"uint32", &serialize_any}}, + {std::type_index(typeid(std::uint64_t)), {"uint64", &serialize_any}}, + {std::type_index(typeid(std::int8_t)), {"int8", &serialize_any}}, + {std::type_index(typeid(std::int16_t)), {"int16", &serialize_any}}, + {std::type_index(typeid(std::int32_t)), {"int32", &serialize_any}}, + {std::type_index(typeid(std::int64_t)), {"int64", &serialize_any}}, + {std::type_index(typeid(float)), {"float", &serialize_any}}, + {std::type_index(typeid(double)), {"double", &serialize_any}}, + {std::type_index(typeid(std::string)), {"string", &serialize_any}}, + {std::type_index(typeid(std::u8string)), {"u8string", &serialize_any}}, + {std::type_index(typeid(std::u16string)), {"u16string", &serialize_any}}, + {std::type_index(typeid(std::u32string)), {"u32string", &serialize_any}} }; // Write dict size @@ -111,39 +112,39 @@ void serializer>::serialize(const dict& dict, } /** - * Deserializes a dict with an unsigned 32-bit key. + * Deserializes a dict with a 32-bit FNV-1a hash key. * - * @param[out] dict Dict to serialize. + * @param[out] dict Dict to deserialize. * @param[in,out] ctx Deserialize context. * * @throw deserialize_error Read error. * @throw deserialize_error Unsupported dict value type. */ template <> -void deserializer>::deserialize(dict& dict, deserialize_context& ctx) +void deserializer>::deserialize(dict& dict, deserialize_context& ctx) { // Map type hashes to deserialize function pointers static const std::unordered_map < - std::uint32_t, + hash::fnv1a32_t, void (*)(std::any&, deserialize_context&) > type_map { - {"bool"_fnv1a32, &deserialize_any}, - {"uint8"_fnv1a32, &deserialize_any}, - {"uint16"_fnv1a32, &deserialize_any}, - {"uint32"_fnv1a32, &deserialize_any}, - {"uint64"_fnv1a32, &deserialize_any}, - {"int8"_fnv1a32, &deserialize_any}, - {"int16"_fnv1a32, &deserialize_any}, - {"int32"_fnv1a32, &deserialize_any}, - {"int64"_fnv1a32, &deserialize_any}, - {"float"_fnv1a32, &deserialize_any}, - {"double"_fnv1a32, &deserialize_any}, - {"string"_fnv1a32, &deserialize_any}, - {"u8string"_fnv1a32, &deserialize_any}, - {"u16string"_fnv1a32, &deserialize_any}, - {"u32string"_fnv1a32, &deserialize_any} + {"bool", &deserialize_any}, + {"uint8", &deserialize_any}, + {"uint16", &deserialize_any}, + {"uint32", &deserialize_any}, + {"uint64", &deserialize_any}, + {"int8", &deserialize_any}, + {"int16", &deserialize_any}, + {"int32", &deserialize_any}, + {"int64", &deserialize_any}, + {"float", &deserialize_any}, + {"double", &deserialize_any}, + {"string", &deserialize_any}, + {"u8string", &deserialize_any}, + {"u16string", &deserialize_any}, + {"u32string", &deserialize_any} }; dict.clear(); @@ -162,7 +163,7 @@ void deserializer>::deserialize(dict& dict, d if (auto i = type_map.find(type_hash); i != type_map.end()) { // Read entry key - std::uint32_t key = 0; + hash::fnv1a32_t key; ctx.read32(reinterpret_cast(&key), 1); // Deserialize entry value @@ -174,3 +175,13 @@ void deserializer>::deserialize(dict& dict, d } } } + +template <> +std::unique_ptr> resource_loader>::load(::resource_manager& resource_manager, deserialize_context& ctx) +{ + auto resource = std::make_unique>(); + + deserializer>().deserialize(*resource, ctx); + + return resource; +} diff --git a/src/engine/utility/hash/combine.hpp b/src/engine/utility/hash/combine.hpp new file mode 100644 index 0000000..2f314d5 --- /dev/null +++ b/src/engine/utility/hash/combine.hpp @@ -0,0 +1,51 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_UTILITY_HASH_COMBINE_HPP +#define ANTKEEPER_UTILITY_HASH_COMBINE_HPP + +#include + +namespace hash { + +/** + * Combines two hash values. + * + * @param x First hash value. + * @param y Second hash value. + * + * @return Combination of @p x and @p y. + */ +/// @{ +[[nodiscard]] inline std::uint32_t combine(std::uint32_t x, std::uint32_t y) noexcept +{ + // 0x9e3779b9 = 2^32 / Phi + return x ^ (y + 0x9e3779b9 + (x << 6) + (x >> 2)); +} + +[[nodiscard]] inline std::uint64_t combine(std::uint64_t x, std::uint64_t y) noexcept +{ + // 0x9e3779b97f4a7c16 = 2^64 / Phi + return x ^ (y + 0x9e3779b97f4a7c16 + (x << 6) + (x >> 2)); +} +/// @} + +} // namespace hash + +#endif // ANTKEEPER_UTILITY_HASH_COMBINE_HPP diff --git a/src/engine/utility/hash/fnv1a.hpp b/src/engine/utility/hash/fnv1a.hpp index 5112e65..ddebc2c 100644 --- a/src/engine/utility/hash/fnv1a.hpp +++ b/src/engine/utility/hash/fnv1a.hpp @@ -21,8 +21,10 @@ #define ANTKEEPER_UTILITY_HASH_FNV1A_HPP #include -#include #include +#include +#include +#include namespace hash { @@ -30,10 +32,9 @@ namespace hash { * FNV-1a hash function. * * @tparam HashT Unsigned integral hash type. - * @tparam CharT String character type. + * @tparam DataT Data element type. * - * @param string String to hash. - * @param length Number of characters in @p string. + * @param data Array of data to hash. * @param offset FNV offset basis value. * @param prime FNV prime value. * @@ -41,27 +42,25 @@ namespace hash { * * @see https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function */ -template -[[nodiscard]] constexpr HashT fnv1a(const CharT* string, std::size_t length, HashT offset, HashT prime) noexcept +template +[[nodiscard]] constexpr HashT fnv1a(std::span data, HashT offset, HashT prime) noexcept { - for (std::size_t i = 0; i < length; ++i) + for (auto element: data) { - if constexpr (sizeof(CharT) == 1) + if constexpr (sizeof(DataT) == 1) { - offset ^= static_cast(*string); + offset ^= static_cast(element); offset *= prime; } else { /// @TODO `reinterpret_cast` is not supported in consteval. C++23 has `if consteval` which can selectively enable reinterpret_cast at runtime, and extract bytes manually at compile-time. - for (std::size_t j = 0; j < sizeof(CharT); ++j) + for (std::size_t j = 0; j < sizeof(DataT); ++j) { - offset ^= static_cast(((*string) >> (j * 8)) & 255); + offset ^= static_cast(((element) >> (j * 8)) & 255); offset *= prime; } } - - ++string; } return offset; @@ -70,13 +69,14 @@ template /** * 32-bit FNV-1a hash function. * - * @param string String to hash. - * @param length Number of characters in @p string. + * @tparam DataT Data element type. * - * @return 32-bit hash value. + * @param data Array of data to hash. + * + * @return 32-bit FNV-1a hash value. */ -template -[[nodiscard]] constexpr std::uint32_t fnv1a32(const CharT* string, std::size_t length) noexcept +template +[[nodiscard]] constexpr std::uint32_t fnv1a32(std::span data) noexcept { // 32-bit FNV offset basis value. constexpr std::uint32_t offset = 2166136261; @@ -84,19 +84,20 @@ template // 32-bit FNV prime value. constexpr std::uint32_t prime = 16777619; - return fnv1a(string, length, offset, prime); + return fnv1a(data, offset, prime); } /** * 64-bit FNV-1a hash function. * - * @param string String to hash. - * @param length Number of characters in @p string. + * @tparam DataT Data element type. * - * @return 64-bit hash value. + * @param data Array of data to hash. + * + * @return 64-bit FNV-1a hash value. */ -template -[[nodiscard]] constexpr std::uint64_t fnv1a64(const CharT* string, std::size_t length) noexcept +template +[[nodiscard]] constexpr std::uint64_t fnv1a64(std::span data) noexcept { // 64-bit FNV offset basis value. constexpr std::uint64_t offset = 14695981039346656037ULL; @@ -104,9 +105,130 @@ template // 64-bit FNV prime value. constexpr std::uint64_t prime = 1099511628211ULL; - return fnv1a(string, length, offset, prime); + return fnv1a(data, offset, prime); } +namespace types { + +/** + * 32-bit FNV-1a hash value. + */ +struct fnv1a32_t +{ + /// Hash value type. + using value_type = std::uint32_t; + + /** + * Constructs a 32-bit FNV-1a hash value. + * + * @param value 32-bit FNV-1a hash value. + */ + /// @{ + inline constexpr fnv1a32_t(value_type value) noexcept: + value{value} + {} + fnv1a32_t() = default; + /// @} + + /** + * Constructs a 32-bit FNV-1a hash value from a string. + * + * @param string String to hash. + */ + /// @{ + constexpr fnv1a32_t(const char* string) noexcept: + value{fnv1a32({string, std::string_view(string).length()})} + {} + constexpr fnv1a32_t(const wchar_t* string) noexcept: + value{fnv1a32({string, std::wstring_view(string).length()})} + {} + constexpr fnv1a32_t(const char8_t* string) noexcept: + value{fnv1a32({string, std::u8string_view(string).length()})} + {} + constexpr fnv1a32_t(const char16_t* string) noexcept: + value{fnv1a32({string, std::u16string_view(string).length()})} + {} + constexpr fnv1a32_t(const char32_t* string) noexcept: + value{fnv1a32({string, std::u32string_view(string).length()})} + {} + /// @} + + /** + * Converts a 32-bit FNV-1a hash value to its underlying type. + * + * @return 32-bit FNV-1a hash value. + */ + [[nodiscard]] inline constexpr operator value_type() const noexcept + { + return value; + } + + /// 32-bit FNV-1a hash value. + value_type value; +}; +static_assert(sizeof(fnv1a32_t) == sizeof(std::uint32_t)); + +/** + * 64-bit FNV-1a hash value. + */ +struct fnv1a64_t +{ + /// Hash value type. + using value_type = std::uint64_t; + + /** + * Constructs a 64-bit FNV-1a hash value. + */ + /// @{ + inline constexpr fnv1a64_t(value_type value) noexcept: + value{value} + {} + fnv1a64_t() = default; + /// @} + + /** + * Constructs a 64-bit FNV-1a hash value from a string. + * + * @param string String to hash. + */ + /// @{ + constexpr fnv1a64_t(const char* string) noexcept: + value{fnv1a64({string, std::string_view(string).length()})} + {} + constexpr fnv1a64_t(const wchar_t* string) noexcept: + value{fnv1a64({string, std::wstring_view(string).length()})} + {} + constexpr fnv1a64_t(const char8_t* string) noexcept: + value{fnv1a64({string, std::u8string_view(string).length()})} + {} + constexpr fnv1a64_t(const char16_t* string) noexcept: + value{fnv1a64({string, std::u16string_view(string).length()})} + {} + constexpr fnv1a64_t(const char32_t* string) noexcept: + value{fnv1a64({string, std::u32string_view(string).length()})} + {} + /// @} + + /** + * Converts a 64-bit FNV-1a hash value to its underlying type. + * + * @return 64-bit FNV-1a hash value. + */ + [[nodiscard]] inline constexpr operator value_type() const noexcept + { + return value; + } + + /// 64-bit FNV-1a hash value. + value_type value; +}; +static_assert(sizeof(fnv1a64_t) == sizeof(std::uint64_t)); + +} // namespace types + +// Bring hash::types namespace into hash namespace +using namespace hash::types; + namespace literals { /** @@ -120,27 +242,27 @@ namespace literals { /// @{ [[nodiscard]] consteval std::uint32_t operator"" _fnv1a32(const char* string, std::size_t length) noexcept { - return hash::fnv1a32(string, length); + return hash::fnv1a32({string, length}); } [[nodiscard]] consteval std::uint32_t operator"" _fnv1a32(const wchar_t* string, std::size_t length) noexcept { - return hash::fnv1a32(string, length); + return hash::fnv1a32({string, length}); } [[nodiscard]] consteval std::uint32_t operator"" _fnv1a32(const char8_t* string, std::size_t length) noexcept { - return hash::fnv1a32(string, length); + return hash::fnv1a32({string, length}); } [[nodiscard]] consteval std::uint32_t operator"" _fnv1a32(const char16_t* string, std::size_t length) noexcept { - return hash::fnv1a32(string, length); + return hash::fnv1a32({string, length}); } [[nodiscard]] consteval std::uint32_t operator"" _fnv1a32(const char32_t* string, std::size_t length) noexcept { - return hash::fnv1a32(string, length); + return hash::fnv1a32({string, length}); } /// @} @@ -155,32 +277,85 @@ namespace literals { /// @{ [[nodiscard]] consteval std::uint64_t operator"" _fnv1a64(const char* string, std::size_t length) noexcept { - return hash::fnv1a64(string, length); + return hash::fnv1a64({string, length}); } [[nodiscard]] consteval std::uint64_t operator"" _fnv1a64(const wchar_t* string, std::size_t length) noexcept { - return hash::fnv1a64(string, length); + return hash::fnv1a64({string, length}); } [[nodiscard]] consteval std::uint64_t operator"" _fnv1a64(const char8_t* string, std::size_t length) noexcept { - return hash::fnv1a64(string, length); + return hash::fnv1a64({string, length}); } [[nodiscard]] consteval std::uint64_t operator"" _fnv1a64(const char16_t* string, std::size_t length) noexcept { - return hash::fnv1a64(string, length); + return hash::fnv1a64({string, length}); } [[nodiscard]] consteval std::uint64_t operator"" _fnv1a64(const char32_t* string, std::size_t length) noexcept { - return hash::fnv1a64(string, length); + return hash::fnv1a64({string, length}); } /// @} } // namespace literals +// Bring hash::literals namespace into hash namespace +using namespace hash::literals; + } // namespace hash +/** + * Compares two FNV-1a hash values for equality. + */ +/// @{ +[[nodiscard]] inline constexpr bool operator==(const ::hash::fnv1a32_t& lhs, const ::hash::fnv1a32_t& rhs) noexcept +{ + return lhs.value == rhs.value; +} +[[nodiscard]] inline constexpr bool operator==(const ::hash::fnv1a64_t& lhs, const ::hash::fnv1a64_t& rhs) noexcept +{ + return lhs.value == rhs.value; +} +/// @} + +/** + * Checks if one FNV-1a hash value is less than another. + */ +/// @{ +[[nodiscard]] inline constexpr bool operator<(const ::hash::fnv1a32_t& lhs, const ::hash::fnv1a32_t& rhs) noexcept +{ + return lhs.value < rhs.value; +} +[[nodiscard]] inline constexpr bool operator<(const ::hash::fnv1a64_t& lhs, const ::hash::fnv1a64_t& rhs) noexcept +{ + return lhs.value < rhs.value; +} +/// @} + +namespace std { + + template <> + struct hash<::hash::fnv1a32_t> + { + [[nodiscard]] inline constexpr std::size_t operator()(const ::hash::fnv1a32_t& hash) const noexcept + { + return static_cast(hash.value); + } + }; + + template <> + struct hash<::hash::fnv1a64_t> + { + [[nodiscard]] inline constexpr std::size_t operator()(const ::hash::fnv1a64_t& hash) const noexcept + { + return static_cast(hash.value); + } + }; + +} // namespace std + #endif // ANTKEEPER_UTILITY_HASH_FNV1A_HPP diff --git a/src/engine/utility/hash/types.hpp b/src/engine/utility/hash/types.hpp new file mode 100644 index 0000000..45df8d1 --- /dev/null +++ b/src/engine/utility/hash/types.hpp @@ -0,0 +1,32 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_UTILITY_HASH_TYPES_HPP +#define ANTKEEPER_UTILITY_HASH_TYPES_HPP + +namespace hash { + +/** + * Distinct hash value types for different hash algorithms. + */ +namespace types {} + +} // namespace hash + +#endif // ANTKEEPER_UTILITY_HASH_TYPES_HPP diff --git a/src/engine/utility/image.cpp b/src/engine/utility/image.cpp new file mode 100644 index 0000000..7ec2ccc --- /dev/null +++ b/src/engine/utility/image.cpp @@ -0,0 +1,277 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +bool image::compatible(const image& other) const noexcept +{ + return (other.m_component_size == m_component_size && other.m_channel_count == m_channel_count); +} + +void image::copy +( + const image& source, + std::uint32_t w, + std::uint32_t h, + std::uint32_t from_x, + std::uint32_t from_y, + std::uint32_t to_x, + std::uint32_t to_y +) +{ + if (!compatible(source)) + { + throw std::runtime_error("Cannot copy image with mismatched format"); + } + + const std::byte* from_pixels = source.pixels.data(); + std::byte* to_pixels = pixels.data(); + + for (std::uint32_t i = 0; i < h; ++i) + { + // Calculate vertical pixel offset + std::uint32_t from_i = from_y + i; + std::uint32_t to_i = to_y + i; + + // Bounds check + if (from_i >= source.m_height || to_i >= m_height) + { + break; + } + + for (std::uint32_t j = 0; j < w; ++j) + { + // Calculate horizontal pixel offsets + std::uint32_t from_j = from_x + j; + std::uint32_t to_j = to_x + j; + + // Bounds check + if (from_j >= source.m_width || to_j >= m_width) + { + continue; + } + + // Calculate pixel data offset (in bytes) + std::size_t from_offset = (from_i * source.m_width + from_j) * m_pixel_size; + std::size_t to_offset = (to_i * m_width + to_j) * m_pixel_size; + + // Copy single pixel + std::memcpy(to_pixels + to_offset, from_pixels + from_offset, m_pixel_size); + } + } +} + +void image::format(std::size_t component_size, std::uint8_t channel_count) +{ + if (m_component_size != component_size || m_channel_count != channel_count) + { + m_component_size = component_size; + m_channel_count = channel_count; + m_pixel_size = m_component_size * m_channel_count; + pixels.resize(m_width * m_height * m_pixel_size); + } +} + +void image::resize(std::uint32_t width, std::uint32_t height) +{ + if (m_width != width || m_height == height) + { + m_width = width; + m_height = height; + pixels.resize(m_width * m_height * m_pixel_size); + } +} + +static void deserialize_tinyexr(image& image, deserialize_context& ctx) +{ + const char* error = nullptr; + + // Read data into file buffer + std::vector file_buffer(ctx.size()); + ctx.read8(reinterpret_cast(file_buffer.data()), file_buffer.size()); + + // Read EXR version + EXRVersion exr_version; + if (int status = ParseEXRVersionFromMemory(&exr_version, file_buffer.data(), file_buffer.size()); status != TINYEXR_SUCCESS) + { + throw deserialize_error(std::format("TinyEXR version parse error {}", status)); + } + + // Check if image is multipart + if (exr_version.multipart) + { + throw deserialize_error("OpenEXR multipart images not supported"); + } + + // Init and read EXR header data + EXRHeader exr_header; + InitEXRHeader(&exr_header); + if (int status = ParseEXRHeaderFromMemory(&exr_header, &exr_version, file_buffer.data(), file_buffer.size(), &error); status != TINYEXR_SUCCESS) + { + const std::string error_message(error); + FreeEXRErrorMessage(error); + throw deserialize_error(error_message); + } + + // Check if image is tiled + if (exr_header.tiled) + { + FreeEXRHeader(&exr_header); + throw deserialize_error("OpenEXR tiled images not supported"); + } + + // Read half channels as float + for (int i = 0; i < exr_header.num_channels; ++i) + { + if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) + { + exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; + } + } + + // Init and read EXR image data + EXRImage exr_image; + InitEXRImage(&exr_image); + if (int status = LoadEXRImageFromMemory(&exr_image, &exr_header, file_buffer.data(), file_buffer.size(), &error); status != TINYEXR_SUCCESS) + { + const std::string error_message(error); + FreeEXRErrorMessage(error); + FreeEXRHeader(&exr_header); + throw deserialize_error(error_message); + } + + // Free file buffer + file_buffer.clear(); + + // Format and resize image + image.format(sizeof(float), static_cast(exr_image.num_channels)); + image.resize(static_cast(exr_image.width), static_cast(exr_image.height)); + + // Fill image pixels + float* component = reinterpret_cast(image.data()); + for (int y = exr_image.height - 1; y >= 0; --y) + { + int row_offset = y * exr_image.width; + + for (int x = 0; x < exr_image.width; ++x) + { + int pixel_index = row_offset + x; + + for (int c = exr_image.num_channels - 1; c >= 0; --c) + { + *(component++) = reinterpret_cast(exr_image.images)[c][pixel_index]; + } + } + } + + // Free EXR image and header data + FreeEXRImage(&exr_image); + FreeEXRHeader(&exr_header); +} + +static int stb_io_read(void* user, char* data, int size) +{ + deserialize_context& ctx = *static_cast(user); + return static_cast(ctx.read8(reinterpret_cast(data), static_cast(size))); +} + +static void stb_io_skip(void* user, int n) +{ + deserialize_context& ctx = *static_cast(user); + ctx.seek(ctx.tell() + n); +} + +static int stb_io_eof(void* user) +{ + deserialize_context& ctx = *static_cast(user); + return static_cast(ctx.eof()); +} + +static void deserialize_stb_image(image& image, deserialize_context& ctx) +{ + // Set vertical flip on load in order to upload pixels correctly to OpenGL + stbi_set_flip_vertically_on_load(true); + + // Setup IO callbacks + const stbi_io_callbacks io_callbacks + { + &stb_io_read, + &stb_io_skip, + &stb_io_eof + }; + + // Load image data + int width = 0; + int height = 0; + int channels = 0; + stbi_uc* pixels = stbi_load_from_callbacks(&io_callbacks, &ctx, &width, &height, &channels, 0); + if (!pixels) + { + throw deserialize_error(stbi_failure_reason()); + } + + // Create image + image.format(sizeof(stbi_uc), static_cast(channels)); + image.resize(static_cast(width), static_cast(height)); + std::memcpy(image.data(), pixels, image.size()); + + // Free loaded image data + stbi_image_free(pixels); +} + +/** + * Deserializes an image. + * + * @param[out] image Image to deserialize. + * @param[in,out] ctx Deserialize context. + * + * @throw deserialize_error Read error. + */ +template <> +void deserializer::deserialize(image& image, deserialize_context& ctx) +{ + // Select loader according to file extension + if (ctx.path().extension() == ".exr") + { + // Deserialize EXR images with TinyEXR + deserialize_tinyexr(image, ctx); + } + else + { + // Deserialize other image formats with stb_image + deserialize_stb_image(image, ctx); + } +} + +template <> +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) +{ + auto resource = std::make_unique(); + + deserializer().deserialize(*resource, ctx); + + return resource; +} diff --git a/src/engine/resources/image.hpp b/src/engine/utility/image.hpp similarity index 62% rename from src/engine/resources/image.hpp rename to src/engine/utility/image.hpp index 1c2e470..e2abd47 100644 --- a/src/engine/resources/image.hpp +++ b/src/engine/utility/image.hpp @@ -17,12 +17,13 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_IMAGE_HPP -#define ANTKEEPER_IMAGE_HPP +#ifndef ANTKEEPER_UTILITY_IMAGE_HPP +#define ANTKEEPER_UTILITY_IMAGE_HPP #include -#include +#include #include +#include /** * Stores basic image data. @@ -30,26 +31,6 @@ class image { public: - /** - * Creates a copy of another image. - * - * @param source Image from which to copy. - */ - image(const image& source); - - /// Creates an image. - image(); - - /// Destroys an image. - ~image(); - - /** - * Makes this image a copy of another image. - * - * @param source Image from which to copy. - */ - image& operator=(const image& source); - /** * Returns an iterator to the first pixel. * @@ -57,25 +38,25 @@ public: */ /// @{ template - inline constexpr T* begin() noexcept + [[nodiscard]] inline constexpr T* begin() noexcept { static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); - return static_cast(pixels); + return reinterpret_cast(pixels.data()); } template - inline constexpr const T* begin() const noexcept + [[nodiscard]] inline constexpr const T* begin() const noexcept { static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); - return static_cast(pixels); + return reinterpret_cast(pixels.data()); } template - inline constexpr const T* cbegin() const noexcept + [[nodiscard]] inline constexpr const T* cbegin() const noexcept { static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); - return static_cast(pixels); + return reinterpret_cast(pixels.data()); } /// @} @@ -86,25 +67,25 @@ public: */ /// @{ template - inline constexpr T* end() noexcept + [[nodiscard]] inline constexpr T* end() noexcept { static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); - return reinterpret_cast(static_cast(pixels) + size); + return reinterpret_cast(pixels.data() + pixels.size()); } template - inline constexpr const T* end() const noexcept + [[nodiscard]] inline constexpr const T* end() const noexcept { static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); - return reinterpret_cast(static_cast(pixels) + size); + return reinterpret_cast(pixels.data() + pixels.size()); } template - inline constexpr const T* cend() const noexcept + [[nodiscard]] inline constexpr const T* cend() const noexcept { static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); - return reinterpret_cast(static_cast(pixels) + size); + return reinterpret_cast(pixels.data() + pixels.size()); } /// @} @@ -114,7 +95,7 @@ public: * @param other Image for with which to compare compatibility. * @return `true` if the image formats are compatible, `false` otherwise. */ - bool compatible(const image& other) const; + [[nodiscard]] bool compatible(const image& other) const noexcept; /** * Copies pixel data from another image with a compatible format into this image. @@ -131,7 +112,16 @@ public: * * @see image::compatible(const image&) const */ - void copy(const image& source, unsigned int w, unsigned int h, unsigned int from_x = 0, int unsigned from_y = 0, unsigned int to_x = 0, unsigned int to_y = 0); + void copy + ( + const image& source, + std::uint32_t w, + std::uint32_t h, + std::uint32_t from_x = 0, + std::uint32_t from_y = 0, + std::uint32_t to_x = 0, + std::uint32_t to_y = 0 + ); /** * Changes the format of the image. Existing pixel data will be erased if the format has changed. @@ -139,7 +129,7 @@ public: * @param component_size Size of channel components, in bytes. * @param channel_count Number of channels in the image. */ - void format(std::size_t component_size, std::size_t channel_count); + void format(std::size_t component_size, std::uint8_t channel_count); /** * Resizes the image. Existing pixel data will be erased if the size has changed. @@ -147,83 +137,63 @@ public: * @param width New width of the image, in pixels. * @param height New height of the image, in pixels. */ - void resize(unsigned int width, unsigned int height); + void resize(std::uint32_t width, std::uint32_t height); /// Returns the width of the image, in pixels. - unsigned int get_width() const; + [[nodiscard]] inline std::uint32_t width() const noexcept + { + return m_width; + } /// Returns the height of the image, in pixels. - unsigned int get_height() const; - - /// Returns the size of channel components, in bytes. - std::size_t get_component_size() const; + [[nodiscard]] inline std::uint32_t height() const noexcept + { + return m_height; + } /// Returns the number of color channels in the image. - std::size_t get_channel_count() const; + [[nodiscard]] inline std::uint8_t channel_count() const noexcept + { + return m_channel_count; + } /// Returns a pointer to the pixel data. /// @{ - void* data() noexcept; - const void* data() const noexcept; + [[nodiscard]] inline const std::byte* data() const noexcept + { + return pixels.data(); + } + [[nodiscard]] inline std::byte* data() noexcept + { + return pixels.data(); + } /// @} + /// Returns the size of channel components, in bytes. + [[nodiscard]] inline std::size_t component_size() const noexcept + { + return m_component_size; + } + /// Returns the size of a single pixel, in bytes. - std::size_t get_pixel_size() const; + [[nodiscard]] inline std::size_t pixel_size() const noexcept + { + return m_pixel_size; + } /// Returns the size of the image, in bytes. - std::size_t get_size() const; - + [[nodiscard]] inline std::size_t size() const noexcept + { + return pixels.size(); + } + private: - void allocate_pixels(); - void free_pixels(); - - unsigned int width; - unsigned int height; - std::size_t component_size; - std::size_t channel_count; - void* pixels; - std::size_t pixel_size; - std::size_t size; + std::uint32_t m_width{0}; + std::uint32_t m_height{0}; + std::uint8_t m_channel_count{0}; + std::size_t m_component_size{0}; + std::size_t m_pixel_size{0}; + std::vector pixels; }; -inline unsigned int image::get_width() const -{ - return width; -} - -inline unsigned int image::get_height() const -{ - return height; -} - -inline std::size_t image::get_component_size() const -{ - return component_size; -} - -inline std::size_t image::get_channel_count() const -{ - return channel_count; -} - -inline void* image::data() noexcept -{ - return pixels; -} - -inline const void* image::data() const noexcept -{ - return pixels; -} - -inline std::size_t image::get_pixel_size() const -{ - return pixel_size; -} - -inline std::size_t image::get_size() const -{ - return size; -} - -#endif // ANTKEEPER_IMAGE_HPP +#endif // ANTKEEPER_UTILITY_IMAGE_HPP diff --git a/src/engine/resources/string-map-loader.cpp b/src/engine/utility/json.cpp similarity index 59% rename from src/engine/resources/string-map-loader.cpp rename to src/engine/utility/json.cpp index 4f15969..5f88530 100644 --- a/src/engine/resources/string-map-loader.cpp +++ b/src/engine/utility/json.cpp @@ -17,25 +17,27 @@ * along with Antkeeper source code. If not, see . */ +#include #include -#include #include -#include template <> -i18n::string_map* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +void deserializer::deserialize(::json& json, deserialize_context& ctx) { - i18n::string_map* map = new i18n::string_map(); + // Read file into buffer + std::string file_buffer(ctx.size(), '\0'); + ctx.read8(reinterpret_cast(file_buffer.data()), ctx.size()); - deserialize_context ctx(file); - deserializer().deserialize(*map, ctx); - - return map; + // Parse JSON from file buffer + json = ::json::parse(file_buffer, nullptr, true, true); } template <> -void resource_loader::save(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path, const i18n::string_map* map) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { - serialize_context ctx(file); - serializer().serialize(*map, ctx); + auto resource = std::make_unique(); + + deserializer().deserialize(*resource, ctx); + + return resource; } diff --git a/src/engine/resources/json.hpp b/src/engine/utility/json.hpp similarity index 86% rename from src/engine/resources/json.hpp rename to src/engine/utility/json.hpp index 75b2474..93ec716 100644 --- a/src/engine/resources/json.hpp +++ b/src/engine/utility/json.hpp @@ -17,12 +17,12 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_RESOURCES_JSON_HPP -#define ANTKEEPER_RESOURCES_JSON_HPP +#ifndef ANTKEEPER_UTILITY_JSON_HPP +#define ANTKEEPER_UTILITY_JSON_HPP #include -/// JSON data +/// JSON data. using json = nlohmann::json; -#endif // ANTKEEPER_RESOURCES_JSON_HPP +#endif // ANTKEEPER_UTILITY_JSON_HPP diff --git a/src/engine/resources/dict-loader.cpp b/src/engine/utility/text-file.cpp similarity index 50% rename from src/engine/resources/dict-loader.cpp rename to src/engine/utility/text-file.cpp index 2585ee0..078b53d 100644 --- a/src/engine/resources/dict-loader.cpp +++ b/src/engine/utility/text-file.cpp @@ -17,27 +17,50 @@ * along with Antkeeper source code. If not, see . */ -#include -#include +#include #include -#include -#include -#include +#include +/** + * Deserializes a text file. + * + * @param[out] file Text file to deserialize. + * @param[in,out] ctx Deserialize context. + * + * @throw deserialize_error Read error. + */ template <> -dict* resource_loader>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +void deserializer::deserialize(text_file& file, deserialize_context& ctx) { - dict* dict = new ::dict(); + file.lines.resize(1); + std::string* line = &file.lines.front(); + line->clear(); - deserialize_context ctx(file); - deserializer<::dict>().deserialize(*dict, ctx); + char c; + while (ctx.read8(reinterpret_cast(&c), 1) == 1) + { + if (c == '\n') + { + line = &file.lines.emplace_back(std::string()); + } + else if (c != '\r') + { + line->append(1, c); + } + } - return dict; + if (line->empty()) + { + file.lines.pop_back(); + } } template <> -void resource_loader>::save(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path, const dict* dict) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { - serialize_context ctx(file); - serializer<::dict>().serialize(*dict, ctx); + auto resource = std::make_unique(); + + deserializer().deserialize(*resource, ctx); + + return resource; } diff --git a/src/engine/resources/text-file.hpp b/src/engine/utility/text-file.hpp similarity index 78% rename from src/engine/resources/text-file.hpp rename to src/engine/utility/text-file.hpp index c9179ac..0af1078 100644 --- a/src/engine/resources/text-file.hpp +++ b/src/engine/utility/text-file.hpp @@ -17,13 +17,19 @@ * along with Antkeeper source code. If not, see . */ -#ifndef TEXT_FILE_HPP -#define TEXT_FILE_HPP +#ifndef ANTKEEPER_UTILITY_TEXT_FILE_HPP +#define ANTKEEPER_UTILITY_TEXT_FILE_HPP #include #include -typedef std::vector text_file; - -#endif // TEXT_FILE_HPP +/** + * Virtual text file. + */ +struct text_file +{ + /// Text file lines. + std::vector lines; +}; +#endif // ANTKEEPER_UTILITY_TEXT_FILE_HPP diff --git a/src/game/ant/caste.hpp b/src/game/ant/ant-caste.hpp similarity index 84% rename from src/game/ant/caste.hpp rename to src/game/ant/ant-caste.hpp index 5d47ca2..d424a0e 100644 --- a/src/game/ant/caste.hpp +++ b/src/game/ant/ant-caste.hpp @@ -22,28 +22,24 @@ #include -namespace ant { - /** - * Ant caste enumerations. + * Ant caste types. * * @see https://www.antwiki.org/wiki/Caste_Terminology */ -enum class caste: std::uint8_t +enum class ant_caste: std::uint8_t { - /// Queen caste type. + /// Queen caste. queen, - /// Worker caste type. + /// Worker caste. worker, - /// Soldier caste type. + /// Soldier caste. soldier, - /// Male caste type. + /// Male caste. male }; -} // namespace ant - #endif // ANTKEEPER_GAME_ANT_CASTE_HPP diff --git a/src/game/ant/cladogenesis.cpp b/src/game/ant/ant-cladogenesis.cpp similarity index 89% rename from src/game/ant/cladogenesis.cpp rename to src/game/ant/ant-cladogenesis.cpp index 3c3b700..1a6970e 100644 --- a/src/game/ant/cladogenesis.cpp +++ b/src/game/ant/ant-cladogenesis.cpp @@ -17,14 +17,12 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/cladogenesis.hpp" +#include "game/ant/ant-cladogenesis.hpp" -namespace ant { - -genome* cladogenesis(const gene_pool& pool, std::random_device& rng) +std::unique_ptr ant_cladogenesis(const ant_gene_pool& pool, std::random_device& rng) { // Allocate genome - ant::genome* genome = new ant::genome(); + std::unique_ptr genome = std::make_unique(); // Randomly sample genes genome->antennae = pool.antennae.sample(rng); @@ -52,5 +50,3 @@ genome* cladogenesis(const gene_pool& pool, std::random_device& rng) return genome; } - -} // namespace ant diff --git a/src/game/ant/cladogenesis.hpp b/src/game/ant/ant-cladogenesis.hpp similarity index 85% rename from src/game/ant/cladogenesis.hpp rename to src/game/ant/ant-cladogenesis.hpp index 510a0f0..b374ae3 100644 --- a/src/game/ant/cladogenesis.hpp +++ b/src/game/ant/ant-cladogenesis.hpp @@ -20,12 +20,11 @@ #ifndef ANTKEEPER_GAME_ANT_CLADOGENESIS_HPP #define ANTKEEPER_GAME_ANT_CLADOGENESIS_HPP -#include "game/ant/genome.hpp" -#include "game/ant/gene-pool.hpp" +#include "game/ant/ant-genome.hpp" +#include "game/ant/ant-gene-pool.hpp" +#include #include -namespace ant { - /** * Generates a genome from a gene pool. * @@ -34,8 +33,6 @@ namespace ant { * * @return New genome. */ -genome* cladogenesis(const gene_pool& pool, std::random_device& rng); - -} // namespace ant +std::unique_ptr ant_cladogenesis(const ant_gene_pool& pool, std::random_device& rng); #endif // ANTKEEPER_GAME_ANT_CLADOGENESIS_HPP diff --git a/src/game/ant/gene-frequency-table.hpp b/src/game/ant/ant-gene-frequency-table.hpp similarity index 88% rename from src/game/ant/gene-frequency-table.hpp rename to src/game/ant/ant-gene-frequency-table.hpp index fe92f8c..76acab9 100644 --- a/src/game/ant/gene-frequency-table.hpp +++ b/src/game/ant/ant-gene-frequency-table.hpp @@ -22,19 +22,18 @@ #include #include - -namespace ant { +#include /** - * Gene frequency table. + * Ant gene frequency table. * * @tparam T Gene type. */ template -struct gene_frequency_table +struct ant_gene_frequency_table { /// Gene array. - std::vector genes; + std::vector> genes; /// Weight array std::vector weights; @@ -49,16 +48,17 @@ struct gene_frequency_table * @return Randomly sampled gene. */ template - const T* sample(Generator& g) const + [[nodiscard]] std::shared_ptr sample(Generator& g) const { if (genes.empty()) + { return nullptr; + } std::discrete_distribution distribution(weights.begin(), weights.end()); + return genes[distribution(g)]; } }; -} // namespace ant - #endif // ANTKEEPER_GAME_ANT_GENE_FREQUENCY_TABLE_HPP diff --git a/src/game/ant/ant-gene-pool.hpp b/src/game/ant/ant-gene-pool.hpp new file mode 100644 index 0000000..e113123 --- /dev/null +++ b/src/game/ant/ant-gene-pool.hpp @@ -0,0 +1,79 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_GAME_ANT_GENE_POOL_HPP +#define ANTKEEPER_GAME_ANT_GENE_POOL_HPP + +#include "game/ant/ant-gene-frequency-table.hpp" +#include "game/ant/genes/ant-antennae-gene.hpp" +#include "game/ant/genes/ant-body-size-gene.hpp" +#include "game/ant/genes/ant-cocoon-gene.hpp" +#include "game/ant/genes/ant-diet-gene.hpp" +#include "game/ant/genes/ant-egg-gene.hpp" +#include "game/ant/genes/ant-eyes-gene.hpp" +#include "game/ant/genes/ant-foraging-time-gene.hpp" +#include "game/ant/genes/ant-founding-mode-gene.hpp" +#include "game/ant/genes/ant-gaster-gene.hpp" +#include "game/ant/genes/ant-head-gene.hpp" +#include "game/ant/genes/ant-larva-gene.hpp" +#include "game/ant/genes/ant-legs-gene.hpp" +#include "game/ant/genes/ant-mandibles-gene.hpp" +#include "game/ant/genes/ant-mesosoma-gene.hpp" +#include "game/ant/genes/ant-nest-site-gene.hpp" +#include "game/ant/genes/ant-ocelli-gene.hpp" +#include "game/ant/genes/ant-pigmentation-gene.hpp" +#include "game/ant/genes/ant-pilosity-gene.hpp" +#include "game/ant/genes/ant-sculpturing-gene.hpp" +#include "game/ant/genes/ant-sting-gene.hpp" +#include "game/ant/genes/ant-waist-gene.hpp" +#include "game/ant/genes/ant-wings-gene.hpp" + +/** + * Pool of ant genes from which ant genomes can be generated. + */ +struct ant_gene_pool +{ + /// Gene pool name. + std::string name; + + ant_gene_frequency_table antennae; + ant_gene_frequency_table body_size; + ant_gene_frequency_table cocoon; + ant_gene_frequency_table diet; + ant_gene_frequency_table egg; + ant_gene_frequency_table eyes; + ant_gene_frequency_table foraging_time; + ant_gene_frequency_table founding_mode; + ant_gene_frequency_table gaster; + ant_gene_frequency_table head; + ant_gene_frequency_table larva; + ant_gene_frequency_table legs; + ant_gene_frequency_table mandibles; + ant_gene_frequency_table mesosoma; + ant_gene_frequency_table nest_site; + ant_gene_frequency_table ocelli; + ant_gene_frequency_table pigmentation; + ant_gene_frequency_table pilosity; + ant_gene_frequency_table sculpturing; + ant_gene_frequency_table sting; + ant_gene_frequency_table waist; + ant_gene_frequency_table wings; +}; + +#endif // ANTKEEPER_GAME_ANT_GENE_POOL_HPP diff --git a/src/engine/resources/resource-handle.cpp b/src/game/ant/ant-genome.cpp similarity index 86% rename from src/engine/resources/resource-handle.cpp rename to src/game/ant/ant-genome.cpp index 80b9afe..d841cc5 100644 --- a/src/engine/resources/resource-handle.cpp +++ b/src/game/ant/ant-genome.cpp @@ -17,9 +17,4 @@ * along with Antkeeper source code. If not, see . */ -#include - -resource_handle_base::resource_handle_base(): - reference_count(0) -{} - +#include "game/ant/ant-genome.hpp" diff --git a/src/game/ant/ant-genome.hpp b/src/game/ant/ant-genome.hpp new file mode 100644 index 0000000..91e989d --- /dev/null +++ b/src/game/ant/ant-genome.hpp @@ -0,0 +1,75 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_GAME_ANT_GENOME_HPP +#define ANTKEEPER_GAME_ANT_GENOME_HPP + +#include "game/ant/genes/ant-antennae-gene.hpp" +#include "game/ant/genes/ant-body-size-gene.hpp" +#include "game/ant/genes/ant-cocoon-gene.hpp" +#include "game/ant/genes/ant-diet-gene.hpp" +#include "game/ant/genes/ant-egg-gene.hpp" +#include "game/ant/genes/ant-eyes-gene.hpp" +#include "game/ant/genes/ant-foraging-time-gene.hpp" +#include "game/ant/genes/ant-founding-mode-gene.hpp" +#include "game/ant/genes/ant-gaster-gene.hpp" +#include "game/ant/genes/ant-head-gene.hpp" +#include "game/ant/genes/ant-larva-gene.hpp" +#include "game/ant/genes/ant-legs-gene.hpp" +#include "game/ant/genes/ant-mandibles-gene.hpp" +#include "game/ant/genes/ant-mesosoma-gene.hpp" +#include "game/ant/genes/ant-nest-site-gene.hpp" +#include "game/ant/genes/ant-ocelli-gene.hpp" +#include "game/ant/genes/ant-pigmentation-gene.hpp" +#include "game/ant/genes/ant-pilosity-gene.hpp" +#include "game/ant/genes/ant-sculpturing-gene.hpp" +#include "game/ant/genes/ant-sting-gene.hpp" +#include "game/ant/genes/ant-waist-gene.hpp" +#include "game/ant/genes/ant-wings-gene.hpp" + +/** + * Complete set of ant genes. + */ +struct ant_genome +{ + std::shared_ptr antennae; + std::shared_ptr body_size; + std::shared_ptr cocoon; + std::shared_ptr diet; + std::shared_ptr egg; + std::shared_ptr eyes; + std::shared_ptr foraging_time; + std::shared_ptr founding_mode; + std::shared_ptr gaster; + std::shared_ptr head; + std::shared_ptr larva; + std::shared_ptr legs; + std::shared_ptr mandibles; + std::shared_ptr mesosoma; + std::shared_ptr nest_site; + std::shared_ptr ocelli; + std::shared_ptr pigmentation; + std::shared_ptr pilosity; + std::shared_ptr sculpturing; + std::shared_ptr sting; + std::shared_ptr waist; + std::shared_ptr wings; +}; + +#endif // ANTKEEPER_GAME_ANT_GENOME_HPP diff --git a/src/game/ant/morphogenesis.cpp b/src/game/ant/ant-morphogenesis.cpp similarity index 67% rename from src/game/ant/morphogenesis.cpp rename to src/game/ant/ant-morphogenesis.cpp index 3e797b4..7d5096a 100644 --- a/src/game/ant/morphogenesis.cpp +++ b/src/game/ant/ant-morphogenesis.cpp @@ -1,975 +1,969 @@ -/* - * 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 . - */ - -#include "game/ant/morphogenesis.hpp" -#include -#include -#include -#include -#include - -namespace ant { - -static render::material* build_exoskeleton_material -( - const phene::pigmentation& pigmentation, - const phene::sculpturing& sculpturing -); -static void reskin_vertices -( - std::uint8_t* vertex_data, - std::size_t index_count, - const gl::vertex_attribute& position_attribute, - const gl::vertex_attribute& normal_attribute, - const gl::vertex_attribute& tangent_attribute, - const gl::vertex_attribute& bone_index_attribute, - const std::unordered_set& old_bone_indices, - std::uint8_t new_bone_index, - const math::transform& transform -); -static geom::aabb calculate_bounds(std::uint8_t* vertex_data, std::size_t index_count, const gl::vertex_attribute& position_attribute); -static render::model* build_model -( - render::material* material, - const render::model* antennae, - const render::model* eyes, - const render::model* forewings, - const render::model* gaster, - const render::model* head, - const render::model* hindwings, - const render::model* legs, - const render::model* mandibles, - const render::model* mesosoma, - const render::model* lateral_ocelli, - const render::model* median_ocellus, - const render::model* sting, - const render::model* waist -); - -render::material* build_exoskeleton_material -( - const phene::pigmentation& pigmentation, - const phene::sculpturing& sculpturing -) -{ - // Allocate copy of pigmentation material - render::material* exoskeleton_material = new render::material(*pigmentation.material); - - // Adjust roughness parameter - if (render::material_property_base* property = exoskeleton_material->get_property("roughness"); property != nullptr) - { - static_cast*>(property)->set_value(sculpturing.roughness); - } - else - { - exoskeleton_material->add_property("roughness")->set_value(sculpturing.roughness); - } - - // Adjust normal map parameter - if (render::material_property_base* property = exoskeleton_material->get_property("normal_map"); property != nullptr) - { - static_cast*>(property)->set_value(sculpturing.normal_map); - } - else - { - exoskeleton_material->add_property("normal_map")->set_value(sculpturing.normal_map); - } - - return exoskeleton_material; -} - -render::model* morphogenesis(const phenome& phenome) -{ - // Build exoskeleton material - render::material* exoskeleton_material = build_exoskeleton_material(*phenome.pigmentation, *phenome.sculpturing); - - // Determine presence of optional parts - bool eyes_present = phenome.eyes->present; - bool lateral_ocelli_present = phenome.ocelli->lateral_ocelli_present; - bool median_ocellus_present = phenome.ocelli->median_ocellus_present; - bool petiole_present = phenome.waist->petiole_present; - bool postpetiole_present = phenome.waist->postpetiole_present; - bool sting_present = phenome.sting->present; - bool wings_present = phenome.wings->present; - - // Get body part models - render::model* antennae_model = phenome.antennae->model; - render::model* eyes_model = phenome.eyes->model; - render::model* forewings_model = phenome.wings->forewings_model; - render::model* gaster_model = phenome.gaster->model; - render::model* head_model = phenome.head->model; - render::model* hindwings_model = phenome.wings->hindwings_model; - render::model* lateral_ocelli_model = phenome.ocelli->lateral_ocelli_model; - render::model* legs_model = phenome.legs->model; - render::model* mandibles_model = phenome.mandibles->model; - render::model* median_ocellus_model = phenome.ocelli->median_ocellus_model; - render::model* mesosoma_model = phenome.mesosoma->model; - render::model* sting_model = phenome.sting->model; - render::model* waist_model = phenome.waist->model; - - // Get body part vertex buffers - const gl::vertex_buffer* antennae_vbo = antennae_model->get_vertex_buffer(); - const gl::vertex_buffer* eyes_vbo = (eyes_present) ? eyes_model->get_vertex_buffer() : nullptr; - const gl::vertex_buffer* forewings_vbo = (wings_present) ? forewings_model->get_vertex_buffer() : nullptr; - const gl::vertex_buffer* gaster_vbo = gaster_model->get_vertex_buffer(); - const gl::vertex_buffer* head_vbo = head_model->get_vertex_buffer(); - const gl::vertex_buffer* hindwings_vbo = (wings_present) ? hindwings_model->get_vertex_buffer() : nullptr; - const gl::vertex_buffer* lateral_ocelli_vbo = (lateral_ocelli_model) ? lateral_ocelli_model->get_vertex_buffer() : nullptr; - const gl::vertex_buffer* legs_vbo = legs_model->get_vertex_buffer(); - const gl::vertex_buffer* mandibles_vbo = mandibles_model->get_vertex_buffer(); - const gl::vertex_buffer* median_ocellus_vbo = (median_ocellus_model) ? median_ocellus_model->get_vertex_buffer() : nullptr; - const gl::vertex_buffer* mesosoma_vbo = mesosoma_model->get_vertex_buffer(); - const gl::vertex_buffer* sting_vbo = (sting_present) ? sting_model->get_vertex_buffer() : nullptr; - const gl::vertex_buffer* waist_vbo = waist_model->get_vertex_buffer(); - - // Determine combined size of vertex buffers and save offsets - std::size_t vertex_buffer_size = 0; - std::size_t mesosoma_vbo_offset = vertex_buffer_size; - vertex_buffer_size += mesosoma_vbo->get_size(); - std::size_t legs_vbo_offset = vertex_buffer_size; - vertex_buffer_size += legs_vbo->get_size(); - std::size_t head_vbo_offset = vertex_buffer_size; - vertex_buffer_size += head_vbo->get_size(); - std::size_t mandibles_vbo_offset = vertex_buffer_size; - vertex_buffer_size += mandibles_vbo->get_size(); - std::size_t antennae_vbo_offset = vertex_buffer_size; - vertex_buffer_size += antennae_vbo->get_size(); - std::size_t waist_vbo_offset = vertex_buffer_size; - vertex_buffer_size += waist_vbo->get_size(); - std::size_t gaster_vbo_offset = vertex_buffer_size; - vertex_buffer_size += gaster_vbo->get_size(); - std::size_t sting_vbo_offset = vertex_buffer_size; - if (sting_present) - vertex_buffer_size += sting_vbo->get_size(); - std::size_t eyes_vbo_offset = vertex_buffer_size; - if (eyes_present) - vertex_buffer_size += eyes_vbo->get_size(); - std::size_t lateral_ocelli_vbo_offset = vertex_buffer_size; - if (lateral_ocelli_present) - vertex_buffer_size += lateral_ocelli_vbo->get_size(); - std::size_t median_ocellus_vbo_offset = vertex_buffer_size; - if (median_ocellus_present) - vertex_buffer_size += median_ocellus_vbo->get_size(); - std::size_t forewings_vbo_offset = vertex_buffer_size; - if (wings_present) - vertex_buffer_size += forewings_vbo->get_size(); - std::size_t hindwings_vbo_offset = vertex_buffer_size; - if (wings_present) - vertex_buffer_size += hindwings_vbo->get_size(); - - // Allocate combined vertex buffer data - std::uint8_t* vertex_buffer_data = new std::uint8_t[vertex_buffer_size]; - - // Read body part vertex buffer data into combined vertex buffer data - mesosoma_vbo->read(0, mesosoma_vbo->get_size(), vertex_buffer_data + mesosoma_vbo_offset); - legs_vbo->read(0, legs_vbo->get_size(), vertex_buffer_data + legs_vbo_offset); - head_vbo->read(0, head_vbo->get_size(), vertex_buffer_data + head_vbo_offset); - mandibles_vbo->read(0, mandibles_vbo->get_size(), vertex_buffer_data + mandibles_vbo_offset); - antennae_vbo->read(0, antennae_vbo->get_size(), vertex_buffer_data + antennae_vbo_offset); - waist_vbo->read(0, waist_vbo->get_size(), vertex_buffer_data + waist_vbo_offset); - gaster_vbo->read(0, gaster_vbo->get_size(), vertex_buffer_data + gaster_vbo_offset); - if (sting_present) - sting_vbo->read(0, sting_vbo->get_size(), vertex_buffer_data + sting_vbo_offset); - if (eyes_present) - eyes_vbo->read(0, eyes_vbo->get_size(), vertex_buffer_data + eyes_vbo_offset); - if (lateral_ocelli_present) - lateral_ocelli_vbo->read(0, lateral_ocelli_vbo->get_size(), vertex_buffer_data + lateral_ocelli_vbo_offset); - if (median_ocellus_present) - median_ocellus_vbo->read(0, median_ocellus_vbo->get_size(), vertex_buffer_data + median_ocellus_vbo_offset); - if (wings_present) - { - forewings_vbo->read(0, forewings_vbo->get_size(), vertex_buffer_data + forewings_vbo_offset); - hindwings_vbo->read(0, hindwings_vbo->get_size(), vertex_buffer_data + hindwings_vbo_offset); - } - - // Allocate model - render::model* model = new render::model(); - - // Setup model VAO - gl::vertex_array* model_vao = model->get_vertex_array(); - for (auto [location, attribute]: mesosoma_model->get_vertex_array()->get_attributes()) - { - attribute.buffer = model->get_vertex_buffer(); - model_vao->bind(location, attribute); - } - - // Get vertex attributes - const gl::vertex_attribute* position_attribute = nullptr; - const gl::vertex_attribute* normal_attribute = nullptr; - const gl::vertex_attribute* tangent_attribute = nullptr; - const gl::vertex_attribute* bone_index_attribute = nullptr; - const auto& vertex_attribute_map = model_vao->get_attributes(); - if (auto it = vertex_attribute_map.find(render::vertex_attribute::position); it != vertex_attribute_map.end()) - position_attribute = &it->second; - if (auto it = vertex_attribute_map.find(render::vertex_attribute::normal); it != vertex_attribute_map.end()) - normal_attribute = &it->second; - if (auto it = vertex_attribute_map.find(render::vertex_attribute::tangent); it != vertex_attribute_map.end()) - tangent_attribute = &it->second; - if (auto it = vertex_attribute_map.find(render::vertex_attribute::bone_index); it != vertex_attribute_map.end()) - bone_index_attribute = &it->second; - - // Get body part skeletons - const ::skeleton& mesosoma_skeleton = mesosoma_model->get_skeleton(); - const ::skeleton& legs_skeleton = legs_model->get_skeleton(); - const ::skeleton& head_skeleton = head_model->get_skeleton(); - const ::skeleton& mandibles_skeleton = mandibles_model->get_skeleton(); - const ::skeleton& antennae_skeleton = antennae_model->get_skeleton(); - const ::skeleton& waist_skeleton = waist_model->get_skeleton(); - const ::skeleton& gaster_skeleton = gaster_model->get_skeleton(); - const ::skeleton* sting_skeleton = (sting_present) ? &sting_model->get_skeleton() : nullptr; - const ::skeleton* eyes_skeleton = (eyes_present) ? &eyes_model->get_skeleton() : nullptr; - const ::skeleton* lateral_ocelli_skeleton = (lateral_ocelli_present) ? &lateral_ocelli_model->get_skeleton() : nullptr; - const ::skeleton* median_ocellus_skeleton = (median_ocellus_present) ? &median_ocellus_model->get_skeleton() : nullptr; - - // Allocate skeleton bones - ::skeleton& skeleton = model->get_skeleton(); - std::size_t bone_count = 33; - if (petiole_present) - bone_count += 1; - if (postpetiole_present) - bone_count += 1; - if (sting_present) - bone_count += 1; - if (wings_present) - bone_count += 4; - - // Assign bone indices - std::uint8_t bone_index = 0; - std::uint8_t mesosoma_bone_index = bone_index++; - std::uint8_t procoxa_l_bone_index = bone_index++; - std::uint8_t procoxa_r_bone_index = bone_index++; - std::uint8_t profemur_l_bone_index = bone_index++; - std::uint8_t profemur_r_bone_index = bone_index++; - std::uint8_t protibia_l_bone_index = bone_index++; - std::uint8_t protibia_r_bone_index = bone_index++; - std::uint8_t protarsus_l_bone_index = bone_index++; - std::uint8_t protarsus_r_bone_index = bone_index++; - std::uint8_t mesocoxa_l_bone_index = bone_index++; - std::uint8_t mesocoxa_r_bone_index = bone_index++; - std::uint8_t mesofemur_l_bone_index = bone_index++; - std::uint8_t mesofemur_r_bone_index = bone_index++; - std::uint8_t mesotibia_l_bone_index = bone_index++; - std::uint8_t mesotibia_r_bone_index = bone_index++; - std::uint8_t mesotarsus_l_bone_index = bone_index++; - std::uint8_t mesotarsus_r_bone_index = bone_index++; - std::uint8_t metacoxa_l_bone_index = bone_index++; - std::uint8_t metacoxa_r_bone_index = bone_index++; - std::uint8_t metafemur_l_bone_index = bone_index++; - std::uint8_t metafemur_r_bone_index = bone_index++; - std::uint8_t metatibia_l_bone_index = bone_index++; - std::uint8_t metatibia_r_bone_index = bone_index++; - std::uint8_t metatarsus_l_bone_index = bone_index++; - std::uint8_t metatarsus_r_bone_index = bone_index++; - std::uint8_t head_bone_index = bone_index++; - std::uint8_t mandible_l_bone_index = bone_index++; - std::uint8_t mandible_r_bone_index = bone_index++; - std::uint8_t antennomere1_l_bone_index = bone_index++; - std::uint8_t antennomere1_r_bone_index = bone_index++; - std::uint8_t antennomere2_l_bone_index = bone_index++; - std::uint8_t antennomere2_r_bone_index = bone_index++; - std::uint8_t petiole_bone_index =(petiole_present) ? bone_index++ : static_cast(bone_count); - std::uint8_t postpetiole_bone_index = (postpetiole_present) ? bone_index++ : static_cast(bone_count); - std::uint8_t gaster_bone_index = bone_index++; - std::uint8_t sting_bone_index = (sting_present) ? bone_index++ : static_cast(bone_count); - - // Construct bone identifiers - ::bone mesosoma_bone = make_bone(mesosoma_bone_index); - ::bone procoxa_l_bone = make_bone(procoxa_l_bone_index, mesosoma_bone_index); - ::bone procoxa_r_bone = make_bone(procoxa_r_bone_index, mesosoma_bone_index); - ::bone profemur_l_bone = make_bone(profemur_l_bone_index, procoxa_l_bone_index); - ::bone profemur_r_bone = make_bone(profemur_r_bone_index, procoxa_r_bone_index); - ::bone protibia_l_bone = make_bone(protibia_l_bone_index, profemur_l_bone_index); - ::bone protibia_r_bone = make_bone(protibia_r_bone_index, profemur_r_bone_index); - ::bone protarsus_l_bone = make_bone(protarsus_l_bone_index, protibia_l_bone_index); - ::bone protarsus_r_bone = make_bone(protarsus_r_bone_index, protibia_r_bone_index); - ::bone mesocoxa_l_bone = make_bone(mesocoxa_l_bone_index, mesosoma_bone_index); - ::bone mesocoxa_r_bone = make_bone(mesocoxa_r_bone_index, mesosoma_bone_index); - ::bone mesofemur_l_bone = make_bone(mesofemur_l_bone_index, mesocoxa_l_bone_index); - ::bone mesofemur_r_bone = make_bone(mesofemur_r_bone_index, mesocoxa_r_bone_index); - ::bone mesotibia_l_bone = make_bone(mesotibia_l_bone_index, mesofemur_l_bone_index); - ::bone mesotibia_r_bone = make_bone(mesotibia_r_bone_index, mesofemur_r_bone_index); - ::bone mesotarsus_l_bone = make_bone(mesotarsus_l_bone_index, mesotibia_l_bone_index); - ::bone mesotarsus_r_bone = make_bone(mesotarsus_r_bone_index, mesotibia_r_bone_index); - ::bone metacoxa_l_bone = make_bone(metacoxa_l_bone_index, mesosoma_bone_index); - ::bone metacoxa_r_bone = make_bone(metacoxa_r_bone_index, mesosoma_bone_index); - ::bone metafemur_l_bone = make_bone(metafemur_l_bone_index, metacoxa_l_bone_index); - ::bone metafemur_r_bone = make_bone(metafemur_r_bone_index, metacoxa_r_bone_index); - ::bone metatibia_l_bone = make_bone(metatibia_l_bone_index, metafemur_l_bone_index); - ::bone metatibia_r_bone = make_bone(metatibia_r_bone_index, metafemur_r_bone_index); - ::bone metatarsus_l_bone = make_bone(metatarsus_l_bone_index, metatibia_l_bone_index); - ::bone metatarsus_r_bone = make_bone(metatarsus_r_bone_index, metatibia_r_bone_index); - ::bone head_bone = make_bone(head_bone_index, mesosoma_bone_index); - ::bone mandible_l_bone = make_bone(mandible_l_bone_index, head_bone_index); - ::bone mandible_r_bone = make_bone(mandible_r_bone_index, head_bone_index); - ::bone antennomere1_l_bone = make_bone(antennomere1_l_bone_index, head_bone_index); - ::bone antennomere1_r_bone = make_bone(antennomere1_r_bone_index, head_bone_index); - ::bone antennomere2_l_bone = make_bone(antennomere2_l_bone_index, antennomere1_l_bone_index); - ::bone antennomere2_r_bone = make_bone(antennomere2_r_bone_index, antennomere1_r_bone_index); - ::bone petiole_bone = make_bone(petiole_bone_index, mesosoma_bone_index); - ::bone postpetiole_bone = make_bone(postpetiole_bone_index, petiole_bone_index); - ::bone gaster_bone = make_bone(gaster_bone_index, (postpetiole_present) ? postpetiole_bone_index : petiole_bone_index); - ::bone sting_bone = make_bone(sting_bone_index, gaster_bone_index); - - // Map bone names to bones - skeleton.bone_map["mesosoma"] = mesosoma_bone; - skeleton.bone_map["procoxa_l"] = procoxa_l_bone; - skeleton.bone_map["procoxa_r"] = procoxa_r_bone; - skeleton.bone_map["profemur_l"] = profemur_l_bone; - skeleton.bone_map["profemur_r"] = profemur_r_bone; - skeleton.bone_map["protibia_l"] = protibia_l_bone; - skeleton.bone_map["protibia_r"] = protibia_r_bone; - skeleton.bone_map["protarsus_l"] = protarsus_l_bone; - skeleton.bone_map["protarsus_r"] = protarsus_r_bone; - skeleton.bone_map["mesocoxa_l"] = mesocoxa_l_bone; - skeleton.bone_map["mesocoxa_r"] = mesocoxa_r_bone; - skeleton.bone_map["mesofemur_l"] = mesofemur_l_bone; - skeleton.bone_map["mesofemur_r"] = mesofemur_r_bone; - skeleton.bone_map["mesotibia_l"] = mesotibia_l_bone; - skeleton.bone_map["mesotibia_r"] = mesotibia_r_bone; - skeleton.bone_map["mesotarsus_l"] = mesotarsus_l_bone; - skeleton.bone_map["mesotarsus_r"] = mesotarsus_r_bone; - skeleton.bone_map["metacoxa_l"] = metacoxa_l_bone; - skeleton.bone_map["metacoxa_r"] = metacoxa_r_bone; - skeleton.bone_map["metafemur_l"] = metafemur_l_bone; - skeleton.bone_map["metafemur_r"] = metafemur_r_bone; - skeleton.bone_map["metatibia_l"] = metatibia_l_bone; - skeleton.bone_map["metatibia_r"] = metatibia_r_bone; - skeleton.bone_map["metatarsus_l"] = metatarsus_l_bone; - skeleton.bone_map["metatarsus_r"] = metatarsus_r_bone; - skeleton.bone_map["head"] = head_bone; - skeleton.bone_map["mandible_l"] = mandible_l_bone; - skeleton.bone_map["mandible_r"] = mandible_r_bone; - skeleton.bone_map["antennomere1_l"] = antennomere1_l_bone; - skeleton.bone_map["antennomere1_r"] = antennomere1_r_bone; - skeleton.bone_map["antennomere2_l"] = antennomere2_l_bone; - skeleton.bone_map["antennomere2_r"] = antennomere2_r_bone; - if (petiole_present) - skeleton.bone_map["petiole"] = petiole_bone; - if (postpetiole_present) - skeleton.bone_map["postpetiole"] = postpetiole_bone; - skeleton.bone_map["gaster"] = gaster_bone; - if (sting_present) - skeleton.bone_map["sting"] = sting_bone; - - // Get reference to skeleton bind pose - pose& bind_pose = skeleton.bind_pose; - - // Skeleton mesosoma pose - if (auto it = mesosoma_skeleton.bone_map.find("mesosoma"); it != mesosoma_skeleton.bone_map.end()) - bind_pose[mesosoma_bone] = mesosoma_skeleton.bind_pose.at(it->second); - - // Skeleton forelegs pose - if (auto it = legs_skeleton.bone_map.find("procoxa_l"); it != legs_skeleton.bone_map.end()) - bind_pose[procoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("procoxa_r"); it != legs_skeleton.bone_map.end()) - bind_pose[procoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("profemur_l"); it != legs_skeleton.bone_map.end()) - bind_pose[profemur_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("profemur_r"); it != legs_skeleton.bone_map.end()) - bind_pose[profemur_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("protibia_l"); it != legs_skeleton.bone_map.end()) - bind_pose[protibia_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("protibia_r"); it != legs_skeleton.bone_map.end()) - bind_pose[protibia_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus1_l"); it != legs_skeleton.bone_map.end()) - bind_pose[protarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus1_r"); it != legs_skeleton.bone_map.end()) - bind_pose[protarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); - - // Skeleton midlegs pose - if (auto it = legs_skeleton.bone_map.find("mesocoxa_l"); it != legs_skeleton.bone_map.end()) - bind_pose[mesocoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesocoxa_r"); it != legs_skeleton.bone_map.end()) - bind_pose[mesocoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesofemur_l"); it != legs_skeleton.bone_map.end()) - bind_pose[mesofemur_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesofemur_r"); it != legs_skeleton.bone_map.end()) - bind_pose[mesofemur_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotibia_l"); it != legs_skeleton.bone_map.end()) - bind_pose[mesotibia_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotibia_r"); it != legs_skeleton.bone_map.end()) - bind_pose[mesotibia_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus1_l"); it != legs_skeleton.bone_map.end()) - bind_pose[mesotarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus1_r"); it != legs_skeleton.bone_map.end()) - bind_pose[mesotarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); - - // Skeleton hindlegs pose - if (auto it = legs_skeleton.bone_map.find("metacoxa_l"); it != legs_skeleton.bone_map.end()) - bind_pose[metacoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metacoxa_r"); it != legs_skeleton.bone_map.end()) - bind_pose[metacoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metafemur_l"); it != legs_skeleton.bone_map.end()) - bind_pose[metafemur_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metafemur_r"); it != legs_skeleton.bone_map.end()) - bind_pose[metafemur_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metatibia_l"); it != legs_skeleton.bone_map.end()) - bind_pose[metatibia_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metatibia_r"); it != legs_skeleton.bone_map.end()) - bind_pose[metatibia_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus1_l"); it != legs_skeleton.bone_map.end()) - bind_pose[metatarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus1_r"); it != legs_skeleton.bone_map.end()) - bind_pose[metatarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); - - // Skeleton head pose - bind_pose[head_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("head")) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("head")); - - // Skeleton mandibles pose - bind_pose[mandible_l_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_l")) * mandibles_skeleton.bind_pose.at(mandibles_skeleton.bone_map.at("mandible_l")); - bind_pose[mandible_r_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_r")) * mandibles_skeleton.bind_pose.at(mandibles_skeleton.bone_map.at("mandible_r")); - - // Skeleton antennae pose - bind_pose[antennomere1_l_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_l")) * antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere1_l")); - bind_pose[antennomere1_r_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_r")) * antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere1_r")); - bind_pose[antennomere2_l_bone] = antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere2_l")); - bind_pose[antennomere2_r_bone] = antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere2_r")); - - // Skeleton waist pose - if (petiole_present) - { - bind_pose[petiole_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("petiole")); - } - if (postpetiole_present) - { - bind_pose[postpetiole_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("postpetiole")); - } - - // Skeleton gaster pose - if (postpetiole_present) - { - bind_pose[gaster_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("postpetiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); - } - else if (petiole_present) - { - bind_pose[gaster_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("petiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); - } - else - { - bind_pose[gaster_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); - } - - // Skeleton sting pose - if (sting_present) - { - bind_pose[sting_bone] = gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("sting")) * sting_skeleton->bind_pose.at(sting_skeleton->bone_map.at("sting")); - } - - // Calculate the skeleton-space bind pose - pose bind_pose_ss; - ::concatenate(bind_pose, bind_pose_ss); - - // Calculate inverse skeleton-space bind pose - ::inverse(bind_pose_ss, skeleton.inverse_bind_pose); - - // Get number of vertex indices for each body part - std::size_t mesosoma_index_count = (*mesosoma_model->get_groups())[0]->get_index_count(); - std::size_t legs_index_count = (*legs_model->get_groups())[0]->get_index_count(); - std::size_t head_index_count = (*head_model->get_groups())[0]->get_index_count(); - std::size_t mandibles_index_count = (*mandibles_model->get_groups())[0]->get_index_count(); - std::size_t antennae_index_count = (*antennae_model->get_groups())[0]->get_index_count(); - std::size_t waist_index_count = (*waist_model->get_groups())[0]->get_index_count(); - std::size_t gaster_index_count = (*gaster_model->get_groups())[0]->get_index_count(); - std::size_t sting_index_count = (sting_present) ? (*sting_model->get_groups())[0]->get_index_count() : 0; - std::size_t eyes_index_count = (eyes_present) ? (*eyes_model->get_groups())[0]->get_index_count() : 0; - std::size_t lateral_ocelli_index_count = (lateral_ocelli_present) ? (*lateral_ocelli_model->get_groups())[0]->get_index_count() : 0; - std::size_t median_ocellus_index_count = (median_ocellus_present) ? (*median_ocellus_model->get_groups())[0]->get_index_count() : 0; - std::size_t forewings_index_count = (wings_present) ? (*forewings_model->get_groups())[0]->get_index_count() : 0; - std::size_t hindwings_index_count = (wings_present) ? (*hindwings_model->get_groups())[0]->get_index_count() : 0; - std::size_t exoskeleton_index_count = - mesosoma_index_count - + legs_index_count - + head_index_count - + mandibles_index_count - + antennae_index_count - + waist_index_count - + gaster_index_count - + sting_index_count; - - // Calculate transform from legs space to body space - const math::transform& legs_to_body = math::transform::identity; - - // Reskin leg bones - std::unordered_set old_procoxa_l_indices; - if (auto it = legs_skeleton.bone_map.find("procoxa_l"); it != legs_skeleton.bone_map.end()) - old_procoxa_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_procoxa_l_indices, procoxa_l_bone_index, legs_to_body); - std::unordered_set old_profemur_l_indices; - if (auto it = legs_skeleton.bone_map.find("profemur_l"); it != legs_skeleton.bone_map.end()) - old_profemur_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_profemur_l_indices, profemur_l_bone_index, legs_to_body); - std::unordered_set old_protibia_l_indices; - if (auto it = legs_skeleton.bone_map.find("protibia_l"); it != legs_skeleton.bone_map.end()) - old_protibia_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protibia_l_indices, protibia_l_bone_index, legs_to_body); - std::unordered_set old_protarsus_l_indices; - if (auto it = legs_skeleton.bone_map.find("protarsus1_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus2_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus3_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus4_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus5_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protarsus_l_indices, protarsus_l_bone_index, legs_to_body); - - std::unordered_set old_procoxa_r_indices; - if (auto it = legs_skeleton.bone_map.find("procoxa_r"); it != legs_skeleton.bone_map.end()) - old_procoxa_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_procoxa_r_indices, procoxa_r_bone_index, legs_to_body); - std::unordered_set old_profemur_r_indices; - if (auto it = legs_skeleton.bone_map.find("profemur_r"); it != legs_skeleton.bone_map.end()) - old_profemur_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_profemur_r_indices, profemur_r_bone_index, legs_to_body); - std::unordered_set old_protibia_r_indices; - if (auto it = legs_skeleton.bone_map.find("protibia_r"); it != legs_skeleton.bone_map.end()) - old_protibia_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protibia_r_indices, protibia_r_bone_index, legs_to_body); - std::unordered_set old_protarsus_r_indices; - if (auto it = legs_skeleton.bone_map.find("protarsus1_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus2_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus3_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus4_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus5_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protarsus_r_indices, protarsus_r_bone_index, legs_to_body); - - std::unordered_set old_mesocoxa_l_indices; - if (auto it = legs_skeleton.bone_map.find("mesocoxa_l"); it != legs_skeleton.bone_map.end()) - old_mesocoxa_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesocoxa_l_indices, mesocoxa_l_bone_index, legs_to_body); - std::unordered_set old_mesofemur_l_indices; - if (auto it = legs_skeleton.bone_map.find("mesofemur_l"); it != legs_skeleton.bone_map.end()) - old_mesofemur_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesofemur_l_indices, mesofemur_l_bone_index, legs_to_body); - std::unordered_set old_mesotibia_l_indices; - if (auto it = legs_skeleton.bone_map.find("mesotibia_l"); it != legs_skeleton.bone_map.end()) - old_mesotibia_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotibia_l_indices, mesotibia_l_bone_index, legs_to_body); - std::unordered_set old_mesotarsus_l_indices; - if (auto it = legs_skeleton.bone_map.find("mesotarsus1_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus2_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus3_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus4_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus5_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotarsus_l_indices, mesotarsus_l_bone_index, legs_to_body); - - std::unordered_set old_mesocoxa_r_indices; - if (auto it = legs_skeleton.bone_map.find("mesocoxa_r"); it != legs_skeleton.bone_map.end()) - old_mesocoxa_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesocoxa_r_indices, mesocoxa_r_bone_index, legs_to_body); - std::unordered_set old_mesofemur_r_indices; - if (auto it = legs_skeleton.bone_map.find("mesofemur_r"); it != legs_skeleton.bone_map.end()) - old_mesofemur_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesofemur_r_indices, mesofemur_r_bone_index, legs_to_body); - std::unordered_set old_mesotibia_r_indices; - if (auto it = legs_skeleton.bone_map.find("mesotibia_r"); it != legs_skeleton.bone_map.end()) - old_mesotibia_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotibia_r_indices, mesotibia_r_bone_index, legs_to_body); - std::unordered_set old_mesotarsus_r_indices; - if (auto it = legs_skeleton.bone_map.find("mesotarsus1_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus2_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus3_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus4_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus5_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotarsus_r_indices, mesotarsus_r_bone_index, legs_to_body); - - std::unordered_set old_metacoxa_l_indices; - if (auto it = legs_skeleton.bone_map.find("metacoxa_l"); it != legs_skeleton.bone_map.end()) - old_metacoxa_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metacoxa_l_indices, metacoxa_l_bone_index, legs_to_body); - std::unordered_set old_metafemur_l_indices; - if (auto it = legs_skeleton.bone_map.find("metafemur_l"); it != legs_skeleton.bone_map.end()) - old_metafemur_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metafemur_l_indices, metafemur_l_bone_index, legs_to_body); - std::unordered_set old_metatibia_l_indices; - if (auto it = legs_skeleton.bone_map.find("metatibia_l"); it != legs_skeleton.bone_map.end()) - old_metatibia_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatibia_l_indices, metatibia_l_bone_index, legs_to_body); - std::unordered_set old_metatarsus_l_indices; - if (auto it = legs_skeleton.bone_map.find("metatarsus1_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus2_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus3_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus4_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus5_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatarsus_l_indices, metatarsus_l_bone_index, legs_to_body); - - std::unordered_set old_metacoxa_r_indices; - if (auto it = legs_skeleton.bone_map.find("metacoxa_r"); it != legs_skeleton.bone_map.end()) - old_metacoxa_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metacoxa_r_indices, metacoxa_r_bone_index, legs_to_body); - std::unordered_set old_metafemur_r_indices; - if (auto it = legs_skeleton.bone_map.find("metafemur_r"); it != legs_skeleton.bone_map.end()) - old_metafemur_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metafemur_r_indices, metafemur_r_bone_index, legs_to_body); - std::unordered_set old_metatibia_r_indices; - if (auto it = legs_skeleton.bone_map.find("metatibia_r"); it != legs_skeleton.bone_map.end()) - old_metatibia_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatibia_r_indices, metatibia_r_bone_index, legs_to_body); - std::unordered_set old_metatarsus_r_indices; - if (auto it = legs_skeleton.bone_map.find("metatarsus1_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus2_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus3_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus4_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus5_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatarsus_r_indices, metatarsus_r_bone_index, legs_to_body); - - // Calculate transform from head space to body space - math::transform head_to_body = bind_pose_ss.at(mesosoma_bone) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("head")); - - // Reskin head bone - std::unordered_set old_head_bone_indices; - if (auto it = head_skeleton.bone_map.find("head"); it != head_skeleton.bone_map.end()) - old_head_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + head_vbo_offset, head_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_bone_indices, head_bone_index, head_to_body); - - // Calculate transforms from mandibles space to body space - math::transform mandible_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_l")); - math::transform mandible_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_r")); - - // Reskin mandible bones - std::unordered_set old_head_mandible_l_bone_indices; - if (auto it = mandibles_skeleton.bone_map.find("mandible_l"); it != mandibles_skeleton.bone_map.end()) - old_head_mandible_l_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + mandibles_vbo_offset, mandibles_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_mandible_l_bone_indices, mandible_l_bone_index, mandible_l_to_body); - std::unordered_set old_head_mandible_r_bone_indices; - if (auto it = mandibles_skeleton.bone_map.find("mandible_r"); it != mandibles_skeleton.bone_map.end()) - old_head_mandible_r_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + mandibles_vbo_offset, mandibles_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_mandible_r_bone_indices, mandible_r_bone_index, mandible_r_to_body); - - // Calculate transforms from antennae space to body space - math::transform antenna_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_l")); - math::transform antenna_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_r")); - - // Reskin antennomere1 bones - std::unordered_set old_antennomere1_l_indices; - if (auto it = antennae_skeleton.bone_map.find("antennomere1_l"); it != antennae_skeleton.bone_map.end()) - old_antennomere1_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere1_l_indices, antennomere1_l_bone_index, antenna_l_to_body); - std::unordered_set old_antennomere1_r_indices; - if (auto it = antennae_skeleton.bone_map.find("antennomere1_r"); it != antennae_skeleton.bone_map.end()) - old_antennomere1_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere1_r_indices, antennomere1_r_bone_index, antenna_r_to_body); - - // Reskin antennomere2+ bones - const std::vector antennomere_bone_names = - { - "antennomere2", - "antennomere3", - "antennomere4", - "antennomere5", - "antennomere6", - "antennomere7", - "antennomere8", - "antennomere9", - "antennomere10", - "antennomere11", - "antennomere12", - "antennomere13" - }; - std::unordered_set old_antennomere_l_indices; - for (const std::string& bone_name: antennomere_bone_names) - if (auto it = antennae_skeleton.bone_map.find(bone_name + "_l"); it != antennae_skeleton.bone_map.end()) - old_antennomere_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere_l_indices, antennomere2_l_bone_index, antenna_l_to_body); - std::unordered_set old_antennomere_r_indices; - for (const std::string& bone_name: antennomere_bone_names) - if (auto it = antennae_skeleton.bone_map.find(bone_name + "_r"); it != antennae_skeleton.bone_map.end()) - old_antennomere_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere_r_indices, antennomere2_r_bone_index, antenna_r_to_body); - - // Calculate transform from waist space to body space - math::transform waist_to_body = bind_pose_ss.at(mesosoma_bone) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")); - - // Reskin waist bones - std::unordered_set old_petiole_bone_indices; - if (auto it = waist_skeleton.bone_map.find("petiole"); it != waist_skeleton.bone_map.end()) - old_petiole_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + waist_vbo_offset, waist_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_petiole_bone_indices, petiole_bone_index, waist_to_body); - if (postpetiole_present) - { - std::unordered_set old_postpetiole_bone_indices; - if (auto it = waist_skeleton.bone_map.find("postpetiole"); it != waist_skeleton.bone_map.end()) - old_postpetiole_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + waist_vbo_offset, waist_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_postpetiole_bone_indices, postpetiole_bone_index, waist_to_body); - } - - // Calculate transform from gaster space to body space - math::transform gaster_to_body = bind_pose_ss.at(bone_parent_index(gaster_bone)) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("gaster")); - - // Reskin gaster bones - std::unordered_set old_gaster_bone_indices; - if (auto it = gaster_skeleton.bone_map.find("gaster"); it != gaster_skeleton.bone_map.end()) - old_gaster_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + gaster_vbo_offset, gaster_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_gaster_bone_indices, gaster_bone_index, gaster_to_body); - - if (sting_present) - { - // Calculate transform from sting space to body space - math::transform sting_to_body = gaster_to_body * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("sting")); - - // Reskin sting bones - std::unordered_set old_sting_bone_indices; - if (auto it = sting_skeleton->bone_map.find("sting"); it != sting_skeleton->bone_map.end()) - old_sting_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + sting_vbo_offset, sting_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_sting_bone_indices, sting_bone_index, sting_to_body); - } - - if (eyes_present) - { - // Calculate transforms from eyes space to body space - math::transform eye_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("eye_l")); - math::transform eye_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("eye_r")); - - // Reskin eye bones - std::unordered_set old_eye_l_bone_indices; - if (auto it = eyes_skeleton->bone_map.find("eye_l"); it != eyes_skeleton->bone_map.end()) - old_eye_l_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + eyes_vbo_offset, eyes_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_eye_l_bone_indices, head_bone_index, eye_l_to_body); - std::unordered_set old_eye_r_bone_indices; - if (auto it = eyes_skeleton->bone_map.find("eye_r"); it != eyes_skeleton->bone_map.end()) - old_eye_r_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + eyes_vbo_offset, eyes_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_eye_r_bone_indices, head_bone_index, eye_r_to_body); - } - - if (lateral_ocelli_present) - { - // Calculate transforms from lateral ocelli space to body space - math::transform ocellus_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_l")); - math::transform ocellus_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_r")); - - // Reskin lateral ocelli bones - std::unordered_set old_ocellus_l_bone_indices; - if (auto it = lateral_ocelli_skeleton->bone_map.find("ocellus_l"); it != lateral_ocelli_skeleton->bone_map.end()) - old_ocellus_l_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + lateral_ocelli_vbo_offset, lateral_ocelli_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_l_bone_indices, head_bone_index, ocellus_l_to_body); - std::unordered_set old_ocellus_r_bone_indices; - if (auto it = lateral_ocelli_skeleton->bone_map.find("ocellus_r"); it != lateral_ocelli_skeleton->bone_map.end()) - old_ocellus_r_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + lateral_ocelli_vbo_offset, lateral_ocelli_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_r_bone_indices, head_bone_index, ocellus_r_to_body); - } - - if (median_ocellus_present) - { - // Calculate transforms from lateral ocelli space to body space - math::transform ocellus_m_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_m")); - - // Reskin lateral ocelli bones - std::unordered_set old_ocellus_m_bone_indices; - if (auto it = median_ocellus_skeleton->bone_map.find("ocellus_m"); it != median_ocellus_skeleton->bone_map.end()) - old_ocellus_m_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + median_ocellus_vbo_offset, median_ocellus_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_m_bone_indices, head_bone_index, ocellus_m_to_body); - } - - // Upload vertex buffer data to model VBO - model->get_vertex_buffer()->repurpose(gl::buffer_usage::static_draw, vertex_buffer_size, vertex_buffer_data); - - // Construct exoskeleton model group - render::model_group* exoskeleton_group = model->add_group("exoskeleton"); - exoskeleton_group->set_material(exoskeleton_material); - exoskeleton_group->set_drawing_mode(gl::drawing_mode::triangles); - exoskeleton_group->set_start_index(0); - exoskeleton_group->set_index_count(exoskeleton_index_count); - - std::size_t index_offset = exoskeleton_index_count; - if (eyes_present) - { - // Construct eyes model group - render::model_group* eyes_group = model->add_group("eyes"); - eyes_group->set_material((*eyes_model->get_groups())[0]->get_material()); - eyes_group->set_drawing_mode(gl::drawing_mode::triangles); - eyes_group->set_start_index(index_offset); - eyes_group->set_index_count(eyes_index_count); - index_offset += eyes_index_count; - } - - if (lateral_ocelli_present || median_ocellus_present) - { - // Construct ocelli model group - render::model_group* ocelli_group = model->add_group("ocelli"); - ocelli_group->set_drawing_mode(gl::drawing_mode::triangles); - ocelli_group->set_start_index(index_offset); - - std::size_t index_count = 0; - if (lateral_ocelli_present) - { - index_count += lateral_ocelli_index_count; - index_offset += lateral_ocelli_index_count; - ocelli_group->set_material((*lateral_ocelli_model->get_groups())[0]->get_material()); - } - if (median_ocellus_present) - { - index_count += median_ocellus_index_count; - index_offset += median_ocellus_index_count; - if (!lateral_ocelli_present) - ocelli_group->set_material((*median_ocellus_model->get_groups())[0]->get_material()); - } - - ocelli_group->set_index_count(index_count); - } - - if (wings_present) - { - // Construct forewings model group - render::model_group* forewings_group = model->add_group("forewings"); - forewings_group->set_material((*forewings_model->get_groups())[0]->get_material()); - forewings_group->set_drawing_mode(gl::drawing_mode::triangles); - forewings_group->set_start_index(index_offset); - forewings_group->set_index_count(forewings_index_count); - index_offset += forewings_index_count; - - // Construct hindwings model group - render::model_group* hindwings_group = model->add_group("hindwings"); - hindwings_group->set_material((*hindwings_model->get_groups())[0]->get_material()); - hindwings_group->set_drawing_mode(gl::drawing_mode::triangles); - hindwings_group->set_start_index(index_offset); - hindwings_group->set_index_count(hindwings_index_count); - index_offset += hindwings_index_count; - } - - // Calculate model bounding box - geom::aabb bounds = calculate_bounds(vertex_buffer_data, index_offset, *position_attribute); - model->set_bounds(bounds); - - // Free vertex buffer data - delete[] vertex_buffer_data; - - return model; -} - -void reskin_vertices -( - std::uint8_t* vertex_data, - std::size_t index_count, - const gl::vertex_attribute& position_attribute, - const gl::vertex_attribute& normal_attribute, - const gl::vertex_attribute& tangent_attribute, - const gl::vertex_attribute& bone_index_attribute, - const std::unordered_set& old_bone_indices, - std::uint8_t new_bone_index, - const math::transform& transform -) -{ - std::uint8_t* position_data = vertex_data + position_attribute.offset; - std::uint8_t* normal_data = vertex_data + normal_attribute.offset; - std::uint8_t* tangent_data = vertex_data + tangent_attribute.offset; - std::uint8_t* bone_index_data = vertex_data + bone_index_attribute.offset; - - for (std::size_t i = 0; i < index_count; ++i) - { - // Get bone index of current vertex - float* bone_index = reinterpret_cast(bone_index_data + bone_index_attribute.stride * i); - - // Skip irrelevant bones - if (!old_bone_indices.count(static_cast(*bone_index + 0.5f))) - continue; - - // Get vertex position - float* x = reinterpret_cast(position_data + position_attribute.stride * i); - float* y = x + 1; - float* z = y + 1; - - // Get vertex normal - float* nx = reinterpret_cast(normal_data + normal_attribute.stride * i); - float* ny = nx + 1; - float* nz = ny + 1; - - // Get vertex tangent - float* tx = reinterpret_cast(tangent_data + tangent_attribute.stride * i); - float* ty = tx + 1; - float* tz = ty + 1; - //float* bts = tz + 1; - - // Transform vertex attributes - float3 position = transform * float3{*x, *y, *z}; - float3 normal = math::normalize(transform.rotation * float3{*nx, *ny, *nz}); - float3 tangent = transform.rotation * float3{*tx, *ty, *tz}; - - // Update vertex data - *x = position.x(); - *y = position.y(); - *z = position.z(); - *nx = normal.x(); - *ny = normal.y(); - *nz = normal.z(); - *tx = tangent.x(); - *ty = tangent.y(); - *tz = tangent.z(); - //*bts = ... - *bone_index = static_cast(new_bone_index); - } -} - -geom::aabb calculate_bounds(std::uint8_t* vertex_data, std::size_t index_count, const gl::vertex_attribute& position_attribute) -{ - std::uint8_t* position_data = vertex_data + position_attribute.offset; - - geom::aabb bounds; - bounds.min_point.x() = std::numeric_limits::infinity(); - bounds.min_point.y() = std::numeric_limits::infinity(); - bounds.min_point.z() = std::numeric_limits::infinity(); - bounds.max_point.x() = -std::numeric_limits::infinity(); - bounds.max_point.y() = -std::numeric_limits::infinity(); - bounds.max_point.z() = -std::numeric_limits::infinity(); - - for (std::size_t i = 0; i < index_count; ++i) - { - // Get vertex position - float* x = reinterpret_cast(position_data + position_attribute.stride * i); - float* y = x + 1; - float* z = y + 1; - - bounds.min_point.x() = std::min(*x, bounds.min_point.x()); - bounds.min_point.y() = std::min(*y, bounds.min_point.y()); - bounds.min_point.z() = std::min(*z, bounds.min_point.z()); - bounds.max_point.x() = std::max(*x, bounds.max_point.x()); - bounds.max_point.y() = std::max(*y, bounds.max_point.y()); - bounds.max_point.z() = std::max(*z, bounds.max_point.z()); - } - - return bounds; -} - -} // namespace ant +/* + * 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 . + */ + +#include "game/ant/ant-morphogenesis.hpp" +#include +#include +#include +#include +#include + +static void reskin_vertices +( + std::byte* vertex_data, + std::size_t index_count, + const gl::vertex_attribute& position_attribute, + const gl::vertex_attribute& normal_attribute, + const gl::vertex_attribute& tangent_attribute, + const gl::vertex_attribute& bone_index_attribute, + const std::unordered_set& old_bone_indices, + std::uint8_t new_bone_index, + const math::transform& transform +); +static geom::aabb calculate_bounds(const std::byte* vertex_data, std::size_t index_count, const gl::vertex_attribute& position_attribute); +static render::model* build_model +( + render::material* material, + const render::model* antennae, + const render::model* eyes, + const render::model* forewings, + const render::model* gaster, + const render::model* head, + const render::model* hindwings, + const render::model* legs, + const render::model* mandibles, + const render::model* mesosoma, + const render::model* lateral_ocelli, + const render::model* median_ocellus, + const render::model* sting, + const render::model* waist +); + +static std::unique_ptr build_exoskeleton_material +( + const ant_pigmentation_phene& pigmentation, + const ant_sculpturing_phene& sculpturing +) +{ + // Allocate copy of pigmentation material + std::unique_ptr exoskeleton_material = std::make_unique(*pigmentation.material); + + // Set roughness variable + exoskeleton_material->set_variable("roughness", std::make_shared(1, sculpturing.roughness)); + + // Set normal map variable + exoskeleton_material->set_variable("normal_map", std::make_shared(1, sculpturing.normal_map)); + + return exoskeleton_material; +} + +std::unique_ptr ant_morphogenesis(const ant_phenome& phenome) +{ + // Build exoskeleton material + std::shared_ptr exoskeleton_material = build_exoskeleton_material(*phenome.pigmentation, *phenome.sculpturing); + + // Determine presence of optional parts + bool eyes_present = phenome.eyes->present; + bool lateral_ocelli_present = phenome.ocelli->lateral_ocelli_present; + bool median_ocellus_present = phenome.ocelli->median_ocellus_present; + bool petiole_present = phenome.waist->petiole_present; + bool postpetiole_present = phenome.waist->postpetiole_present; + bool sting_present = phenome.sting->present; + bool wings_present = phenome.wings->present; + + // Get body part models + const render::model* antennae_model = phenome.antennae->model.get(); + const render::model* eyes_model = phenome.eyes->model.get(); + const render::model* forewings_model = phenome.wings->forewings_model.get(); + const render::model* gaster_model = phenome.gaster->model.get(); + const render::model* head_model = phenome.head->model.get(); + const render::model* hindwings_model = phenome.wings->hindwings_model.get(); + const render::model* lateral_ocelli_model = phenome.ocelli->lateral_ocelli_model.get(); + const render::model* legs_model = phenome.legs->model.get(); + const render::model* mandibles_model = phenome.mandibles->model.get(); + const render::model* median_ocellus_model = phenome.ocelli->median_ocellus_model.get(); + const render::model* mesosoma_model = phenome.mesosoma->model.get(); + const render::model* sting_model = phenome.sting->model.get(); + const render::model* waist_model = phenome.waist->model.get(); + + // Get body part vertex buffers + const gl::vertex_buffer* antennae_vbo = antennae_model->get_vertex_buffer().get(); + const gl::vertex_buffer* eyes_vbo = (eyes_present) ? eyes_model->get_vertex_buffer().get() : nullptr; + const gl::vertex_buffer* forewings_vbo = (wings_present) ? forewings_model->get_vertex_buffer().get() : nullptr; + const gl::vertex_buffer* gaster_vbo = gaster_model->get_vertex_buffer().get(); + const gl::vertex_buffer* head_vbo = head_model->get_vertex_buffer().get(); + const gl::vertex_buffer* hindwings_vbo = (wings_present) ? hindwings_model->get_vertex_buffer().get() : nullptr; + const gl::vertex_buffer* lateral_ocelli_vbo = (lateral_ocelli_model) ? lateral_ocelli_model->get_vertex_buffer().get() : nullptr; + const gl::vertex_buffer* legs_vbo = legs_model->get_vertex_buffer().get(); + const gl::vertex_buffer* mandibles_vbo = mandibles_model->get_vertex_buffer().get(); + const gl::vertex_buffer* median_ocellus_vbo = (median_ocellus_model) ? median_ocellus_model->get_vertex_buffer().get() : nullptr; + const gl::vertex_buffer* mesosoma_vbo = mesosoma_model->get_vertex_buffer().get(); + const gl::vertex_buffer* sting_vbo = (sting_present) ? sting_model->get_vertex_buffer().get() : nullptr; + const gl::vertex_buffer* waist_vbo = waist_model->get_vertex_buffer().get(); + + // Determine combined size of vertex buffers and save offsets + std::size_t vertex_buffer_size = 0; + std::size_t mesosoma_vbo_offset = vertex_buffer_size; + vertex_buffer_size += mesosoma_vbo->size(); + std::size_t legs_vbo_offset = vertex_buffer_size; + vertex_buffer_size += legs_vbo->size(); + std::size_t head_vbo_offset = vertex_buffer_size; + vertex_buffer_size += head_vbo->size(); + std::size_t mandibles_vbo_offset = vertex_buffer_size; + vertex_buffer_size += mandibles_vbo->size(); + std::size_t antennae_vbo_offset = vertex_buffer_size; + vertex_buffer_size += antennae_vbo->size(); + std::size_t waist_vbo_offset = vertex_buffer_size; + vertex_buffer_size += waist_vbo->size(); + std::size_t gaster_vbo_offset = vertex_buffer_size; + vertex_buffer_size += gaster_vbo->size(); + std::size_t sting_vbo_offset = vertex_buffer_size; + if (sting_present) + vertex_buffer_size += sting_vbo->size(); + std::size_t eyes_vbo_offset = vertex_buffer_size; + if (eyes_present) + vertex_buffer_size += eyes_vbo->size(); + std::size_t lateral_ocelli_vbo_offset = vertex_buffer_size; + if (lateral_ocelli_present) + vertex_buffer_size += lateral_ocelli_vbo->size(); + std::size_t median_ocellus_vbo_offset = vertex_buffer_size; + if (median_ocellus_present) + vertex_buffer_size += median_ocellus_vbo->size(); + std::size_t forewings_vbo_offset = vertex_buffer_size; + if (wings_present) + vertex_buffer_size += forewings_vbo->size(); + std::size_t hindwings_vbo_offset = vertex_buffer_size; + if (wings_present) + vertex_buffer_size += hindwings_vbo->size(); + + // Allocate combined vertex buffer data + std::vector vertex_buffer_data(vertex_buffer_size); + + // Read body part vertex buffer data into combined vertex buffer data + mesosoma_vbo->read({vertex_buffer_data.data() + mesosoma_vbo_offset, mesosoma_vbo->size()}); + legs_vbo->read({vertex_buffer_data.data() + legs_vbo_offset, legs_vbo->size()}); + head_vbo->read({vertex_buffer_data.data() + head_vbo_offset, head_vbo->size()}); + mandibles_vbo->read({vertex_buffer_data.data() + mandibles_vbo_offset, mandibles_vbo->size()}); + antennae_vbo->read({vertex_buffer_data.data() + antennae_vbo_offset, antennae_vbo->size()}); + waist_vbo->read({vertex_buffer_data.data() + waist_vbo_offset, waist_vbo->size()}); + gaster_vbo->read({vertex_buffer_data.data() + gaster_vbo_offset, gaster_vbo->size()}); + if (sting_present) + { + sting_vbo->read({vertex_buffer_data.data() + sting_vbo_offset, sting_vbo->size()}); + } + if (eyes_present) + { + eyes_vbo->read({vertex_buffer_data.data() + eyes_vbo_offset, eyes_vbo->size()}); + } + if (lateral_ocelli_present) + { + lateral_ocelli_vbo->read({vertex_buffer_data.data() + lateral_ocelli_vbo_offset, lateral_ocelli_vbo->size()}); + } + if (median_ocellus_present) + { + median_ocellus_vbo->read({vertex_buffer_data.data() + median_ocellus_vbo_offset, median_ocellus_vbo->size()}); + } + if (wings_present) + { + forewings_vbo->read({vertex_buffer_data.data() + forewings_vbo_offset, forewings_vbo->size()}); + hindwings_vbo->read({vertex_buffer_data.data() + hindwings_vbo_offset, hindwings_vbo->size()}); + } + + // Allocate model + std::unique_ptr model = std::make_unique(); + + // Setup model VAO + gl::vertex_array* model_vao = model->get_vertex_array().get(); + for (auto [location, attribute]: mesosoma_model->get_vertex_array()->attributes()) + { + attribute.buffer = model->get_vertex_buffer().get(); + model_vao->bind(location, attribute); + } + + // Get vertex attributes + const gl::vertex_attribute* position_attribute = nullptr; + const gl::vertex_attribute* normal_attribute = nullptr; + const gl::vertex_attribute* tangent_attribute = nullptr; + const gl::vertex_attribute* bone_index_attribute = nullptr; + const auto& vertex_attribute_map = model_vao->attributes(); + if (auto it = vertex_attribute_map.find(render::vertex_attribute::position); it != vertex_attribute_map.end()) + position_attribute = &it->second; + if (auto it = vertex_attribute_map.find(render::vertex_attribute::normal); it != vertex_attribute_map.end()) + normal_attribute = &it->second; + if (auto it = vertex_attribute_map.find(render::vertex_attribute::tangent); it != vertex_attribute_map.end()) + tangent_attribute = &it->second; + if (auto it = vertex_attribute_map.find(render::vertex_attribute::bone_index); it != vertex_attribute_map.end()) + bone_index_attribute = &it->second; + + // Get body part skeletons + const ::skeleton& mesosoma_skeleton = mesosoma_model->get_skeleton(); + const ::skeleton& legs_skeleton = legs_model->get_skeleton(); + const ::skeleton& head_skeleton = head_model->get_skeleton(); + const ::skeleton& mandibles_skeleton = mandibles_model->get_skeleton(); + const ::skeleton& antennae_skeleton = antennae_model->get_skeleton(); + const ::skeleton& waist_skeleton = waist_model->get_skeleton(); + const ::skeleton& gaster_skeleton = gaster_model->get_skeleton(); + const ::skeleton* sting_skeleton = (sting_present) ? &sting_model->get_skeleton() : nullptr; + const ::skeleton* eyes_skeleton = (eyes_present) ? &eyes_model->get_skeleton() : nullptr; + const ::skeleton* lateral_ocelli_skeleton = (lateral_ocelli_present) ? &lateral_ocelli_model->get_skeleton() : nullptr; + const ::skeleton* median_ocellus_skeleton = (median_ocellus_present) ? &median_ocellus_model->get_skeleton() : nullptr; + + // Allocate skeleton bones + ::skeleton& skeleton = model->get_skeleton(); + std::size_t bone_count = 33; + if (petiole_present) + bone_count += 1; + if (postpetiole_present) + bone_count += 1; + if (sting_present) + bone_count += 1; + if (wings_present) + bone_count += 4; + + // Assign bone indices + std::uint8_t bone_index = 0; + std::uint8_t mesosoma_bone_index = bone_index++; + std::uint8_t procoxa_l_bone_index = bone_index++; + std::uint8_t procoxa_r_bone_index = bone_index++; + std::uint8_t profemur_l_bone_index = bone_index++; + std::uint8_t profemur_r_bone_index = bone_index++; + std::uint8_t protibia_l_bone_index = bone_index++; + std::uint8_t protibia_r_bone_index = bone_index++; + std::uint8_t protarsus_l_bone_index = bone_index++; + std::uint8_t protarsus_r_bone_index = bone_index++; + std::uint8_t mesocoxa_l_bone_index = bone_index++; + std::uint8_t mesocoxa_r_bone_index = bone_index++; + std::uint8_t mesofemur_l_bone_index = bone_index++; + std::uint8_t mesofemur_r_bone_index = bone_index++; + std::uint8_t mesotibia_l_bone_index = bone_index++; + std::uint8_t mesotibia_r_bone_index = bone_index++; + std::uint8_t mesotarsus_l_bone_index = bone_index++; + std::uint8_t mesotarsus_r_bone_index = bone_index++; + std::uint8_t metacoxa_l_bone_index = bone_index++; + std::uint8_t metacoxa_r_bone_index = bone_index++; + std::uint8_t metafemur_l_bone_index = bone_index++; + std::uint8_t metafemur_r_bone_index = bone_index++; + std::uint8_t metatibia_l_bone_index = bone_index++; + std::uint8_t metatibia_r_bone_index = bone_index++; + std::uint8_t metatarsus_l_bone_index = bone_index++; + std::uint8_t metatarsus_r_bone_index = bone_index++; + std::uint8_t head_bone_index = bone_index++; + std::uint8_t mandible_l_bone_index = bone_index++; + std::uint8_t mandible_r_bone_index = bone_index++; + std::uint8_t antennomere1_l_bone_index = bone_index++; + std::uint8_t antennomere1_r_bone_index = bone_index++; + std::uint8_t antennomere2_l_bone_index = bone_index++; + std::uint8_t antennomere2_r_bone_index = bone_index++; + std::uint8_t petiole_bone_index =(petiole_present) ? bone_index++ : static_cast(bone_count); + std::uint8_t postpetiole_bone_index = (postpetiole_present) ? bone_index++ : static_cast(bone_count); + std::uint8_t gaster_bone_index = bone_index++; + std::uint8_t sting_bone_index = (sting_present) ? bone_index++ : static_cast(bone_count); + + // Construct bone identifiers + ::bone mesosoma_bone = make_bone(mesosoma_bone_index); + ::bone procoxa_l_bone = make_bone(procoxa_l_bone_index, mesosoma_bone_index); + ::bone procoxa_r_bone = make_bone(procoxa_r_bone_index, mesosoma_bone_index); + ::bone profemur_l_bone = make_bone(profemur_l_bone_index, procoxa_l_bone_index); + ::bone profemur_r_bone = make_bone(profemur_r_bone_index, procoxa_r_bone_index); + ::bone protibia_l_bone = make_bone(protibia_l_bone_index, profemur_l_bone_index); + ::bone protibia_r_bone = make_bone(protibia_r_bone_index, profemur_r_bone_index); + ::bone protarsus_l_bone = make_bone(protarsus_l_bone_index, protibia_l_bone_index); + ::bone protarsus_r_bone = make_bone(protarsus_r_bone_index, protibia_r_bone_index); + ::bone mesocoxa_l_bone = make_bone(mesocoxa_l_bone_index, mesosoma_bone_index); + ::bone mesocoxa_r_bone = make_bone(mesocoxa_r_bone_index, mesosoma_bone_index); + ::bone mesofemur_l_bone = make_bone(mesofemur_l_bone_index, mesocoxa_l_bone_index); + ::bone mesofemur_r_bone = make_bone(mesofemur_r_bone_index, mesocoxa_r_bone_index); + ::bone mesotibia_l_bone = make_bone(mesotibia_l_bone_index, mesofemur_l_bone_index); + ::bone mesotibia_r_bone = make_bone(mesotibia_r_bone_index, mesofemur_r_bone_index); + ::bone mesotarsus_l_bone = make_bone(mesotarsus_l_bone_index, mesotibia_l_bone_index); + ::bone mesotarsus_r_bone = make_bone(mesotarsus_r_bone_index, mesotibia_r_bone_index); + ::bone metacoxa_l_bone = make_bone(metacoxa_l_bone_index, mesosoma_bone_index); + ::bone metacoxa_r_bone = make_bone(metacoxa_r_bone_index, mesosoma_bone_index); + ::bone metafemur_l_bone = make_bone(metafemur_l_bone_index, metacoxa_l_bone_index); + ::bone metafemur_r_bone = make_bone(metafemur_r_bone_index, metacoxa_r_bone_index); + ::bone metatibia_l_bone = make_bone(metatibia_l_bone_index, metafemur_l_bone_index); + ::bone metatibia_r_bone = make_bone(metatibia_r_bone_index, metafemur_r_bone_index); + ::bone metatarsus_l_bone = make_bone(metatarsus_l_bone_index, metatibia_l_bone_index); + ::bone metatarsus_r_bone = make_bone(metatarsus_r_bone_index, metatibia_r_bone_index); + ::bone head_bone = make_bone(head_bone_index, mesosoma_bone_index); + ::bone mandible_l_bone = make_bone(mandible_l_bone_index, head_bone_index); + ::bone mandible_r_bone = make_bone(mandible_r_bone_index, head_bone_index); + ::bone antennomere1_l_bone = make_bone(antennomere1_l_bone_index, head_bone_index); + ::bone antennomere1_r_bone = make_bone(antennomere1_r_bone_index, head_bone_index); + ::bone antennomere2_l_bone = make_bone(antennomere2_l_bone_index, antennomere1_l_bone_index); + ::bone antennomere2_r_bone = make_bone(antennomere2_r_bone_index, antennomere1_r_bone_index); + ::bone petiole_bone = make_bone(petiole_bone_index, mesosoma_bone_index); + ::bone postpetiole_bone = make_bone(postpetiole_bone_index, petiole_bone_index); + ::bone gaster_bone = make_bone(gaster_bone_index, (postpetiole_present) ? postpetiole_bone_index : petiole_bone_index); + ::bone sting_bone = make_bone(sting_bone_index, gaster_bone_index); + + // Map bone names to bones + skeleton.bone_map["mesosoma"] = mesosoma_bone; + skeleton.bone_map["procoxa_l"] = procoxa_l_bone; + skeleton.bone_map["procoxa_r"] = procoxa_r_bone; + skeleton.bone_map["profemur_l"] = profemur_l_bone; + skeleton.bone_map["profemur_r"] = profemur_r_bone; + skeleton.bone_map["protibia_l"] = protibia_l_bone; + skeleton.bone_map["protibia_r"] = protibia_r_bone; + skeleton.bone_map["protarsus_l"] = protarsus_l_bone; + skeleton.bone_map["protarsus_r"] = protarsus_r_bone; + skeleton.bone_map["mesocoxa_l"] = mesocoxa_l_bone; + skeleton.bone_map["mesocoxa_r"] = mesocoxa_r_bone; + skeleton.bone_map["mesofemur_l"] = mesofemur_l_bone; + skeleton.bone_map["mesofemur_r"] = mesofemur_r_bone; + skeleton.bone_map["mesotibia_l"] = mesotibia_l_bone; + skeleton.bone_map["mesotibia_r"] = mesotibia_r_bone; + skeleton.bone_map["mesotarsus_l"] = mesotarsus_l_bone; + skeleton.bone_map["mesotarsus_r"] = mesotarsus_r_bone; + skeleton.bone_map["metacoxa_l"] = metacoxa_l_bone; + skeleton.bone_map["metacoxa_r"] = metacoxa_r_bone; + skeleton.bone_map["metafemur_l"] = metafemur_l_bone; + skeleton.bone_map["metafemur_r"] = metafemur_r_bone; + skeleton.bone_map["metatibia_l"] = metatibia_l_bone; + skeleton.bone_map["metatibia_r"] = metatibia_r_bone; + skeleton.bone_map["metatarsus_l"] = metatarsus_l_bone; + skeleton.bone_map["metatarsus_r"] = metatarsus_r_bone; + skeleton.bone_map["head"] = head_bone; + skeleton.bone_map["mandible_l"] = mandible_l_bone; + skeleton.bone_map["mandible_r"] = mandible_r_bone; + skeleton.bone_map["antennomere1_l"] = antennomere1_l_bone; + skeleton.bone_map["antennomere1_r"] = antennomere1_r_bone; + skeleton.bone_map["antennomere2_l"] = antennomere2_l_bone; + skeleton.bone_map["antennomere2_r"] = antennomere2_r_bone; + if (petiole_present) + skeleton.bone_map["petiole"] = petiole_bone; + if (postpetiole_present) + skeleton.bone_map["postpetiole"] = postpetiole_bone; + skeleton.bone_map["gaster"] = gaster_bone; + if (sting_present) + skeleton.bone_map["sting"] = sting_bone; + + // Get reference to skeleton bind pose + pose& bind_pose = skeleton.bind_pose; + + // Skeleton mesosoma pose + if (auto it = mesosoma_skeleton.bone_map.find("mesosoma"); it != mesosoma_skeleton.bone_map.end()) + bind_pose[mesosoma_bone] = mesosoma_skeleton.bind_pose.at(it->second); + + // Skeleton forelegs pose + if (auto it = legs_skeleton.bone_map.find("procoxa_l"); it != legs_skeleton.bone_map.end()) + bind_pose[procoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("procoxa_r"); it != legs_skeleton.bone_map.end()) + bind_pose[procoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("profemur_l"); it != legs_skeleton.bone_map.end()) + bind_pose[profemur_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("profemur_r"); it != legs_skeleton.bone_map.end()) + bind_pose[profemur_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("protibia_l"); it != legs_skeleton.bone_map.end()) + bind_pose[protibia_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("protibia_r"); it != legs_skeleton.bone_map.end()) + bind_pose[protibia_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus1_l"); it != legs_skeleton.bone_map.end()) + bind_pose[protarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus1_r"); it != legs_skeleton.bone_map.end()) + bind_pose[protarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); + + // Skeleton midlegs pose + if (auto it = legs_skeleton.bone_map.find("mesocoxa_l"); it != legs_skeleton.bone_map.end()) + bind_pose[mesocoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesocoxa_r"); it != legs_skeleton.bone_map.end()) + bind_pose[mesocoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesofemur_l"); it != legs_skeleton.bone_map.end()) + bind_pose[mesofemur_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesofemur_r"); it != legs_skeleton.bone_map.end()) + bind_pose[mesofemur_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotibia_l"); it != legs_skeleton.bone_map.end()) + bind_pose[mesotibia_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotibia_r"); it != legs_skeleton.bone_map.end()) + bind_pose[mesotibia_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus1_l"); it != legs_skeleton.bone_map.end()) + bind_pose[mesotarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus1_r"); it != legs_skeleton.bone_map.end()) + bind_pose[mesotarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); + + // Skeleton hindlegs pose + if (auto it = legs_skeleton.bone_map.find("metacoxa_l"); it != legs_skeleton.bone_map.end()) + bind_pose[metacoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metacoxa_r"); it != legs_skeleton.bone_map.end()) + bind_pose[metacoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metafemur_l"); it != legs_skeleton.bone_map.end()) + bind_pose[metafemur_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metafemur_r"); it != legs_skeleton.bone_map.end()) + bind_pose[metafemur_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metatibia_l"); it != legs_skeleton.bone_map.end()) + bind_pose[metatibia_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metatibia_r"); it != legs_skeleton.bone_map.end()) + bind_pose[metatibia_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus1_l"); it != legs_skeleton.bone_map.end()) + bind_pose[metatarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus1_r"); it != legs_skeleton.bone_map.end()) + bind_pose[metatarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); + + // Skeleton head pose + bind_pose[head_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("head")) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("head")); + + // Skeleton mandibles pose + bind_pose[mandible_l_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_l")) * mandibles_skeleton.bind_pose.at(mandibles_skeleton.bone_map.at("mandible_l")); + bind_pose[mandible_r_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_r")) * mandibles_skeleton.bind_pose.at(mandibles_skeleton.bone_map.at("mandible_r")); + + // Skeleton antennae pose + bind_pose[antennomere1_l_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_l")) * antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere1_l")); + bind_pose[antennomere1_r_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_r")) * antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere1_r")); + bind_pose[antennomere2_l_bone] = antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere2_l")); + bind_pose[antennomere2_r_bone] = antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere2_r")); + + // Skeleton waist pose + if (petiole_present) + { + bind_pose[petiole_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("petiole")); + } + if (postpetiole_present) + { + bind_pose[postpetiole_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("postpetiole")); + } + + // Skeleton gaster pose + if (postpetiole_present) + { + bind_pose[gaster_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("postpetiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); + } + else if (petiole_present) + { + bind_pose[gaster_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("petiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); + } + else + { + bind_pose[gaster_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); + } + + // Skeleton sting pose + if (sting_present) + { + bind_pose[sting_bone] = gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("sting")) * sting_skeleton->bind_pose.at(sting_skeleton->bone_map.at("sting")); + } + + // Calculate the skeleton-space bind pose + pose bind_pose_ss; + ::concatenate(bind_pose, bind_pose_ss); + + // Calculate inverse skeleton-space bind pose + ::inverse(bind_pose_ss, skeleton.inverse_bind_pose); + + // Get number of vertex indices for each body part + std::uint32_t mesosoma_index_count = (mesosoma_model->get_groups()).front().index_count; + std::uint32_t legs_index_count = (legs_model->get_groups()).front().index_count; + std::uint32_t head_index_count = (head_model->get_groups()).front().index_count; + std::uint32_t mandibles_index_count = (mandibles_model->get_groups()).front().index_count; + std::uint32_t antennae_index_count = (antennae_model->get_groups()).front().index_count; + std::uint32_t waist_index_count = (waist_model->get_groups()).front().index_count; + std::uint32_t gaster_index_count = (gaster_model->get_groups()).front().index_count; + std::uint32_t sting_index_count = (sting_present) ? (sting_model->get_groups()).front().index_count : 0; + std::uint32_t eyes_index_count = (eyes_present) ? (eyes_model->get_groups()).front().index_count : 0; + std::uint32_t lateral_ocelli_index_count = (lateral_ocelli_present) ? (lateral_ocelli_model->get_groups()).front().index_count : 0; + std::uint32_t median_ocellus_index_count = (median_ocellus_present) ? (median_ocellus_model->get_groups()).front().index_count : 0; + std::uint32_t forewings_index_count = (wings_present) ? (forewings_model->get_groups()).front().index_count : 0; + std::uint32_t hindwings_index_count = (wings_present) ? (hindwings_model->get_groups()).front().index_count : 0; + std::uint32_t exoskeleton_index_count = + mesosoma_index_count + + legs_index_count + + head_index_count + + mandibles_index_count + + antennae_index_count + + waist_index_count + + gaster_index_count + + sting_index_count; + + // Calculate transform from legs space to body space + const math::transform& legs_to_body = math::transform::identity; + + // Reskin leg bones + std::unordered_set old_procoxa_l_indices; + if (auto it = legs_skeleton.bone_map.find("procoxa_l"); it != legs_skeleton.bone_map.end()) + old_procoxa_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_procoxa_l_indices, procoxa_l_bone_index, legs_to_body); + std::unordered_set old_profemur_l_indices; + if (auto it = legs_skeleton.bone_map.find("profemur_l"); it != legs_skeleton.bone_map.end()) + old_profemur_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_profemur_l_indices, profemur_l_bone_index, legs_to_body); + std::unordered_set old_protibia_l_indices; + if (auto it = legs_skeleton.bone_map.find("protibia_l"); it != legs_skeleton.bone_map.end()) + old_protibia_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protibia_l_indices, protibia_l_bone_index, legs_to_body); + std::unordered_set old_protarsus_l_indices; + if (auto it = legs_skeleton.bone_map.find("protarsus1_l"); it != legs_skeleton.bone_map.end()) + old_protarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus2_l"); it != legs_skeleton.bone_map.end()) + old_protarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus3_l"); it != legs_skeleton.bone_map.end()) + old_protarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus4_l"); it != legs_skeleton.bone_map.end()) + old_protarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus5_l"); it != legs_skeleton.bone_map.end()) + old_protarsus_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protarsus_l_indices, protarsus_l_bone_index, legs_to_body); + + std::unordered_set old_procoxa_r_indices; + if (auto it = legs_skeleton.bone_map.find("procoxa_r"); it != legs_skeleton.bone_map.end()) + old_procoxa_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_procoxa_r_indices, procoxa_r_bone_index, legs_to_body); + std::unordered_set old_profemur_r_indices; + if (auto it = legs_skeleton.bone_map.find("profemur_r"); it != legs_skeleton.bone_map.end()) + old_profemur_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_profemur_r_indices, profemur_r_bone_index, legs_to_body); + std::unordered_set old_protibia_r_indices; + if (auto it = legs_skeleton.bone_map.find("protibia_r"); it != legs_skeleton.bone_map.end()) + old_protibia_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protibia_r_indices, protibia_r_bone_index, legs_to_body); + std::unordered_set old_protarsus_r_indices; + if (auto it = legs_skeleton.bone_map.find("protarsus1_r"); it != legs_skeleton.bone_map.end()) + old_protarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus2_r"); it != legs_skeleton.bone_map.end()) + old_protarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus3_r"); it != legs_skeleton.bone_map.end()) + old_protarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus4_r"); it != legs_skeleton.bone_map.end()) + old_protarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus5_r"); it != legs_skeleton.bone_map.end()) + old_protarsus_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protarsus_r_indices, protarsus_r_bone_index, legs_to_body); + + std::unordered_set old_mesocoxa_l_indices; + if (auto it = legs_skeleton.bone_map.find("mesocoxa_l"); it != legs_skeleton.bone_map.end()) + old_mesocoxa_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesocoxa_l_indices, mesocoxa_l_bone_index, legs_to_body); + std::unordered_set old_mesofemur_l_indices; + if (auto it = legs_skeleton.bone_map.find("mesofemur_l"); it != legs_skeleton.bone_map.end()) + old_mesofemur_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesofemur_l_indices, mesofemur_l_bone_index, legs_to_body); + std::unordered_set old_mesotibia_l_indices; + if (auto it = legs_skeleton.bone_map.find("mesotibia_l"); it != legs_skeleton.bone_map.end()) + old_mesotibia_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotibia_l_indices, mesotibia_l_bone_index, legs_to_body); + std::unordered_set old_mesotarsus_l_indices; + if (auto it = legs_skeleton.bone_map.find("mesotarsus1_l"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus2_l"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus3_l"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus4_l"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus5_l"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotarsus_l_indices, mesotarsus_l_bone_index, legs_to_body); + + std::unordered_set old_mesocoxa_r_indices; + if (auto it = legs_skeleton.bone_map.find("mesocoxa_r"); it != legs_skeleton.bone_map.end()) + old_mesocoxa_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesocoxa_r_indices, mesocoxa_r_bone_index, legs_to_body); + std::unordered_set old_mesofemur_r_indices; + if (auto it = legs_skeleton.bone_map.find("mesofemur_r"); it != legs_skeleton.bone_map.end()) + old_mesofemur_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesofemur_r_indices, mesofemur_r_bone_index, legs_to_body); + std::unordered_set old_mesotibia_r_indices; + if (auto it = legs_skeleton.bone_map.find("mesotibia_r"); it != legs_skeleton.bone_map.end()) + old_mesotibia_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotibia_r_indices, mesotibia_r_bone_index, legs_to_body); + std::unordered_set old_mesotarsus_r_indices; + if (auto it = legs_skeleton.bone_map.find("mesotarsus1_r"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus2_r"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus3_r"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus4_r"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus5_r"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotarsus_r_indices, mesotarsus_r_bone_index, legs_to_body); + + std::unordered_set old_metacoxa_l_indices; + if (auto it = legs_skeleton.bone_map.find("metacoxa_l"); it != legs_skeleton.bone_map.end()) + old_metacoxa_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metacoxa_l_indices, metacoxa_l_bone_index, legs_to_body); + std::unordered_set old_metafemur_l_indices; + if (auto it = legs_skeleton.bone_map.find("metafemur_l"); it != legs_skeleton.bone_map.end()) + old_metafemur_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metafemur_l_indices, metafemur_l_bone_index, legs_to_body); + std::unordered_set old_metatibia_l_indices; + if (auto it = legs_skeleton.bone_map.find("metatibia_l"); it != legs_skeleton.bone_map.end()) + old_metatibia_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatibia_l_indices, metatibia_l_bone_index, legs_to_body); + std::unordered_set old_metatarsus_l_indices; + if (auto it = legs_skeleton.bone_map.find("metatarsus1_l"); it != legs_skeleton.bone_map.end()) + old_metatarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus2_l"); it != legs_skeleton.bone_map.end()) + old_metatarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus3_l"); it != legs_skeleton.bone_map.end()) + old_metatarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus4_l"); it != legs_skeleton.bone_map.end()) + old_metatarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus5_l"); it != legs_skeleton.bone_map.end()) + old_metatarsus_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatarsus_l_indices, metatarsus_l_bone_index, legs_to_body); + + std::unordered_set old_metacoxa_r_indices; + if (auto it = legs_skeleton.bone_map.find("metacoxa_r"); it != legs_skeleton.bone_map.end()) + old_metacoxa_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metacoxa_r_indices, metacoxa_r_bone_index, legs_to_body); + std::unordered_set old_metafemur_r_indices; + if (auto it = legs_skeleton.bone_map.find("metafemur_r"); it != legs_skeleton.bone_map.end()) + old_metafemur_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metafemur_r_indices, metafemur_r_bone_index, legs_to_body); + std::unordered_set old_metatibia_r_indices; + if (auto it = legs_skeleton.bone_map.find("metatibia_r"); it != legs_skeleton.bone_map.end()) + old_metatibia_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatibia_r_indices, metatibia_r_bone_index, legs_to_body); + std::unordered_set old_metatarsus_r_indices; + if (auto it = legs_skeleton.bone_map.find("metatarsus1_r"); it != legs_skeleton.bone_map.end()) + old_metatarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus2_r"); it != legs_skeleton.bone_map.end()) + old_metatarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus3_r"); it != legs_skeleton.bone_map.end()) + old_metatarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus4_r"); it != legs_skeleton.bone_map.end()) + old_metatarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus5_r"); it != legs_skeleton.bone_map.end()) + old_metatarsus_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatarsus_r_indices, metatarsus_r_bone_index, legs_to_body); + + // Calculate transform from head space to body space + math::transform head_to_body = bind_pose_ss.at(mesosoma_bone) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("head")); + + // Reskin head bone + std::unordered_set old_head_bone_indices; + if (auto it = head_skeleton.bone_map.find("head"); it != head_skeleton.bone_map.end()) + old_head_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + head_vbo_offset, head_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_bone_indices, head_bone_index, head_to_body); + + // Calculate transforms from mandibles space to body space + math::transform mandible_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_l")); + math::transform mandible_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_r")); + + // Reskin mandible bones + std::unordered_set old_head_mandible_l_bone_indices; + if (auto it = mandibles_skeleton.bone_map.find("mandible_l"); it != mandibles_skeleton.bone_map.end()) + old_head_mandible_l_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + mandibles_vbo_offset, mandibles_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_mandible_l_bone_indices, mandible_l_bone_index, mandible_l_to_body); + std::unordered_set old_head_mandible_r_bone_indices; + if (auto it = mandibles_skeleton.bone_map.find("mandible_r"); it != mandibles_skeleton.bone_map.end()) + old_head_mandible_r_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + mandibles_vbo_offset, mandibles_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_mandible_r_bone_indices, mandible_r_bone_index, mandible_r_to_body); + + // Calculate transforms from antennae space to body space + math::transform antenna_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_l")); + math::transform antenna_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_r")); + + // Reskin antennomere1 bones + std::unordered_set old_antennomere1_l_indices; + if (auto it = antennae_skeleton.bone_map.find("antennomere1_l"); it != antennae_skeleton.bone_map.end()) + old_antennomere1_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere1_l_indices, antennomere1_l_bone_index, antenna_l_to_body); + std::unordered_set old_antennomere1_r_indices; + if (auto it = antennae_skeleton.bone_map.find("antennomere1_r"); it != antennae_skeleton.bone_map.end()) + old_antennomere1_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere1_r_indices, antennomere1_r_bone_index, antenna_r_to_body); + + // Reskin antennomere2+ bones + const std::vector antennomere_bone_names = + { + "antennomere2", + "antennomere3", + "antennomere4", + "antennomere5", + "antennomere6", + "antennomere7", + "antennomere8", + "antennomere9", + "antennomere10", + "antennomere11", + "antennomere12", + "antennomere13" + }; + std::unordered_set old_antennomere_l_indices; + for (const auto& bone_name: antennomere_bone_names) + if (auto it = antennae_skeleton.bone_map.find(bone_name + "_l"); it != antennae_skeleton.bone_map.end()) + old_antennomere_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere_l_indices, antennomere2_l_bone_index, antenna_l_to_body); + std::unordered_set old_antennomere_r_indices; + for (const auto& bone_name: antennomere_bone_names) + if (auto it = antennae_skeleton.bone_map.find(bone_name + "_r"); it != antennae_skeleton.bone_map.end()) + old_antennomere_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere_r_indices, antennomere2_r_bone_index, antenna_r_to_body); + + // Calculate transform from waist space to body space + math::transform waist_to_body = bind_pose_ss.at(mesosoma_bone) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")); + + // Reskin waist bones + std::unordered_set old_petiole_bone_indices; + if (auto it = waist_skeleton.bone_map.find("petiole"); it != waist_skeleton.bone_map.end()) + old_petiole_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + waist_vbo_offset, waist_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_petiole_bone_indices, petiole_bone_index, waist_to_body); + if (postpetiole_present) + { + std::unordered_set old_postpetiole_bone_indices; + if (auto it = waist_skeleton.bone_map.find("postpetiole"); it != waist_skeleton.bone_map.end()) + old_postpetiole_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + waist_vbo_offset, waist_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_postpetiole_bone_indices, postpetiole_bone_index, waist_to_body); + } + + // Calculate transform from gaster space to body space + math::transform gaster_to_body = bind_pose_ss.at(bone_parent_index(gaster_bone)) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("gaster")); + + // Reskin gaster bones + std::unordered_set old_gaster_bone_indices; + if (auto it = gaster_skeleton.bone_map.find("gaster"); it != gaster_skeleton.bone_map.end()) + old_gaster_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + gaster_vbo_offset, gaster_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_gaster_bone_indices, gaster_bone_index, gaster_to_body); + + if (sting_present) + { + // Calculate transform from sting space to body space + math::transform sting_to_body = gaster_to_body * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("sting")); + + // Reskin sting bones + std::unordered_set old_sting_bone_indices; + if (auto it = sting_skeleton->bone_map.find("sting"); it != sting_skeleton->bone_map.end()) + old_sting_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + sting_vbo_offset, sting_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_sting_bone_indices, sting_bone_index, sting_to_body); + } + + if (eyes_present) + { + // Calculate transforms from eyes space to body space + math::transform eye_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("eye_l")); + math::transform eye_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("eye_r")); + + // Reskin eye bones + std::unordered_set old_eye_l_bone_indices; + if (auto it = eyes_skeleton->bone_map.find("eye_l"); it != eyes_skeleton->bone_map.end()) + old_eye_l_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + eyes_vbo_offset, eyes_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_eye_l_bone_indices, head_bone_index, eye_l_to_body); + std::unordered_set old_eye_r_bone_indices; + if (auto it = eyes_skeleton->bone_map.find("eye_r"); it != eyes_skeleton->bone_map.end()) + old_eye_r_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + eyes_vbo_offset, eyes_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_eye_r_bone_indices, head_bone_index, eye_r_to_body); + } + + if (lateral_ocelli_present) + { + // Calculate transforms from lateral ocelli space to body space + math::transform ocellus_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_l")); + math::transform ocellus_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_r")); + + // Reskin lateral ocelli bones + std::unordered_set old_ocellus_l_bone_indices; + if (auto it = lateral_ocelli_skeleton->bone_map.find("ocellus_l"); it != lateral_ocelli_skeleton->bone_map.end()) + old_ocellus_l_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + lateral_ocelli_vbo_offset, lateral_ocelli_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_l_bone_indices, head_bone_index, ocellus_l_to_body); + std::unordered_set old_ocellus_r_bone_indices; + if (auto it = lateral_ocelli_skeleton->bone_map.find("ocellus_r"); it != lateral_ocelli_skeleton->bone_map.end()) + old_ocellus_r_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + lateral_ocelli_vbo_offset, lateral_ocelli_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_r_bone_indices, head_bone_index, ocellus_r_to_body); + } + + if (median_ocellus_present) + { + // Calculate transforms from lateral ocelli space to body space + math::transform ocellus_m_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_m")); + + // Reskin lateral ocelli bones + std::unordered_set old_ocellus_m_bone_indices; + if (auto it = median_ocellus_skeleton->bone_map.find("ocellus_m"); it != median_ocellus_skeleton->bone_map.end()) + old_ocellus_m_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data.data() + median_ocellus_vbo_offset, median_ocellus_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_m_bone_indices, head_bone_index, ocellus_m_to_body); + } + + // Upload vertex buffer data to model VBO + model->get_vertex_buffer()->repurpose(gl::buffer_usage::static_draw, vertex_buffer_size, vertex_buffer_data); + + // Construct exoskeleton model group + model->get_groups().resize(1); + render::model_group& exoskeleton_group = model->get_groups().back(); + exoskeleton_group.id = "exoskeleton"; + exoskeleton_group.material = exoskeleton_material; + exoskeleton_group.drawing_mode = gl::drawing_mode::triangles; + exoskeleton_group.start_index = 0; + exoskeleton_group.index_count = exoskeleton_index_count; + + std::uint32_t index_offset = exoskeleton_index_count; + if (eyes_present) + { + // Construct eyes model group + model->get_groups().resize(model->get_groups().size() + 1); + render::model_group& eyes_group = model->get_groups().back(); + eyes_group.id = "eyes"; + eyes_group.material = eyes_model->get_groups().front().material; + eyes_group.drawing_mode = gl::drawing_mode::triangles; + eyes_group.start_index = index_offset; + eyes_group.index_count = eyes_index_count; + + index_offset += eyes_index_count; + } + + if (lateral_ocelli_present || median_ocellus_present) + { + // Construct ocelli model group + model->get_groups().resize(model->get_groups().size() + 1); + render::model_group& ocelli_group = model->get_groups().back(); + ocelli_group.id = "ocelli"; + ocelli_group.drawing_mode = gl::drawing_mode::triangles; + ocelli_group.start_index = index_offset; + + std::uint32_t index_count = 0; + if (lateral_ocelli_present) + { + index_count += lateral_ocelli_index_count; + index_offset += lateral_ocelli_index_count; + ocelli_group.material = lateral_ocelli_model->get_groups().front().material; + } + if (median_ocellus_present) + { + index_count += median_ocellus_index_count; + index_offset += median_ocellus_index_count; + if (!lateral_ocelli_present) + { + ocelli_group.material = median_ocellus_model->get_groups().front().material; + } + } + + ocelli_group.index_count = index_count; + } + + if (wings_present) + { + // Construct forewings model group + model->get_groups().resize(model->get_groups().size() + 1); + render::model_group& forewings_group = model->get_groups().back(); + forewings_group.id = "forewings"; + forewings_group.material = forewings_model->get_groups().front().material; + forewings_group.drawing_mode = gl::drawing_mode::triangles; + forewings_group.start_index = index_offset; + forewings_group.index_count = forewings_index_count; + index_offset += forewings_index_count; + + // Construct hindwings model group + model->get_groups().resize(model->get_groups().size() + 1); + render::model_group& hindwings_group = model->get_groups().back(); + hindwings_group.id = "hindwings"; + hindwings_group.material = hindwings_model->get_groups().front().material; + hindwings_group.drawing_mode = gl::drawing_mode::triangles; + hindwings_group.start_index = index_offset; + hindwings_group.index_count = hindwings_index_count; + index_offset += hindwings_index_count; + } + + // Calculate model bounding box + model->get_bounds() = calculate_bounds(vertex_buffer_data.data(), index_offset, *position_attribute); + + return model; +} + +void reskin_vertices +( + std::byte* vertex_data, + std::size_t index_count, + const gl::vertex_attribute& position_attribute, + const gl::vertex_attribute& normal_attribute, + const gl::vertex_attribute& tangent_attribute, + const gl::vertex_attribute& bone_index_attribute, + const std::unordered_set& old_bone_indices, + std::uint8_t new_bone_index, + const math::transform& transform +) +{ + std::byte* position_data = vertex_data + position_attribute.offset; + std::byte* normal_data = vertex_data + normal_attribute.offset; + std::byte* tangent_data = vertex_data + tangent_attribute.offset; + std::byte* bone_index_data = vertex_data + bone_index_attribute.offset; + + for (std::size_t i = 0; i < index_count; ++i) + { + // Get bone index of current vertex + float* bone_index = reinterpret_cast(bone_index_data + bone_index_attribute.stride * i); + + // Skip irrelevant bones + if (!old_bone_indices.count(static_cast(*bone_index + 0.5f))) + continue; + + // Get vertex position + float* x = reinterpret_cast(position_data + position_attribute.stride * i); + float* y = x + 1; + float* z = y + 1; + + // Get vertex normal + float* nx = reinterpret_cast(normal_data + normal_attribute.stride * i); + float* ny = nx + 1; + float* nz = ny + 1; + + // Get vertex tangent + float* tx = reinterpret_cast(tangent_data + tangent_attribute.stride * i); + float* ty = tx + 1; + float* tz = ty + 1; + //float* bts = tz + 1; + + // Transform vertex attributes + float3 position = transform * float3{*x, *y, *z}; + float3 normal = math::normalize(transform.rotation * float3{*nx, *ny, *nz}); + float3 tangent = transform.rotation * float3{*tx, *ty, *tz}; + + // Update vertex data + *x = position.x(); + *y = position.y(); + *z = position.z(); + *nx = normal.x(); + *ny = normal.y(); + *nz = normal.z(); + *tx = tangent.x(); + *ty = tangent.y(); + *tz = tangent.z(); + //*bts = ... + *bone_index = static_cast(new_bone_index); + } +} + +geom::aabb calculate_bounds(const std::byte* vertex_data, std::size_t index_count, const gl::vertex_attribute& position_attribute) +{ + const std::byte* position_data = vertex_data + position_attribute.offset; + + geom::aabb bounds; + bounds.min_point.x() = std::numeric_limits::infinity(); + bounds.min_point.y() = std::numeric_limits::infinity(); + bounds.min_point.z() = std::numeric_limits::infinity(); + bounds.max_point.x() = -std::numeric_limits::infinity(); + bounds.max_point.y() = -std::numeric_limits::infinity(); + bounds.max_point.z() = -std::numeric_limits::infinity(); + + for (std::size_t i = 0; i < index_count; ++i) + { + // Get vertex position + const float* x = reinterpret_cast(position_data + position_attribute.stride * i); + const float* y = x + 1; + const float* z = y + 1; + + bounds.min_point.x() = std::min(*x, bounds.min_point.x()); + bounds.min_point.y() = std::min(*y, bounds.min_point.y()); + bounds.min_point.z() = std::min(*z, bounds.min_point.z()); + bounds.max_point.x() = std::max(*x, bounds.max_point.x()); + bounds.max_point.y() = std::max(*y, bounds.max_point.y()); + bounds.max_point.z() = std::max(*z, bounds.max_point.z()); + } + + return bounds; +} diff --git a/src/game/ant/morphogenesis.hpp b/src/game/ant/ant-morphogenesis.hpp similarity index 89% rename from src/game/ant/morphogenesis.hpp rename to src/game/ant/ant-morphogenesis.hpp index 94d4fb5..f7bc92b 100644 --- a/src/game/ant/morphogenesis.hpp +++ b/src/game/ant/ant-morphogenesis.hpp @@ -20,10 +20,9 @@ #ifndef ANTKEEPER_GAME_ANT_MORPHOGENESIS_HPP #define ANTKEEPER_GAME_ANT_MORPHOGENESIS_HPP -#include "game/ant/phenome.hpp" +#include "game/ant/ant-phenome.hpp" #include - -namespace ant { +#include /** * Generates a 3D model of an ant given its phenome. @@ -32,8 +31,6 @@ namespace ant { * * @return 3D model of the given phenome. */ -render::model* morphogenesis(const phenome& phenome); - -} // namespace ant +std::unique_ptr ant_morphogenesis(const ant_phenome& phenome); #endif // ANTKEEPER_GAME_ANT_MORPHOGENESIS_HPP diff --git a/src/game/ant/phenome.cpp b/src/game/ant/ant-phenome.cpp similarity index 84% rename from src/game/ant/phenome.cpp rename to src/game/ant/ant-phenome.cpp index dd234af..a4979e7 100644 --- a/src/game/ant/phenome.cpp +++ b/src/game/ant/ant-phenome.cpp @@ -17,12 +17,9 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/phenome.hpp" +#include "game/ant/ant-phenome.hpp" -namespace ant { - -phenome::phenome(const genome& genome, caste caste): - phenome() +ant_phenome::ant_phenome(const ant_genome& genome, ant_caste caste) { if (genome.antennae) if (auto it = genome.antennae->phenes.find(caste); it != genome.antennae->phenes.end()) @@ -84,30 +81,3 @@ phenome::phenome(const genome& genome, caste caste): if (auto it = genome.wings->phenes.find(caste); it != genome.wings->phenes.end()) wings = &it->second; } - -phenome::phenome(): - antennae(nullptr), - body_size(nullptr), - cocoon(nullptr), - diet(nullptr), - egg(nullptr), - eyes(nullptr), - foraging_time(nullptr), - founding_mode(nullptr), - gaster(nullptr), - head(nullptr), - larva(nullptr), - legs(nullptr), - mandibles(nullptr), - mesosoma(nullptr), - nest_site(nullptr), - ocelli(nullptr), - pigmentation(nullptr), - pilosity(nullptr), - sculpturing(nullptr), - sting(nullptr), - waist(nullptr), - wings(nullptr) -{} - -} // namespace ant diff --git a/src/game/ant/ant-phenome.hpp b/src/game/ant/ant-phenome.hpp new file mode 100644 index 0000000..6254ef3 --- /dev/null +++ b/src/game/ant/ant-phenome.hpp @@ -0,0 +1,88 @@ +/* + * 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 . + */ + +#ifndef ANTKEEPER_GAME_ANT_PHENOME_HPP +#define ANTKEEPER_GAME_ANT_PHENOME_HPP + +#include "game/ant/genes/ant-antennae-gene.hpp" +#include "game/ant/genes/ant-body-size-gene.hpp" +#include "game/ant/genes/ant-cocoon-gene.hpp" +#include "game/ant/genes/ant-diet-gene.hpp" +#include "game/ant/genes/ant-egg-gene.hpp" +#include "game/ant/genes/ant-eyes-gene.hpp" +#include "game/ant/genes/ant-foraging-time-gene.hpp" +#include "game/ant/genes/ant-founding-mode-gene.hpp" +#include "game/ant/genes/ant-gaster-gene.hpp" +#include "game/ant/genes/ant-head-gene.hpp" +#include "game/ant/genes/ant-larva-gene.hpp" +#include "game/ant/genes/ant-legs-gene.hpp" +#include "game/ant/genes/ant-mandibles-gene.hpp" +#include "game/ant/genes/ant-mesosoma-gene.hpp" +#include "game/ant/genes/ant-nest-site-gene.hpp" +#include "game/ant/genes/ant-ocelli-gene.hpp" +#include "game/ant/genes/ant-pigmentation-gene.hpp" +#include "game/ant/genes/ant-pilosity-gene.hpp" +#include "game/ant/genes/ant-sculpturing-gene.hpp" +#include "game/ant/genes/ant-sting-gene.hpp" +#include "game/ant/genes/ant-waist-gene.hpp" +#include "game/ant/genes/ant-wings-gene.hpp" +#include "game/ant/ant-genome.hpp" +#include "game/ant/ant-caste.hpp" + +/** + * Complete set of ant phenes. + */ +struct ant_phenome +{ + /** + * Constructs an ant phenome for a given caste. + * + * @param genome Ant genome. + * @param caste Ant caste. + */ + ant_phenome(const ant_genome& genome, ant_caste caste); + + /// Constructs an empty ant phenome. + ant_phenome() = default; + + const ant_antennae_phene* antennae{nullptr}; + const ant_body_size_phene* body_size{nullptr}; + const ant_cocoon_phene* cocoon{nullptr}; + const ant_diet_phene* diet{nullptr}; + const ant_egg_phene* egg{nullptr}; + const ant_eyes_phene* eyes{nullptr}; + const ant_foraging_time_phene* foraging_time{nullptr}; + const ant_founding_mode_phene* founding_mode{nullptr}; + const ant_gaster_phene* gaster{nullptr}; + const ant_head_phene* head{nullptr}; + const ant_larva_phene* larva{nullptr}; + const ant_legs_phene* legs{nullptr}; + const ant_mandibles_phene* mandibles{nullptr}; + const ant_mesosoma_phene* mesosoma{nullptr}; + const ant_nest_site_phene* nest_site{nullptr}; + const ant_ocelli_phene* ocelli{nullptr}; + const ant_pigmentation_phene* pigmentation{nullptr}; + const ant_pilosity_phene* pilosity{nullptr}; + const ant_sculpturing_phene* sculpturing{nullptr}; + const ant_sting_phene* sting{nullptr}; + const ant_waist_phene* waist{nullptr}; + const ant_wings_phene* wings{nullptr}; +}; + +#endif // ANTKEEPER_GAME_ANT_PHENOME_HPP diff --git a/src/game/ant/species.hpp b/src/game/ant/ant-species.hpp similarity index 81% rename from src/game/ant/species.hpp rename to src/game/ant/ant-species.hpp index b87a2a1..a21fd40 100644 --- a/src/game/ant/species.hpp +++ b/src/game/ant/ant-species.hpp @@ -20,22 +20,19 @@ #ifndef ANTKEEPER_GAME_ANT_SPECIES_HPP #define ANTKEEPER_GAME_ANT_SPECIES_HPP -#include "game/ant/caste.hpp" -#include "game/ant/phenome.hpp" +#include "game/ant/ant-caste.hpp" +#include "game/ant/ant-phenome.hpp" #include #include +#include -namespace ant { - -struct species +struct ant_species { /// Caste-specific phenomes - std::unordered_map phenomes; + std::unordered_map phenomes; /// Caste-specific models - std::unordered_map models; + std::unordered_map> models; }; -} // namespace ant - #endif // ANTKEEPER_GAME_ANT_SPECIES_HPP diff --git a/src/game/ant/subcaste.hpp b/src/game/ant/ant-subcaste.hpp similarity index 93% rename from src/game/ant/subcaste.hpp rename to src/game/ant/ant-subcaste.hpp index 898c2c8..f87eb6e 100644 --- a/src/game/ant/subcaste.hpp +++ b/src/game/ant/ant-subcaste.hpp @@ -20,12 +20,12 @@ #ifndef ANTKEEPER_GAME_ANT_SUBCASTE_HPP #define ANTKEEPER_GAME_ANT_SUBCASTE_HPP -namespace ant { +#include /** - * Ant subcaste enumerations. + * Ant subcaste types. */ -enum class subcaste +enum class ant_subcaste: std::uint8_t { /// Worker from the queen's first batch of eggs, smaller than normal workers. nanitic, @@ -52,6 +52,4 @@ enum class subcaste brachypterous }; -} // namespace ant - #endif // ANTKEEPER_GAME_ANT_SUBCASTE_HPP diff --git a/src/game/ant/swarm.cpp b/src/game/ant/ant-swarm.cpp similarity index 89% rename from src/game/ant/swarm.cpp rename to src/game/ant/ant-swarm.cpp index 9640c03..60b66eb 100644 --- a/src/game/ant/swarm.cpp +++ b/src/game/ant/ant-swarm.cpp @@ -1,179 +1,176 @@ -/* - * 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 . - */ - -#include "game/ant/swarm.hpp" -#include "game/components/transform-component.hpp" -#include "game/components/steering-component.hpp" -#include "game/components/model-component.hpp" -#include "game/components/picking-component.hpp" -#include "game/components/caste-component.hpp" -#include -#include -#include -#include -#include - -namespace ant { - -/** - * Generates a random point in a unit sphere. - * - * @see https://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability/87238#87238 - */ -template -static math::vector3 sphere_random(Generator& rng) -{ - std::uniform_real_distribution distribution(T{-1}, T{1}); - - math::vector3 position; - for (std::size_t i = 0; i < 3; ++i) - position[i] = distribution(rng); - - return math::normalize(position) * std::cbrt(distribution(rng)); -} - -entity::id create_swarm(::game& ctx) -{ - // Determine swarm properties - const float3 swarm_center = {0, 100, 0}; - const float swarm_radius = 25.0f; - const std::size_t male_count = 50; - const std::size_t queen_count = 50; - const std::size_t alate_count = male_count + queen_count; - - const float3 male_scale = {0.5, 0.5, 0.5}; - const float3 queen_scale = {1, 1, 1}; - - // Init transform component - ::transform_component transform; - transform.local = math::transform::identity; - transform.world = transform.local; - transform.warp = true; - - // Init picking component - ::picking_component picking; - picking.sphere = {float3{0, 0, 0}, 0.5f * 2.0f}; - std::uint32_t male_picking_flags = 0b01; - std::uint32_t queen_picking_flags = 0b10; - - // Create swarm entity - entity::id swarm_eid = ctx.entity_registry->create(); - transform.local.translation = swarm_center; - transform.world = transform.local; - transform.warp = true; - ctx.entity_registry->emplace<::transform_component>(swarm_eid, transform); - - // Init male model component - ::model_component male_model; - male_model.render_model = ctx.resource_manager->load("male-boid.mdl"); - male_model.instance_count = 0; - male_model.layers = 1; - - // Init queen model component - ::model_component queen_model; - queen_model.render_model = ctx.resource_manager->load("queen-boid.mdl"); - queen_model.instance_count = 0; - queen_model.layers = 1; - - // Init steering component - ::steering_component steering; - steering.agent.mass = 1.0f; - steering.agent.velocity = {0, 0, 0}; - steering.agent.acceleration = {0, 0, 0}; - steering.agent.max_force = 4.0f; - steering.agent.max_speed = 5.0f; - steering.agent.max_speed_squared = steering.agent.max_speed * steering.agent.max_speed; - steering.agent.orientation = math::quaternion::identity(); - steering.agent.forward = steering.agent.orientation * config::global_forward; - steering.agent.up = steering.agent.orientation * config::global_up; - steering.wander_weight = 1.0f; - steering.wander_noise = math::radians(2000.0f); - steering.wander_distance = 10.0f; - steering.wander_radius = 8.0f; - steering.wander_angle = 0.0f; - steering.wander_angle2 = 0.0f; - steering.seek_weight = 0.2f; - steering.seek_target = swarm_center; - steering.flee_weight = 0.0f; - steering.sum_weights = steering.wander_weight + steering.seek_weight + steering.flee_weight; - - // Init queen caste component - ::caste_component queen_caste; - queen_caste.type = ::ant::caste::queen; - - // Init male caste component - ::caste_component male_caste; - male_caste.type = ::ant::caste::male; - - // Construct and seed random number generator - std::random_device seed; - std::mt19937 rng(seed()); - - // Create alates - for (std::size_t i = 0; i < alate_count; ++i) - { - // Generate random position in swarm sphere - steering.agent.position = swarm_center + sphere_random(rng) * swarm_radius; - transform.local.translation = steering.agent.position; - - entity::id alate_eid = ctx.entity_registry->create(); - ctx.entity_registry->emplace<::steering_component>(alate_eid, steering); - - if (i < male_count) - { - // Create male - ctx.entity_registry->emplace<::caste_component>(alate_eid, male_caste); - ctx.entity_registry->emplace<::model_component>(alate_eid, male_model); - - transform.local.scale = male_scale; - transform.world = transform.local; - ctx.entity_registry->emplace<::transform_component>(alate_eid, transform); - - picking.flags = male_picking_flags; - ctx.entity_registry->emplace<::picking_component>(alate_eid, picking); - } - else - { - // Create queen - ctx.entity_registry->emplace<::caste_component>(alate_eid, queen_caste); - ctx.entity_registry->emplace<::model_component>(alate_eid, queen_model); - - transform.local.scale = queen_scale; - transform.world = transform.local; - ctx.entity_registry->emplace<::transform_component>(alate_eid, transform); - - picking.flags = queen_picking_flags; - ctx.entity_registry->emplace<::picking_component>(alate_eid, picking); - } - } - - return swarm_eid; -} - -void destroy_swarm(::game& ctx, entity::id swarm_eid) -{ - // Destroy alates - auto view = ctx.entity_registry->view<::steering_component>(); - ctx.entity_registry->destroy(view.begin(), view.end()); - - // Destroy swarm - ctx.entity_registry->destroy(swarm_eid); -} - -} // namespace ant +/* + * 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 . + */ + +#include "game/ant/ant-swarm.hpp" +#include "game/components/transform-component.hpp" +#include "game/components/steering-component.hpp" +#include "game/components/model-component.hpp" +#include "game/components/picking-component.hpp" +#include "game/components/ant-caste-component.hpp" +#include +#include +#include +#include +#include +#include + +/** + * Generates a random point in a unit sphere. + * + * @see https://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability/87238#87238 + */ +template +static math::vector3 sphere_random(Generator& rng) +{ + std::uniform_real_distribution distribution(T{-1}, T{1}); + + math::vector3 position; + for (std::size_t i = 0; i < 3; ++i) + position[i] = distribution(rng); + + return math::normalize(position) * std::cbrt(distribution(rng)); +} + +entity::id create_ant_swarm(::game& ctx) +{ + // Determine swarm properties + const float3 swarm_center = {0, 100, 0}; + const float swarm_radius = 25.0f; + const std::size_t male_count = 50; + const std::size_t queen_count = 50; + const std::size_t alate_count = male_count + queen_count; + + const float3 male_scale = {0.5, 0.5, 0.5}; + const float3 queen_scale = {1, 1, 1}; + + // Init transform component + ::transform_component transform; + transform.local = math::transform::identity; + transform.world = transform.local; + transform.warp = true; + + // Init picking component + ::picking_component picking; + picking.sphere = {float3{0, 0, 0}, 0.5f * 2.0f}; + std::uint32_t male_picking_flags = 0b01; + std::uint32_t queen_picking_flags = 0b10; + + // Create swarm entity + entity::id swarm_eid = ctx.entity_registry->create(); + transform.local.translation = swarm_center; + transform.world = transform.local; + transform.warp = true; + ctx.entity_registry->emplace<::transform_component>(swarm_eid, transform); + + // Init male model component + ::model_component male_model; + male_model.render_model = ctx.resource_manager->load("male-boid.mdl"); + male_model.instance_count = 0; + male_model.layers = 1; + + // Init queen model component + ::model_component queen_model; + queen_model.render_model = ctx.resource_manager->load("queen-boid.mdl"); + queen_model.instance_count = 0; + queen_model.layers = 1; + + // Init steering component + ::steering_component steering; + steering.agent.mass = 1.0f; + steering.agent.velocity = {0, 0, 0}; + steering.agent.acceleration = {0, 0, 0}; + steering.agent.max_force = 4.0f; + steering.agent.max_speed = 5.0f; + steering.agent.max_speed_squared = steering.agent.max_speed * steering.agent.max_speed; + steering.agent.orientation = math::quaternion::identity(); + steering.agent.forward = steering.agent.orientation * config::global_forward; + steering.agent.up = steering.agent.orientation * config::global_up; + steering.wander_weight = 1.0f; + steering.wander_noise = math::radians(2000.0f); + steering.wander_distance = 10.0f; + steering.wander_radius = 8.0f; + steering.wander_angle = 0.0f; + steering.wander_angle2 = 0.0f; + steering.seek_weight = 0.2f; + steering.seek_target = swarm_center; + steering.flee_weight = 0.0f; + steering.sum_weights = steering.wander_weight + steering.seek_weight + steering.flee_weight; + + // Init queen caste component + ant_caste_component queen_caste; + queen_caste.type = ant_caste::queen; + + // Init male caste component + ant_caste_component male_caste; + male_caste.type = ant_caste::male; + + // Construct and seed random number generator + std::random_device seed; + std::mt19937 rng(seed()); + + // Create alates + for (std::size_t i = 0; i < alate_count; ++i) + { + // Generate random position in swarm sphere + steering.agent.position = swarm_center + sphere_random(rng) * swarm_radius; + transform.local.translation = steering.agent.position; + + entity::id alate_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace<::steering_component>(alate_eid, steering); + + if (i < male_count) + { + // Create male + ctx.entity_registry->emplace(alate_eid, male_caste); + ctx.entity_registry->emplace<::model_component>(alate_eid, male_model); + + transform.local.scale = male_scale; + transform.world = transform.local; + ctx.entity_registry->emplace<::transform_component>(alate_eid, transform); + + picking.flags = male_picking_flags; + ctx.entity_registry->emplace<::picking_component>(alate_eid, picking); + } + else + { + // Create queen + ctx.entity_registry->emplace(alate_eid, queen_caste); + ctx.entity_registry->emplace<::model_component>(alate_eid, queen_model); + + transform.local.scale = queen_scale; + transform.world = transform.local; + ctx.entity_registry->emplace<::transform_component>(alate_eid, transform); + + picking.flags = queen_picking_flags; + ctx.entity_registry->emplace<::picking_component>(alate_eid, picking); + } + } + + return swarm_eid; +} + +void destroy_ant_swarm(::game& ctx, entity::id swarm_eid) +{ + // Destroy alates + auto view = ctx.entity_registry->view<::steering_component>(); + ctx.entity_registry->destroy(view.begin(), view.end()); + + // Destroy swarm + ctx.entity_registry->destroy(swarm_eid); +} diff --git a/src/game/ant/swarm.hpp b/src/game/ant/ant-swarm.hpp similarity index 87% rename from src/game/ant/swarm.hpp rename to src/game/ant/ant-swarm.hpp index 683491d..b3c1c8a 100644 --- a/src/game/ant/swarm.hpp +++ b/src/game/ant/ant-swarm.hpp @@ -23,11 +23,7 @@ #include "game/game.hpp" #include -namespace ant { - -entity::id create_swarm(::game& ctx); -void destroy_swarm(::game& ctx, entity::id swarm_eid); - -} // namespace ant +entity::id create_ant_swarm(::game& ctx); +void destroy_ant_swarm(::game& ctx, entity::id swarm_eid); #endif // ANTKEEPER_GAME_ANT_SWARM_HPP diff --git a/src/game/ant/gene-pool.hpp b/src/game/ant/gene-pool.hpp deleted file mode 100644 index 1b9ac07..0000000 --- a/src/game/ant/gene-pool.hpp +++ /dev/null @@ -1,83 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_POOL_HPP -#define ANTKEEPER_GAME_ANT_GENE_POOL_HPP - -#include "game/ant/gene-frequency-table.hpp" -#include "game/ant/gene/antennae.hpp" -#include "game/ant/gene/body-size.hpp" -#include "game/ant/gene/cocoon.hpp" -#include "game/ant/gene/diet.hpp" -#include "game/ant/gene/egg.hpp" -#include "game/ant/gene/eyes.hpp" -#include "game/ant/gene/foraging-time.hpp" -#include "game/ant/gene/founding-mode.hpp" -#include "game/ant/gene/gaster.hpp" -#include "game/ant/gene/head.hpp" -#include "game/ant/gene/larva.hpp" -#include "game/ant/gene/legs.hpp" -#include "game/ant/gene/mandibles.hpp" -#include "game/ant/gene/mesosoma.hpp" -#include "game/ant/gene/nest-site.hpp" -#include "game/ant/gene/ocelli.hpp" -#include "game/ant/gene/pigmentation.hpp" -#include "game/ant/gene/pilosity.hpp" -#include "game/ant/gene/sculpturing.hpp" -#include "game/ant/gene/sting.hpp" -#include "game/ant/gene/waist.hpp" -#include "game/ant/gene/wings.hpp" - -namespace ant { - -/** - * Pool of genes from which ant genomes can be generated. - */ -struct gene_pool -{ - /// Gene pool name. - std::string name; - - gene_frequency_table antennae; - gene_frequency_table body_size; - gene_frequency_table cocoon; - gene_frequency_table diet; - gene_frequency_table egg; - gene_frequency_table eyes; - gene_frequency_table foraging_time; - gene_frequency_table founding_mode; - gene_frequency_table gaster; - gene_frequency_table head; - gene_frequency_table larva; - gene_frequency_table legs; - gene_frequency_table mandibles; - gene_frequency_table mesosoma; - gene_frequency_table nest_site; - gene_frequency_table ocelli; - gene_frequency_table pigmentation; - gene_frequency_table pilosity; - gene_frequency_table sculpturing; - gene_frequency_table sting; - gene_frequency_table waist; - gene_frequency_table wings; -}; - -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_POOL_HPP diff --git a/src/game/ant/gene/antennae.hpp b/src/game/ant/gene/antennae.hpp deleted file mode 100644 index 02b1ff1..0000000 --- a/src/game/ant/gene/antennae.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_ANTENNAE_HPP -#define ANTKEEPER_GAME_ANT_GENE_ANTENNAE_HPP - -#include "game/ant/phene/antennae.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic antennae gene. -typedef polyphenic_gene antennae; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_ANTENNAE_HPP diff --git a/src/game/ant/gene/body-size.hpp b/src/game/ant/gene/body-size.hpp deleted file mode 100644 index 02e47d3..0000000 --- a/src/game/ant/gene/body-size.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_BODY_SIZE_HPP -#define ANTKEEPER_GAME_ANT_GENE_BODY_SIZE_HPP - -#include "game/ant/phene/body-size.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic body size gene. -typedef polyphenic_gene body_size; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_BODY_SIZE_HPP diff --git a/src/game/ant/gene/cocoon.hpp b/src/game/ant/gene/cocoon.hpp deleted file mode 100644 index 8a1be19..0000000 --- a/src/game/ant/gene/cocoon.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_COCOON_HPP -#define ANTKEEPER_GAME_ANT_GENE_COCOON_HPP - -#include "game/ant/phene/cocoon.hpp" -#include "game/ant/gene/monophenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Monophenic cocoon gene. -typedef monophenic_gene cocoon; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_COCOON_HPP diff --git a/src/game/ant/gene/diet.hpp b/src/game/ant/gene/diet.hpp deleted file mode 100644 index 501dcbd..0000000 --- a/src/game/ant/gene/diet.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_DIET_HPP -#define ANTKEEPER_GAME_ANT_GENE_DIET_HPP - -#include "game/ant/phene/diet.hpp" -#include "game/ant/gene/monophenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Monophenic diet gene. -typedef monophenic_gene diet; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_DIET_HPP diff --git a/src/game/ant/gene/egg.hpp b/src/game/ant/gene/egg.hpp deleted file mode 100644 index db703cd..0000000 --- a/src/game/ant/gene/egg.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_EGG_HPP -#define ANTKEEPER_GAME_ANT_GENE_EGG_HPP - -#include "game/ant/phene/egg.hpp" -#include "game/ant/gene/monophenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Monophenic egg gene. -typedef monophenic_gene egg; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_EGG_HPP diff --git a/src/game/ant/gene/eyes.hpp b/src/game/ant/gene/eyes.hpp deleted file mode 100644 index cd24783..0000000 --- a/src/game/ant/gene/eyes.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_EYES_HPP -#define ANTKEEPER_GAME_ANT_GENE_EYES_HPP - -#include "game/ant/phene/eyes.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic eyes gene. -typedef polyphenic_gene eyes; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_EYES_HPP diff --git a/src/game/ant/gene/foraging-time.hpp b/src/game/ant/gene/foraging-time.hpp deleted file mode 100644 index 7658911..0000000 --- a/src/game/ant/gene/foraging-time.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_FORAGING_TIME_HPP -#define ANTKEEPER_GAME_ANT_GENE_FORAGING_TIME_HPP - -#include "game/ant/phene/foraging-time.hpp" -#include "game/ant/gene/monophenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Monophenic foraging time gene. -typedef monophenic_gene foraging_time; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_FORAGING_TIME_HPP diff --git a/src/game/ant/gene/founding-mode.hpp b/src/game/ant/gene/founding-mode.hpp deleted file mode 100644 index 17ba4a1..0000000 --- a/src/game/ant/gene/founding-mode.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_FOUNDING_MODE_HPP -#define ANTKEEPER_GAME_ANT_GENE_FOUNDING_MODE_HPP - -#include "game/ant/phene/founding-mode.hpp" -#include "game/ant/gene/monophenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Monophenic founding mode gene. -typedef monophenic_gene founding_mode; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_FOUNDING_MODE_HPP diff --git a/src/game/ant/gene/gaster.hpp b/src/game/ant/gene/gaster.hpp deleted file mode 100644 index 1911f4d..0000000 --- a/src/game/ant/gene/gaster.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_GASTER_HPP -#define ANTKEEPER_GAME_ANT_GENE_GASTER_HPP - -#include "game/ant/phene/gaster.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic gaster gene. -typedef polyphenic_gene gaster; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_GASTER_HPP diff --git a/src/game/ant/gene/head.hpp b/src/game/ant/gene/head.hpp deleted file mode 100644 index 341f62f..0000000 --- a/src/game/ant/gene/head.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_HEAD_HPP -#define ANTKEEPER_GAME_ANT_GENE_HEAD_HPP - -#include "game/ant/phene/head.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic head gene. -typedef polyphenic_gene head; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_HEAD_HPP diff --git a/src/game/ant/gene/larva.hpp b/src/game/ant/gene/larva.hpp deleted file mode 100644 index 4d9b4ce..0000000 --- a/src/game/ant/gene/larva.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_LARVA_HPP -#define ANTKEEPER_GAME_ANT_GENE_LARVA_HPP - -#include "game/ant/phene/larva.hpp" -#include "game/ant/gene/monophenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Monophenic larva gene. -typedef monophenic_gene larva; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_LARVA_HPP diff --git a/src/game/ant/gene/legs.hpp b/src/game/ant/gene/legs.hpp deleted file mode 100644 index d4c484f..0000000 --- a/src/game/ant/gene/legs.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_LEGS_HPP -#define ANTKEEPER_GAME_ANT_GENE_LEGS_HPP - -#include "game/ant/phene/legs.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic legs gene. -typedef polyphenic_gene legs; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_LEGS_HPP diff --git a/src/game/ant/gene/mandibles.hpp b/src/game/ant/gene/mandibles.hpp deleted file mode 100644 index f398135..0000000 --- a/src/game/ant/gene/mandibles.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_MANDIBLES_HPP -#define ANTKEEPER_GAME_ANT_GENE_MANDIBLES_HPP - -#include "game/ant/phene/mandibles.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic mandibles gene. -typedef polyphenic_gene mandibles; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_MANDIBLES_HPP diff --git a/src/game/ant/gene/mesosoma.hpp b/src/game/ant/gene/mesosoma.hpp deleted file mode 100644 index 808910a..0000000 --- a/src/game/ant/gene/mesosoma.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_MESOSOMA_HPP -#define ANTKEEPER_GAME_ANT_GENE_MESOSOMA_HPP - -#include "game/ant/phene/mesosoma.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic mesosoma gene. -typedef polyphenic_gene mesosoma; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_MESOSOMA_HPP diff --git a/src/game/ant/gene/nest-site.hpp b/src/game/ant/gene/nest-site.hpp deleted file mode 100644 index 4d1761b..0000000 --- a/src/game/ant/gene/nest-site.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_NEST_SITE_HPP -#define ANTKEEPER_GAME_ANT_GENE_NEST_SITE_HPP - -#include "game/ant/phene/nest-site.hpp" -#include "game/ant/gene/monophenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Monophenic nest site gene. -typedef monophenic_gene nest_site; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_NEST_SITE_HPP diff --git a/src/game/ant/gene/ocelli.hpp b/src/game/ant/gene/ocelli.hpp deleted file mode 100644 index 9c07ffd..0000000 --- a/src/game/ant/gene/ocelli.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_OCELLI_HPP -#define ANTKEEPER_GAME_ANT_GENE_OCELLI_HPP - -#include "game/ant/phene/ocelli.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic ocelli gene. -typedef polyphenic_gene ocelli; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_OCELLI_HPP diff --git a/src/game/ant/gene/pigmentation.hpp b/src/game/ant/gene/pigmentation.hpp deleted file mode 100644 index 3fd9f97..0000000 --- a/src/game/ant/gene/pigmentation.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_PIGMENTATION_HPP -#define ANTKEEPER_GAME_ANT_GENE_PIGMENTATION_HPP - -#include "game/ant/phene/pigmentation.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic pigmentation gene. -typedef polyphenic_gene pigmentation; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_PIGMENTATION_HPP diff --git a/src/game/ant/gene/pilosity.hpp b/src/game/ant/gene/pilosity.hpp deleted file mode 100644 index e6c3252..0000000 --- a/src/game/ant/gene/pilosity.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_PILOSITY_HPP -#define ANTKEEPER_GAME_ANT_GENE_PILOSITY_HPP - -#include "game/ant/phene/pilosity.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic pilosity gene. -typedef polyphenic_gene pilosity; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_PILOSITY_HPP diff --git a/src/game/ant/gene/sculpturing.hpp b/src/game/ant/gene/sculpturing.hpp deleted file mode 100644 index 1455fb1..0000000 --- a/src/game/ant/gene/sculpturing.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_SCULPTURING_HPP -#define ANTKEEPER_GAME_ANT_GENE_SCULPTURING_HPP - -#include "game/ant/phene/sculpturing.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic sculpturing gene. -typedef polyphenic_gene sculpturing; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_SCULPTURING_HPP diff --git a/src/game/ant/gene/sting.hpp b/src/game/ant/gene/sting.hpp deleted file mode 100644 index f289337..0000000 --- a/src/game/ant/gene/sting.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_STING_HPP -#define ANTKEEPER_GAME_ANT_GENE_STING_HPP - -#include "game/ant/phene/sting.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic sting gene. -typedef polyphenic_gene sting; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_STING_HPP diff --git a/src/game/ant/gene/waist.hpp b/src/game/ant/gene/waist.hpp deleted file mode 100644 index e63be05..0000000 --- a/src/game/ant/gene/waist.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_WAIST_HPP -#define ANTKEEPER_GAME_ANT_GENE_WAIST_HPP - -#include "game/ant/phene/waist.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic waist gene. -typedef polyphenic_gene waist; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_WAIST_HPP diff --git a/src/game/ant/gene/wings.hpp b/src/game/ant/gene/wings.hpp deleted file mode 100644 index 910bdfb..0000000 --- a/src/game/ant/gene/wings.hpp +++ /dev/null @@ -1,35 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENE_WINGS_HPP -#define ANTKEEPER_GAME_ANT_GENE_WINGS_HPP - -#include "game/ant/phene/wings.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" - -namespace ant { -namespace gene { - -/// Polyphenic wings gene. -typedef polyphenic_gene wings; - -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_WINGS_HPP diff --git a/src/game/ant/gene/loader/antennae-loader.cpp b/src/game/ant/genes/ant-antennae-gene.cpp similarity index 61% rename from src/game/ant/gene/loader/antennae-loader.cpp rename to src/game/ant/genes/ant-antennae-gene.cpp index 6992071..30e9d17 100644 --- a/src/game/ant/gene/loader/antennae-loader.cpp +++ b/src/game/ant/genes/ant-antennae-gene.cpp @@ -17,17 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/antennae.hpp" +#include "game/ant/genes/ant-antennae-gene.hpp" #include +#include #include -using namespace ::ant; - -static void deserialize_antennae_phene(phene::antennae& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_antennae_phene(ant_antennae_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.model = nullptr; phene.total_antennomere_count = 0; @@ -35,36 +33,33 @@ static void deserialize_antennae_phene(phene::antennae& phene, const json& phene // Load antennae model if (auto element = phene_element.find("model"); element != phene_element.end()) - phene.model = resource_manager->load(element->get()); + phene.model = resource_manager.load(element->get()); // Parse total antennomere count if (auto element = phene_element.find("total_antennomere_count"); element != phene_element.end()) - phene.total_antennomere_count = element->get(); + phene.total_antennomere_count = element->get(); // Parse club antennomere count if (auto element = phene_element.find("club_antennomere_count"); element != phene_element.end()) - phene.club_antennomere_count = element->get(); + phene.club_antennomere_count = element->get(); } template <> -gene::antennae* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto antennae_element = data->find("antennae"); - if (antennae_element == data->end()) + auto antennae_element = json_data->find("antennae"); + if (antennae_element == json_data->end()) throw std::runtime_error("Invalid antennae gene."); // Allocate gene - gene::antennae* antennae = new gene::antennae(); + std::unique_ptr antennae = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*antennae, &deserialize_antennae_phene, *antennae_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*antennae, &deserialize_ant_antennae_phene, *antennae_element, resource_manager); return antennae; } diff --git a/src/game/ant/phene/antennae.hpp b/src/game/ant/genes/ant-antennae-gene.hpp similarity index 68% rename from src/game/ant/phene/antennae.hpp rename to src/game/ant/genes/ant-antennae-gene.hpp index 4fce8bc..1d90c3d 100644 --- a/src/game/ant/phene/antennae.hpp +++ b/src/game/ant/genes/ant-antennae-gene.hpp @@ -17,13 +17,13 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_ANTENNAE_HPP -#define ANTKEEPER_GAME_ANT_PHENE_ANTENNAE_HPP +#ifndef ANTKEEPER_GAME_ANT_ANTENNAE_GENE_HPP +#define ANTKEEPER_GAME_ANT_ANTENNAE_GENE_HPP +#include "game/ant/genes/polyphenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include +#include /** * Ant antennae phene. @@ -31,19 +31,19 @@ namespace phene { * @see https://www.antwiki.org/wiki/Character_States_for_Extant_Ant_Genera_of_the_Formicidae * @see https://www.antwiki.org/wiki/Morphological_Measurements */ -struct antennae +struct ant_antennae_phene { /// 3D model of the antennae. - render::model* model; + std::shared_ptr model; - /// Total number of antennal segments. - int total_antennomere_count; + /// Total number of antennal segments per antenna. + std::uint8_t total_antennomere_count; /// Number of antennal segments that constitute a club. - int club_antennomere_count; + std::uint8_t club_antennomere_count; }; -} // namespace phene -} // namespace ant +/// Ant antennae gene. +using ant_antennae_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_ANTENNAE_HPP +#endif // ANTKEEPER_GAME_ANT_ANTENNAE_GENE_HPP diff --git a/src/game/ant/gene/loader/body-size-loader.cpp b/src/game/ant/genes/ant-body-size-gene.cpp similarity index 66% rename from src/game/ant/gene/loader/body-size-loader.cpp rename to src/game/ant/genes/ant-body-size-gene.cpp index 63bd63a..8c85541 100644 --- a/src/game/ant/gene/loader/body-size-loader.cpp +++ b/src/game/ant/genes/ant-body-size-gene.cpp @@ -17,16 +17,14 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/body-size.hpp" +#include +#include "game/ant/genes/ant-body-size-gene.hpp" #include -using namespace ::ant; - -static void deserialize_body_size_phene(phene::body_size& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_body_size_phene(ant_body_size_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.min_mesosoma_length = 1.0f; phene.max_mesosoma_length = 1.0f; @@ -46,24 +44,21 @@ static void deserialize_body_size_phene(phene::body_size& phene, const json& phe } template <> -gene::body_size* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto body_size_element = data->find("body_size"); - if (body_size_element == data->end()) - throw std::runtime_error("Invalid body size gene."); + auto body_size_element = json_data->find("body_size"); + if (body_size_element == json_data->end()) + throw std::runtime_error("Invalid body_size gene."); // Allocate gene - gene::body_size* body_size = new gene::body_size(); + std::unique_ptr body_size = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*body_size, &deserialize_body_size_phene, *body_size_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*body_size, &deserialize_ant_body_size_phene, *body_size_element, resource_manager); return body_size; } diff --git a/src/game/ant/phene/body-size.hpp b/src/game/ant/genes/ant-body-size-gene.hpp similarity index 78% rename from src/game/ant/phene/body-size.hpp rename to src/game/ant/genes/ant-body-size-gene.hpp index 383550e..ba1f0a8 100644 --- a/src/game/ant/phene/body-size.hpp +++ b/src/game/ant/genes/ant-body-size-gene.hpp @@ -17,20 +17,17 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_BODY_SIZE_HPP -#define ANTKEEPER_GAME_ANT_PHENE_BODY_SIZE_HPP +#ifndef ANTKEEPER_GAME_ANT_BODY_SIZE_GENE_HPP +#define ANTKEEPER_GAME_ANT_BODY_SIZE_GENE_HPP -#include - -namespace ant { -namespace phene { +#include "game/ant/genes/polyphenic-ant-gene.hpp" /** * Ant body size phene. * * @see https://www.antwiki.org/wiki/Morphological_Measurements */ -struct body_size +struct ant_body_size_phene { /// Minimum mesosoma length (Weber's length), in centimeters. float min_mesosoma_length; @@ -42,7 +39,7 @@ struct body_size float mean_mesosoma_length; }; -} // namespace phene -} // namespace ant +/// Ant body size gene. +using ant_body_size_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_BODY_SIZE_HPP +#endif // ANTKEEPER_GAME_ANT_BODY_SIZE_GENE_HPP diff --git a/src/game/ant/gene/loader/cocoon-loader.cpp b/src/game/ant/genes/ant-cocoon-gene.cpp similarity index 63% rename from src/game/ant/gene/loader/cocoon-loader.cpp rename to src/game/ant/genes/ant-cocoon-gene.cpp index f784378..78c1bde 100644 --- a/src/game/ant/gene/loader/cocoon-loader.cpp +++ b/src/game/ant/genes/ant-cocoon-gene.cpp @@ -17,17 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/cocoon.hpp" +#include +#include "game/ant/genes/ant-cocoon-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_cocoon_phene(phene::cocoon& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_cocoon_phene(ant_cocoon_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.present = false; phene.model = nullptr; @@ -40,29 +38,26 @@ static void deserialize_cocoon_phene(phene::cocoon& phene, const json& phene_ele if (phene.present) { if (auto element = phene_element.find("model"); element != phene_element.end()) - phene.model = resource_manager->load(element->get()); + phene.model = resource_manager.load(element->get()); } } template <> -gene::cocoon* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto cocoon_element = data->find("cocoon"); - if (cocoon_element == data->end()) + auto cocoon_element = json_data->find("cocoon"); + if (cocoon_element == json_data->end()) throw std::runtime_error("Invalid cocoon gene."); // Allocate gene - gene::cocoon* cocoon = new gene::cocoon(); + std::unique_ptr cocoon = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*cocoon, &deserialize_cocoon_phene, *cocoon_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*cocoon, &deserialize_ant_cocoon_phene, *cocoon_element, resource_manager); return cocoon; } diff --git a/src/game/ant/phene/cocoon.hpp b/src/game/ant/genes/ant-cocoon-gene.hpp similarity index 73% rename from src/game/ant/phene/cocoon.hpp rename to src/game/ant/genes/ant-cocoon-gene.hpp index c740db4..1acbcd4 100644 --- a/src/game/ant/phene/cocoon.hpp +++ b/src/game/ant/genes/ant-cocoon-gene.hpp @@ -17,27 +17,26 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_COCOON_HPP -#define ANTKEEPER_GAME_ANT_PHENE_COCOON_HPP +#ifndef ANTKEEPER_GAME_ANT_COCOON_GENE_HPP +#define ANTKEEPER_GAME_ANT_COCOON_GENE_HPP +#include "game/ant/genes/monophenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include /** * Ant cocoon phene. */ -struct cocoon +struct ant_cocoon_phene { /// 3D model of the cocoon, if present. - render::model* model; + std::shared_ptr model; /// Indicates whether a cocoon is formed by the larvae or not. bool present; }; -} // namespace phene -} // namespace ant +/// Ant cocoon gene. +using ant_cocoon_gene = monophenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_COCOON_HPP +#endif // ANTKEEPER_GAME_ANT_COCOON_GENE_HPP diff --git a/src/game/ant/gene/loader/diet-loader.cpp b/src/game/ant/genes/ant-diet-gene.cpp similarity index 58% rename from src/game/ant/gene/loader/diet-loader.cpp rename to src/game/ant/genes/ant-diet-gene.cpp index 80d37a9..52951c8 100644 --- a/src/game/ant/gene/loader/diet-loader.cpp +++ b/src/game/ant/genes/ant-diet-gene.cpp @@ -17,41 +17,34 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/diet.hpp" -#include -#include +#include +#include "game/ant/genes/ant-diet-gene.hpp" #include -using namespace ::ant; - -static void deserialize_diet_phene(phene::diet& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_diet_phene(ant_diet_phene& phene, const json& phene_element, resource_manager& resource_manager) { } template <> -gene::diet* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto diet_element = data->find("diet"); - if (diet_element == data->end()) + auto diet_element = json_data->find("diet"); + if (diet_element == json_data->end()) throw std::runtime_error("Invalid diet gene."); // Allocate gene - gene::diet* diet = new gene::diet(); + std::unique_ptr diet = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*diet, &deserialize_diet_phene, *diet_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*diet, &deserialize_ant_diet_phene, *diet_element, resource_manager); return diet; } diff --git a/src/game/ant/phene/diet.hpp b/src/game/ant/genes/ant-diet-gene.hpp similarity index 80% rename from src/game/ant/phene/diet.hpp rename to src/game/ant/genes/ant-diet-gene.hpp index 86084a6..72af8e0 100644 --- a/src/game/ant/phene/diet.hpp +++ b/src/game/ant/genes/ant-diet-gene.hpp @@ -17,16 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_DIET_HPP -#define ANTKEEPER_GAME_ANT_PHENE_DIET_HPP +#ifndef ANTKEEPER_GAME_ANT_DIET_GENE_HPP +#define ANTKEEPER_GAME_ANT_DIET_GENE_HPP -namespace ant { -namespace phene { +#include "game/ant/genes/monophenic-ant-gene.hpp" /** * Ant diet phene. */ -struct diet +struct ant_diet_phene { /// Preference for eating seeds. float seeds; @@ -47,7 +46,7 @@ struct diet float carrion; }; -} // namespace phene -} // namespace ant +/// Ant diet gene. +using ant_diet_gene = monophenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_DIET_HPP +#endif // ANTKEEPER_GAME_ANT_DIET_GENE_HPP diff --git a/src/game/ant/gene/loader/egg-loader.cpp b/src/game/ant/genes/ant-egg-gene.cpp similarity index 61% rename from src/game/ant/gene/loader/egg-loader.cpp rename to src/game/ant/genes/ant-egg-gene.cpp index 0d1a8ab..55cd5a1 100644 --- a/src/game/ant/gene/loader/egg-loader.cpp +++ b/src/game/ant/genes/ant-egg-gene.cpp @@ -17,44 +17,39 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/egg.hpp" +#include +#include "game/ant/genes/ant-egg-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_egg_phene(phene::egg& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_egg_phene(ant_egg_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.model = nullptr; // Load egg model if (auto element = phene_element.find("model"); element != phene_element.end()) - phene.model = resource_manager->load(element->get()); + phene.model = resource_manager.load(element->get()); } template <> -gene::egg* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto egg_element = data->find("egg"); - if (egg_element == data->end()) + auto egg_element = json_data->find("egg"); + if (egg_element == json_data->end()) throw std::runtime_error("Invalid egg gene."); // Allocate gene - gene::egg* egg = new gene::egg(); + std::unique_ptr egg = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*egg, &deserialize_egg_phene, *egg_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*egg, &deserialize_ant_egg_phene, *egg_element, resource_manager); return egg; } diff --git a/src/game/ant/phene/egg.hpp b/src/game/ant/genes/ant-egg-gene.hpp similarity index 72% rename from src/game/ant/phene/egg.hpp rename to src/game/ant/genes/ant-egg-gene.hpp index c9536b1..756563d 100644 --- a/src/game/ant/phene/egg.hpp +++ b/src/game/ant/genes/ant-egg-gene.hpp @@ -17,24 +17,23 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_EGG_HPP -#define ANTKEEPER_GAME_ANT_PHENE_EGG_HPP +#ifndef ANTKEEPER_GAME_ANT_EGG_GENE_HPP +#define ANTKEEPER_GAME_ANT_EGG_GENE_HPP +#include "game/ant/genes/monophenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include /** * Ant egg phene. */ -struct egg +struct ant_egg_phene { /// 3D model of the egg. - render::model* model; + std::shared_ptr model; }; -} // namespace phene -} // namespace ant +/// Ant egg gene. +using ant_egg_gene = monophenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_EGG_HPP +#endif // ANTKEEPER_GAME_ANT_EGG_GENE_HPP diff --git a/src/game/ant/gene/loader/eyes-loader.cpp b/src/game/ant/genes/ant-eyes-gene.cpp similarity index 72% rename from src/game/ant/gene/loader/eyes-loader.cpp rename to src/game/ant/genes/ant-eyes-gene.cpp index 3c2fe35..a699b13 100644 --- a/src/game/ant/gene/loader/eyes-loader.cpp +++ b/src/game/ant/genes/ant-eyes-gene.cpp @@ -17,17 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/eyes.hpp" +#include +#include "game/ant/genes/ant-eyes-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_eyes_phene(phene::eyes& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_eyes_phene(ant_eyes_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.present = false; phene.model = nullptr; @@ -44,7 +42,7 @@ static void deserialize_eyes_phene(phene::eyes& phene, const json& phene_element { // Load eyes model if (auto element = phene_element.find("model"); element != phene_element.end()) - phene.model = resource_manager->load(element->get()); + phene.model = resource_manager.load(element->get()); // Parse length if (auto element = phene_element.find("length"); element != phene_element.end()) @@ -65,24 +63,21 @@ static void deserialize_eyes_phene(phene::eyes& phene, const json& phene_element } template <> -gene::eyes* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto eyes_element = data->find("eyes"); - if (eyes_element == data->end()) + auto eyes_element = json_data->find("eyes"); + if (eyes_element == json_data->end()) throw std::runtime_error("Invalid eyes gene."); // Allocate gene - gene::eyes* eyes = new gene::eyes(); + std::unique_ptr eyes = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*eyes, &deserialize_eyes_phene, *eyes_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*eyes, &deserialize_ant_eyes_phene, *eyes_element, resource_manager); return eyes; } diff --git a/src/game/ant/phene/eyes.hpp b/src/game/ant/genes/ant-eyes-gene.hpp similarity index 75% rename from src/game/ant/phene/eyes.hpp rename to src/game/ant/genes/ant-eyes-gene.hpp index a8193b2..07f56a1 100644 --- a/src/game/ant/phene/eyes.hpp +++ b/src/game/ant/genes/ant-eyes-gene.hpp @@ -17,23 +17,23 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_EYES_HPP -#define ANTKEEPER_GAME_ANT_PHENE_EYES_HPP +#ifndef ANTKEEPER_GAME_ANT_EYES_GENE_HPP +#define ANTKEEPER_GAME_ANT_EYES_GENE_HPP +#include "game/ant/genes/polyphenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include +#include /** * Ant eyes phene. * * @see https://www.antwiki.org/wiki/Morphological_Measurements */ -struct eyes +struct ant_eyes_phene { /// 3D model of the eyes, if present. - render::model* model; + std::shared_ptr model; /// Indicates whether eyes are present. bool present; @@ -48,10 +48,10 @@ struct eyes float height; /// Number of ommatidia. - int ommatidia_count; + std::uint32_t ommatidia_count; }; -} // namespace phene -} // namespace ant +/// Ant eyes gene. +using ant_eyes_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_EYES_HPP +#endif // ANTKEEPER_GAME_ANT_EYES_GENE_HPP diff --git a/src/game/ant/gene/loader/foraging-time-loader.cpp b/src/game/ant/genes/ant-foraging-time-gene.cpp similarity index 63% rename from src/game/ant/gene/loader/foraging-time-loader.cpp rename to src/game/ant/genes/ant-foraging-time-gene.cpp index 0c8bb77..a2cb353 100644 --- a/src/game/ant/gene/loader/foraging-time-loader.cpp +++ b/src/game/ant/genes/ant-foraging-time-gene.cpp @@ -17,18 +17,16 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/foraging-time.hpp" +#include +#include "game/ant/genes/ant-foraging-time-gene.hpp" #include #include #include -using namespace ::ant; - -static void deserialize_foraging_time_phene(phene::foraging_time& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_foraging_time_phene(ant_foraging_time_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.min_solar_altitude = -math::half_pi; phene.max_solar_altitude = math::half_pi; @@ -43,24 +41,21 @@ static void deserialize_foraging_time_phene(phene::foraging_time& phene, const j } template <> -gene::foraging_time* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto foraging_time_element = data->find("foraging_time"); - if (foraging_time_element == data->end()) - throw std::runtime_error("Invalid foraging time gene."); + auto foraging_time_element = json_data->find("foraging_time"); + if (foraging_time_element == json_data->end()) + throw std::runtime_error("Invalid foraging_time gene."); // Allocate gene - gene::foraging_time* foraging_time = new gene::foraging_time(); + std::unique_ptr foraging_time = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*foraging_time, &deserialize_foraging_time_phene, *foraging_time_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*foraging_time, &deserialize_ant_foraging_time_phene, *foraging_time_element, resource_manager); return foraging_time; } diff --git a/src/game/ant/phene/foraging-time.hpp b/src/game/ant/genes/ant-foraging-time-gene.hpp similarity index 74% rename from src/game/ant/phene/foraging-time.hpp rename to src/game/ant/genes/ant-foraging-time-gene.hpp index 108c7a5..07c0f5e 100644 --- a/src/game/ant/phene/foraging-time.hpp +++ b/src/game/ant/genes/ant-foraging-time-gene.hpp @@ -17,18 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_FORAGING_TIME_HPP -#define ANTKEEPER_GAME_ANT_PHENE_FORAGING_TIME_HPP +#ifndef ANTKEEPER_GAME_ANT_FORAGING_TIME_GENE_HPP +#define ANTKEEPER_GAME_ANT_FORAGING_TIME_GENE_HPP -#include - -namespace ant { -namespace phene { +#include "game/ant/genes/monophenic-ant-gene.hpp" /** * Ant foraging time phene. */ -struct foraging_time +struct ant_foraging_time_phene { /// Minimum solar altitude at which foraging occurs. float min_solar_altitude; @@ -37,7 +34,7 @@ struct foraging_time float max_solar_altitude; }; -} // namespace phene -} // namespace ant +/// Ant foraging time gene. +using ant_foraging_time_gene = monophenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_FORAGING_TIME_HPP +#endif // ANTKEEPER_GAME_ANT_FORAGING_TIME_GENE_HPP diff --git a/src/game/ant/gene/loader/founding-mode-loader.cpp b/src/game/ant/genes/ant-founding-mode-gene.cpp similarity index 51% rename from src/game/ant/gene/loader/founding-mode-loader.cpp rename to src/game/ant/genes/ant-founding-mode-gene.cpp index 0daa1ea..c0df65e 100644 --- a/src/game/ant/gene/loader/founding-mode-loader.cpp +++ b/src/game/ant/genes/ant-founding-mode-gene.cpp @@ -17,41 +17,34 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/founding-mode.hpp" -#include -#include +#include +#include "game/ant/genes/ant-founding-mode-gene.hpp" #include -using namespace ::ant; - -static void deserialize_founding_mode_phene(phene::founding_mode& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_founding_mode_phene(ant_founding_mode_phene& phene, const json& phene_element, resource_manager& resource_manager) { } template <> -gene::founding_mode* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto founding_mode_element = data->find("founding_mode"); - if (founding_mode_element == data->end()) - throw std::runtime_error("Invalid founding mode gene."); + auto founding_mode_element = json_data->find("founding_mode"); + if (founding_mode_element == json_data->end()) + throw std::runtime_error("Invalid founding_mode gene."); // Allocate gene - gene::founding_mode* founding_mode = new gene::founding_mode(); + std::unique_ptr founding_mode = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*founding_mode, &deserialize_founding_mode_phene, *founding_mode_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*founding_mode, &deserialize_ant_founding_mode_phene, *founding_mode_element, resource_manager); return founding_mode; } diff --git a/src/game/ant/phene/founding-mode.hpp b/src/game/ant/genes/ant-founding-mode-gene.hpp similarity index 74% rename from src/game/ant/phene/founding-mode.hpp rename to src/game/ant/genes/ant-founding-mode-gene.hpp index f3aefcd..b30c9b1 100644 --- a/src/game/ant/phene/founding-mode.hpp +++ b/src/game/ant/genes/ant-founding-mode-gene.hpp @@ -17,18 +17,18 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_FOUNDING_MODE_HPP -#define ANTKEEPER_GAME_ANT_PHENE_FOUNDING_MODE_HPP +#ifndef ANTKEEPER_GAME_ANT_FOUNDING_MODE_GENE_HPP +#define ANTKEEPER_GAME_ANT_FOUNDING_MODE_GENE_HPP -namespace ant { -namespace phene { +#include "game/ant/genes/monophenic-ant-gene.hpp" +#include /** * Colony founding mode phene. * * @see https://www.antwiki.org/wiki/Colony_Foundation */ -enum class founding_mode +enum class ant_founding_mode_phene: std::uint8_t { /// Foraging outside the claustral chamber is required. semi_claustral, @@ -40,7 +40,7 @@ enum class founding_mode fission }; -} // namespace phene -} // namespace ant +/// Monophenic founding mode gene. +using ant_founding_mode_gene = monophenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_FOUNDING_MODE_HPP +#endif // ANTKEEPER_GAME_ANT_FOUNDING_MODE_GENE_HPP diff --git a/src/game/ant/gene/loader/gaster-loader.cpp b/src/game/ant/genes/ant-gaster-gene.cpp similarity index 63% rename from src/game/ant/gene/loader/gaster-loader.cpp rename to src/game/ant/genes/ant-gaster-gene.cpp index 621f0ce..35ece07 100644 --- a/src/game/ant/gene/loader/gaster-loader.cpp +++ b/src/game/ant/genes/ant-gaster-gene.cpp @@ -17,24 +17,22 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/gaster.hpp" +#include +#include "game/ant/genes/ant-gaster-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_gaster_phene(phene::gaster& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_gaster_phene(ant_gaster_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.model = nullptr; phene.phragmosis = 0.0f; // Load gaster model if (auto element = phene_element.find("model"); element != phene_element.end()) - phene.model = resource_manager->load(element->get()); + phene.model = resource_manager.load(element->get()); // Parse phragmosis if (auto element = phene_element.find("phragmosis"); element != phene_element.end()) @@ -42,24 +40,21 @@ static void deserialize_gaster_phene(phene::gaster& phene, const json& phene_ele } template <> -gene::gaster* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto gaster_element = data->find("gaster"); - if (gaster_element == data->end()) + auto gaster_element = json_data->find("gaster"); + if (gaster_element == json_data->end()) throw std::runtime_error("Invalid gaster gene."); // Allocate gene - gene::gaster* gaster = new gene::gaster(); + std::unique_ptr gaster = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*gaster, &deserialize_gaster_phene, *gaster_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*gaster, &deserialize_ant_gaster_phene, *gaster_element, resource_manager); return gaster; } diff --git a/src/game/ant/phene/gaster.hpp b/src/game/ant/genes/ant-gaster-gene.hpp similarity index 73% rename from src/game/ant/phene/gaster.hpp rename to src/game/ant/genes/ant-gaster-gene.hpp index be06958..172f414 100644 --- a/src/game/ant/phene/gaster.hpp +++ b/src/game/ant/genes/ant-gaster-gene.hpp @@ -17,29 +17,28 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_GASTER_HPP -#define ANTKEEPER_GAME_ANT_PHENE_GASTER_HPP +#ifndef ANTKEEPER_GAME_ANT_GASTER_GENE_HPP +#define ANTKEEPER_GAME_ANT_GASTER_GENE_HPP +#include "game/ant/genes/polyphenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include /** * Ant gaster phene. * * @see https://antwiki.org/wiki/Phragmosis */ -struct gaster +struct ant_gaster_phene { /// 3D model of the gaster. - render::model* model; + std::shared_ptr model; /// Degree of phragmosis. float phragmosis; }; -} // namespace phene -} // namespace ant +/// Ant gaster gene. +using ant_gaster_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_GASTER_HPP +#endif // ANTKEEPER_GAME_ANT_GASTER_GENE_HPP diff --git a/src/game/ant/gene/loader/gene-loader.hpp b/src/game/ant/genes/ant-gene-loader.hpp similarity index 55% rename from src/game/ant/gene/loader/gene-loader.hpp rename to src/game/ant/genes/ant-gene-loader.hpp index f20c69c..488d822 100644 --- a/src/game/ant/gene/loader/gene-loader.hpp +++ b/src/game/ant/genes/ant-gene-loader.hpp @@ -17,69 +17,77 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_GENE_GENE_LOADER_HPP -#define ANTKEEPER_GAME_ANT_GENE_GENE_LOADER_HPP +#ifndef ANTKEEPER_GAME_ANT_GENE_LOADER_HPP +#define ANTKEEPER_GAME_ANT_GENE_LOADER_HPP -#include "game/ant/gene/monophenic-gene.hpp" -#include "game/ant/gene/polyphenic-gene.hpp" -#include +#include "game/ant/genes/monophenic-ant-gene.hpp" +#include "game/ant/genes/polyphenic-ant-gene.hpp" +#include #include -namespace ant { -namespace gene { - /** - * Deserializes a gene. + * Deserializes an ant gene. * - * @tparam T Phene type. + * @tparam T Ant phene type. * - * @param gene Gene to deserialize. - * @param deserialize_phene Phene deserialization function. - * @param gene_element JSON element containing a gene definition. - * @param resource_manager Resource manager pointer. + * @param gene Ant gene to deserialize. + * @param deserialize_phene Ant phene deserialization function. + * @param gene_element JSON element containing an ant gene definition. + * @param resource_manager Resource manager. */ /// @{ template -void deserialize_gene(monophenic_gene& gene, void (*deserialize_phene)(T&, const json&, resource_manager*), const json& gene_element, resource_manager* resource_manager) +void deserialize_ant_gene(monophenic_ant_gene& gene, void (*deserialize_phene)(T&, const json&, resource_manager&), const json& gene_element, resource_manager& resource_manager) { // Read gene name if (auto element = gene_element.find("name"); element != gene_element.end()) + { gene.name = element->get(); + } // Deserialize phene if (auto element = gene_element.find("phene"); element != gene_element.end()) + { deserialize_phene(gene.phene, *element, resource_manager); + } } template -void deserialize_gene(polyphenic_gene& gene, void (*deserialize_phene)(T&, const json&, resource_manager*), const json& gene_element, resource_manager* resource_manager) +void deserialize_ant_gene(polyphenic_ant_gene& gene, void (*deserialize_phene)(T&, const json&, resource_manager&), const json& gene_element, resource_manager& resource_manager) { // Read gene name if (auto element = gene_element.find("name"); element != gene_element.end()) + { gene.name = element->get(); + } // Deserialize phenes if (auto phenes_element = gene_element.find("phenes"); phenes_element != gene_element.end()) { if (auto element = phenes_element->find("female"); element != phenes_element->end()) { - deserialize_phene(gene.phenes[caste::queen], *element, resource_manager); - deserialize_phene(gene.phenes[caste::worker], *element, resource_manager); - deserialize_phene(gene.phenes[caste::soldier], *element, resource_manager); + deserialize_phene(gene.phenes[ant_caste::queen], *element, resource_manager); + deserialize_phene(gene.phenes[ant_caste::worker], *element, resource_manager); + deserialize_phene(gene.phenes[ant_caste::soldier], *element, resource_manager); } if (auto element = phenes_element->find("male"); element != phenes_element->end()) - deserialize_phene(gene.phenes[caste::male], *element, resource_manager); + { + deserialize_phene(gene.phenes[ant_caste::male], *element, resource_manager); + } if (auto element = phenes_element->find("queen"); element != phenes_element->end()) - deserialize_phene(gene.phenes[caste::queen], *element, resource_manager); + { + deserialize_phene(gene.phenes[ant_caste::queen], *element, resource_manager); + } if (auto element = phenes_element->find("worker"); element != phenes_element->end()) - deserialize_phene(gene.phenes[caste::worker], *element, resource_manager); + { + deserialize_phene(gene.phenes[ant_caste::worker], *element, resource_manager); + } if (auto element = phenes_element->find("soldier"); element != phenes_element->end()) - deserialize_phene(gene.phenes[caste::soldier], *element, resource_manager); + { + deserialize_phene(gene.phenes[ant_caste::soldier], *element, resource_manager); + } } } /// @} -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_GENE_LOADER_HPP +#endif // ANTKEEPER_GAME_ANT_GENE_LOADER_HPP diff --git a/src/game/ant/gene/loader/head-loader.cpp b/src/game/ant/genes/ant-head-gene.cpp similarity index 68% rename from src/game/ant/gene/loader/head-loader.cpp rename to src/game/ant/genes/ant-head-gene.cpp index 99ffb19..ee24e97 100644 --- a/src/game/ant/gene/loader/head-loader.cpp +++ b/src/game/ant/genes/ant-head-gene.cpp @@ -17,17 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/head.hpp" +#include +#include "game/ant/genes/ant-head-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_head_phene(phene::head& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_head_phene(ant_head_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.model = nullptr; phene.length = 0.0f; @@ -36,7 +34,7 @@ static void deserialize_head_phene(phene::head& phene, const json& phene_element // Load head model if (auto element = phene_element.find("model"); element != phene_element.end()) - phene.model = resource_manager->load(element->get()); + phene.model = resource_manager.load(element->get()); // Parse length if (auto element = phene_element.find("length"); element != phene_element.end()) @@ -52,24 +50,21 @@ static void deserialize_head_phene(phene::head& phene, const json& phene_element } template <> -gene::head* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto head_element = data->find("head"); - if (head_element == data->end()) + auto head_element = json_data->find("head"); + if (head_element == json_data->end()) throw std::runtime_error("Invalid head gene."); // Allocate gene - gene::head* head = new gene::head(); + std::unique_ptr head = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*head, &deserialize_head_phene, *head_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*head, &deserialize_ant_head_phene, *head_element, resource_manager); return head; } diff --git a/src/game/ant/phene/head.hpp b/src/game/ant/genes/ant-head-gene.hpp similarity index 77% rename from src/game/ant/phene/head.hpp rename to src/game/ant/genes/ant-head-gene.hpp index d7ed417..439f842 100644 --- a/src/game/ant/phene/head.hpp +++ b/src/game/ant/genes/ant-head-gene.hpp @@ -17,13 +17,12 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_HEAD_HPP -#define ANTKEEPER_GAME_ANT_PHENE_HEAD_HPP +#ifndef ANTKEEPER_GAME_ANT_HEAD_GENE_HPP +#define ANTKEEPER_GAME_ANT_HEAD_GENE_HPP +#include "game/ant/genes/polyphenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include /** * Ant head phene. @@ -31,10 +30,10 @@ namespace phene { * @see https://www.antwiki.org/wiki/Morphological_Measurements * @see https://antwiki.org/wiki/Phragmosis */ -struct head +struct ant_head_phene { /// 3D model of the head. - render::model* model; + std::shared_ptr model; /// Head length, in mesosomal lengths. float length; @@ -46,7 +45,7 @@ struct head float phragmosis; }; -} // namespace phene -} // namespace ant +/// Polyphenic head gene. +using ant_head_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_HEAD_HPP +#endif // ANTKEEPER_GAME_ANT_HEAD_GENE_HPP diff --git a/src/game/ant/gene/loader/larva-loader.cpp b/src/game/ant/genes/ant-larva-gene.cpp similarity index 63% rename from src/game/ant/gene/loader/larva-loader.cpp rename to src/game/ant/genes/ant-larva-gene.cpp index 0df4792..eece30d 100644 --- a/src/game/ant/gene/loader/larva-loader.cpp +++ b/src/game/ant/genes/ant-larva-gene.cpp @@ -17,24 +17,22 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/larva.hpp" +#include +#include "game/ant/genes/ant-larva-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_larva_phene(phene::larva& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_larva_phene(ant_larva_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.model = nullptr; phene.instar_count = 0; // Load larva model if (auto element = phene_element.find("model"); element != phene_element.end()) - phene.model = resource_manager->load(element->get()); + phene.model = resource_manager.load(element->get()); // Parse instar count if (auto element = phene_element.find("instar_count"); element != phene_element.end()) @@ -42,24 +40,21 @@ static void deserialize_larva_phene(phene::larva& phene, const json& phene_eleme } template <> -gene::larva* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto larva_element = data->find("larva"); - if (larva_element == data->end()) + auto larva_element = json_data->find("larva"); + if (larva_element == json_data->end()) throw std::runtime_error("Invalid larva gene."); // Allocate gene - gene::larva* larva = new gene::larva(); + std::unique_ptr larva = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*larva, &deserialize_larva_phene, *larva_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*larva, &deserialize_ant_larva_phene, *larva_element, resource_manager); return larva; } diff --git a/src/game/ant/phene/larva.hpp b/src/game/ant/genes/ant-larva-gene.hpp similarity index 70% rename from src/game/ant/phene/larva.hpp rename to src/game/ant/genes/ant-larva-gene.hpp index 5f9745e..2e079c2 100644 --- a/src/game/ant/phene/larva.hpp +++ b/src/game/ant/genes/ant-larva-gene.hpp @@ -17,27 +17,27 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_LARVA_HPP -#define ANTKEEPER_GAME_ANT_PHENE_LARVA_HPP +#ifndef ANTKEEPER_GAME_ANT_LARVA_GENE_HPP +#define ANTKEEPER_GAME_ANT_LARVA_GENE_HPP +#include "game/ant/genes/monophenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include +#include /** * Ant larva phene. */ -struct larva +struct ant_larva_phene { /// 3D model of the larva. - render::model* model; + std::shared_ptr model; /// Number of larval instars before pupation. - int instar_count; + std::uint8_t instar_count; }; -} // namespace phene -} // namespace ant +/// Ant larva gene. +using ant_larva_gene = monophenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_LARVA_HPP +#endif // ANTKEEPER_GAME_ANT_LARVA_GENE_HPP diff --git a/src/game/ant/gene/loader/legs-loader.cpp b/src/game/ant/genes/ant-legs-gene.cpp similarity index 66% rename from src/game/ant/gene/loader/legs-loader.cpp rename to src/game/ant/genes/ant-legs-gene.cpp index 5f46985..c1ff0ac 100644 --- a/src/game/ant/gene/loader/legs-loader.cpp +++ b/src/game/ant/genes/ant-legs-gene.cpp @@ -17,17 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/legs.hpp" +#include +#include "game/ant/genes/ant-legs-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_legs_phene(phene::legs& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_legs_phene(ant_legs_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.model = nullptr; phene.speed = 0.0f; @@ -35,7 +33,7 @@ static void deserialize_legs_phene(phene::legs& phene, const json& phene_element // Load legs model if (auto element = phene_element.find("model"); element != phene_element.end()) - phene.model = resource_manager->load(element->get()); + phene.model = resource_manager.load(element->get()); // Parse speed if (auto element = phene_element.find("speed"); element != phene_element.end()) @@ -47,24 +45,21 @@ static void deserialize_legs_phene(phene::legs& phene, const json& phene_element } template <> -gene::legs* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto legs_element = data->find("legs"); - if (legs_element == data->end()) + auto legs_element = json_data->find("legs"); + if (legs_element == json_data->end()) throw std::runtime_error("Invalid legs gene."); // Allocate gene - gene::legs* legs = new gene::legs(); + std::unique_ptr legs = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*legs, &deserialize_legs_phene, *legs_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*legs, &deserialize_ant_legs_phene, *legs_element, resource_manager); return legs; } diff --git a/src/game/ant/phene/legs.hpp b/src/game/ant/genes/ant-legs-gene.hpp similarity index 75% rename from src/game/ant/phene/legs.hpp rename to src/game/ant/genes/ant-legs-gene.hpp index ad40bf3..8a6aea9 100644 --- a/src/game/ant/phene/legs.hpp +++ b/src/game/ant/genes/ant-legs-gene.hpp @@ -17,23 +17,22 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_LEGS_HPP -#define ANTKEEPER_GAME_ANT_PHENE_LEGS_HPP +#ifndef ANTKEEPER_GAME_ANT_LEGS_GENE_HPP +#define ANTKEEPER_GAME_ANT_LEGS_GENE_HPP +#include "game/ant/genes/polyphenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include /** * Ant legs phene. * * @see https://www.antwiki.org/wiki/Morphological_Measurements */ -struct legs +struct ant_legs_phene { /// 3D model of the legs. - render::model* model; + std::shared_ptr model; /// Running speed, in mesosomal lengths per second. float speed; @@ -42,7 +41,7 @@ struct legs float grip; }; -} // namespace phene -} // namespace ant +/// Polyphenic legs gene. +using ant_legs_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_LEGS_HPP +#endif // ANTKEEPER_GAME_ANT_LEGS_GENE_HPP diff --git a/src/game/ant/gene/loader/mandibles-loader.cpp b/src/game/ant/genes/ant-mandibles-gene.cpp similarity index 67% rename from src/game/ant/gene/loader/mandibles-loader.cpp rename to src/game/ant/genes/ant-mandibles-gene.cpp index c666cc3..3403654 100644 --- a/src/game/ant/gene/loader/mandibles-loader.cpp +++ b/src/game/ant/genes/ant-mandibles-gene.cpp @@ -17,17 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/mandibles.hpp" +#include +#include "game/ant/genes/ant-mandibles-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_mandibles_phene(phene::mandibles& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_mandibles_phene(ant_mandibles_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.model = nullptr; phene.length = 0.0f; @@ -36,7 +34,7 @@ static void deserialize_mandibles_phene(phene::mandibles& phene, const json& phe // Load mandibles model if (auto element = phene_element.find("model"); element != phene_element.end()) - phene.model = resource_manager->load(element->get()); + phene.model = resource_manager.load(element->get()); // Parse length if (auto element = phene_element.find("length"); element != phene_element.end()) @@ -52,24 +50,21 @@ static void deserialize_mandibles_phene(phene::mandibles& phene, const json& phe } template <> -gene::mandibles* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto mandibles_element = data->find("mandibles"); - if (mandibles_element == data->end()) + auto mandibles_element = json_data->find("mandibles"); + if (mandibles_element == json_data->end()) throw std::runtime_error("Invalid mandibles gene."); // Allocate gene - gene::mandibles* mandibles = new gene::mandibles(); + std::unique_ptr mandibles = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*mandibles, &deserialize_mandibles_phene, *mandibles_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*mandibles, &deserialize_ant_mandibles_phene, *mandibles_element, resource_manager); return mandibles; } diff --git a/src/game/ant/phene/mandibles.hpp b/src/game/ant/genes/ant-mandibles-gene.hpp similarity index 79% rename from src/game/ant/phene/mandibles.hpp rename to src/game/ant/genes/ant-mandibles-gene.hpp index 88100ea..4c718cc 100644 --- a/src/game/ant/phene/mandibles.hpp +++ b/src/game/ant/genes/ant-mandibles-gene.hpp @@ -17,13 +17,12 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_MANDIBLES_HPP -#define ANTKEEPER_GAME_ANT_PHENE_MANDIBLES_HPP +#ifndef ANTKEEPER_GAME_ANT_MANDIBLES_GENE_HPP +#define ANTKEEPER_GAME_ANT_MANDIBLES_GENE_HPP +#include "game/ant/genes/polyphenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include /** * Ant mandibles phene. @@ -32,10 +31,10 @@ namespace phene { * @see https://www.antwiki.org/wiki/Morphological_and_Functional_Diversity_of_Ant_Mandibles * @see https://www.antwiki.org/wiki/Morphological_Measurements */ -struct mandibles +struct ant_mandibles_phene { /// 3D model of the mandibles. - render::model* model; + std::shared_ptr model; /// Mandible length at closure, in mesosomal lengths. float length; @@ -47,7 +46,7 @@ struct mandibles int basal_dental_count; }; -} // namespace phene -} // namespace ant +/// Ant mandibles gene. +using ant_mandibles_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_MANDIBLES_HPP +#endif // ANTKEEPER_GAME_ANT_MANDIBLES_GENE_HPP diff --git a/src/game/ant/gene/loader/mesosoma-loader.cpp b/src/game/ant/genes/ant-mesosoma-gene.cpp similarity index 71% rename from src/game/ant/gene/loader/mesosoma-loader.cpp rename to src/game/ant/genes/ant-mesosoma-gene.cpp index 7689186..73ab8b2 100644 --- a/src/game/ant/gene/loader/mesosoma-loader.cpp +++ b/src/game/ant/genes/ant-mesosoma-gene.cpp @@ -17,17 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/mesosoma.hpp" +#include +#include "game/ant/genes/ant-mesosoma-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_mesosoma_phene(phene::mesosoma& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_mesosoma_phene(ant_mesosoma_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.model = nullptr; phene.pronotum_width = 0.0f; @@ -37,7 +35,7 @@ static void deserialize_mesosoma_phene(phene::mesosoma& phene, const json& phene // Load mesosoma model if (auto element = phene_element.find("model"); element != phene_element.end()) - phene.model = resource_manager->load(element->get()); + phene.model = resource_manager.load(element->get()); // Parse pronotum width if (auto element = phene_element.find("pronotum_width"); element != phene_element.end()) @@ -57,24 +55,21 @@ static void deserialize_mesosoma_phene(phene::mesosoma& phene, const json& phene } template <> -gene::mesosoma* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto mesosoma_element = data->find("mesosoma"); - if (mesosoma_element == data->end()) + auto mesosoma_element = json_data->find("mesosoma"); + if (mesosoma_element == json_data->end()) throw std::runtime_error("Invalid mesosoma gene."); // Allocate gene - gene::mesosoma* mesosoma = new gene::mesosoma(); + std::unique_ptr mesosoma = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*mesosoma, &deserialize_mesosoma_phene, *mesosoma_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*mesosoma, &deserialize_ant_mesosoma_phene, *mesosoma_element, resource_manager); return mesosoma; } diff --git a/src/game/ant/phene/mesosoma.hpp b/src/game/ant/genes/ant-mesosoma-gene.hpp similarity index 77% rename from src/game/ant/phene/mesosoma.hpp rename to src/game/ant/genes/ant-mesosoma-gene.hpp index dcebb4f..9d81732 100644 --- a/src/game/ant/phene/mesosoma.hpp +++ b/src/game/ant/genes/ant-mesosoma-gene.hpp @@ -17,23 +17,22 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_MESOSOMA_HPP -#define ANTKEEPER_GAME_ANT_PHENE_MESOSOMA_HPP +#ifndef ANTKEEPER_GAME_ANT_MESOSOMA_GENE_HPP +#define ANTKEEPER_GAME_ANT_MESOSOMA_GENE_HPP +#include "game/ant/genes/polyphenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include /** * Ant mesosoma phene. * * @see https://www.antwiki.org/wiki/Morphological_Measurements */ -struct mesosoma +struct ant_mesosoma_phene { /// 3D model of the mesosoma. - render::model* model; + std::shared_ptr model; /// Pronotum width, in mesosomal lengths. float pronotum_width; @@ -48,7 +47,7 @@ struct mesosoma float propodeum_spinescence; }; -} // namespace phene -} // namespace ant +/// Ant mesosoma gene. +using ant_mesosoma_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_MESOSOMA_HPP +#endif // ANTKEEPER_GAME_ANT_MESOSOMA_GENE_HPP diff --git a/src/game/ant/gene/loader/nest-site-loader.cpp b/src/game/ant/genes/ant-nest-site-gene.cpp similarity index 53% rename from src/game/ant/gene/loader/nest-site-loader.cpp rename to src/game/ant/genes/ant-nest-site-gene.cpp index 86adce2..243421f 100644 --- a/src/game/ant/gene/loader/nest-site-loader.cpp +++ b/src/game/ant/genes/ant-nest-site-gene.cpp @@ -17,41 +17,34 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/nest-site.hpp" -#include -#include +#include +#include "game/ant/genes/ant-nest-site-gene.hpp" #include -using namespace ::ant; - -static void deserialize_nest_site_phene(phene::nest_site& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_nest_site_phene(ant_nest_site_phene& phene, const json& phene_element, resource_manager& resource_manager) { } template <> -gene::nest_site* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto nest_site_element = data->find("nest_site"); - if (nest_site_element == data->end()) - throw std::runtime_error("Invalid nest site gene."); + auto nest_site_element = json_data->find("nest_site"); + if (nest_site_element == json_data->end()) + throw std::runtime_error("Invalid nest_site gene."); // Allocate gene - gene::nest_site* nest_site = new gene::nest_site(); + std::unique_ptr nest_site = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*nest_site, &deserialize_nest_site_phene, *nest_site_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*nest_site, &deserialize_ant_nest_site_phene, *nest_site_element, resource_manager); return nest_site; } diff --git a/src/game/ant/phene/nest-site.hpp b/src/game/ant/genes/ant-nest-site-gene.hpp similarity index 68% rename from src/game/ant/phene/nest-site.hpp rename to src/game/ant/genes/ant-nest-site-gene.hpp index 33759f9..e2b34dc 100644 --- a/src/game/ant/phene/nest-site.hpp +++ b/src/game/ant/genes/ant-nest-site-gene.hpp @@ -17,22 +17,22 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_NEST_SITE_HPP -#define ANTKEEPER_GAME_ANT_NEST_SITE_HPP +#ifndef ANTKEEPER_GAME_ANT_NEST_SITE_GENE_HPP +#define ANTKEEPER_GAME_ANT_NEST_SITE_GENE_HPP -namespace ant { -namespace phene { +#include "game/ant/genes/monophenic-ant-gene.hpp" +#include /** - * Colony nest site phene. + * Ant nest site phene. */ -enum class nest_site +enum class ant_nest_site_phene: std::uint8_t { hypogeic, arboreal }; -} // namespace phene -} // namespace ant +/// Ant nest site gene. +using ant_nest_site_gene = monophenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_NEST_SITE_HPP +#endif // ANTKEEPER_GAME_ANT_NEST_SITE_GENE_HPP diff --git a/src/game/ant/gene/loader/ocelli-loader.cpp b/src/game/ant/genes/ant-ocelli-gene.cpp similarity index 71% rename from src/game/ant/gene/loader/ocelli-loader.cpp rename to src/game/ant/genes/ant-ocelli-gene.cpp index 1a90780..7eb0328 100644 --- a/src/game/ant/gene/loader/ocelli-loader.cpp +++ b/src/game/ant/genes/ant-ocelli-gene.cpp @@ -17,17 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/ocelli.hpp" +#include +#include "game/ant/genes/ant-ocelli-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_ocelli_phene(phene::ocelli& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_ocelli_phene(ant_ocelli_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.lateral_ocelli_present = false; phene.median_ocellus_present = false; @@ -56,36 +54,33 @@ static void deserialize_ocelli_phene(phene::ocelli& phene, const json& phene_ele if (phene.lateral_ocelli_present) { if (auto element = phene_element.find("lateral_ocelli_model"); element != phene_element.end()) - phene.lateral_ocelli_model = resource_manager->load(element->get()); + phene.lateral_ocelli_model = resource_manager.load(element->get()); } // Load median ocellus model, if present if (phene.median_ocellus_present) { if (auto element = phene_element.find("median_ocellus_model"); element != phene_element.end()) - phene.median_ocellus_model = resource_manager->load(element->get()); + phene.median_ocellus_model = resource_manager.load(element->get()); } } template <> -gene::ocelli* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto ocelli_element = data->find("ocelli"); - if (ocelli_element == data->end()) + auto ocelli_element = json_data->find("ocelli"); + if (ocelli_element == json_data->end()) throw std::runtime_error("Invalid ocelli gene."); // Allocate gene - gene::ocelli* ocelli = new gene::ocelli(); + std::unique_ptr ocelli = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*ocelli, &deserialize_ocelli_phene, *ocelli_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*ocelli, &deserialize_ant_ocelli_phene, *ocelli_element, resource_manager); return ocelli; } diff --git a/src/game/ant/phene/ocelli.hpp b/src/game/ant/genes/ant-ocelli-gene.hpp similarity index 73% rename from src/game/ant/phene/ocelli.hpp rename to src/game/ant/genes/ant-ocelli-gene.hpp index 157f208..2a5e01a 100644 --- a/src/game/ant/phene/ocelli.hpp +++ b/src/game/ant/genes/ant-ocelli-gene.hpp @@ -17,24 +17,23 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_OCELLI_HPP -#define ANTKEEPER_GAME_ANT_PHENE_OCELLI_HPP +#ifndef ANTKEEPER_GAME_ANT_PHENE_OCELLI_GENE_HPP +#define ANTKEEPER_GAME_ANT_PHENE_OCELLI_GENE_HPP +#include "game/ant/genes/polyphenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include /** * Ant ocelli phene. */ -struct ocelli +struct ant_ocelli_phene { /// 3D model of the lateral ocelli, if present. - render::model* lateral_ocelli_model; + std::shared_ptr lateral_ocelli_model; /// 3D model of the median ocellus, if present. - render::model* median_ocellus_model; + std::shared_ptr median_ocellus_model; /// Lateral ocelli present. bool lateral_ocelli_present; @@ -49,7 +48,7 @@ struct ocelli float height; }; -} // namespace phene -} // namespace ant +/// Ant ocelli gene. +using ant_ocelli_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_OCELLI_HPP +#endif // ANTKEEPER_GAME_ANT_PHENE_OCELLI_GENE_HPP diff --git a/src/game/ant/gene/loader/pigmentation-loader.cpp b/src/game/ant/genes/ant-pigmentation-gene.cpp similarity index 58% rename from src/game/ant/gene/loader/pigmentation-loader.cpp rename to src/game/ant/genes/ant-pigmentation-gene.cpp index 682a956..d891fad 100644 --- a/src/game/ant/gene/loader/pigmentation-loader.cpp +++ b/src/game/ant/genes/ant-pigmentation-gene.cpp @@ -17,44 +17,39 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/pigmentation.hpp" +#include +#include "game/ant/genes/ant-pigmentation-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_pigmentation_phene(phene::pigmentation& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_pigmentation_phene(ant_pigmentation_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.material = nullptr; // Load pigmentation material if (auto element = phene_element.find("material"); element != phene_element.end()) - phene.material = resource_manager->load(element->get()); + phene.material = resource_manager.load(element->get()); } template <> -gene::pigmentation* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto pigmentation_element = data->find("pigmentation"); - if (pigmentation_element == data->end()) + auto pigmentation_element = json_data->find("pigmentation"); + if (pigmentation_element == json_data->end()) throw std::runtime_error("Invalid pigmentation gene."); // Allocate gene - gene::pigmentation* pigmentation = new gene::pigmentation(); + std::unique_ptr pigmentation = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*pigmentation, &deserialize_pigmentation_phene, *pigmentation_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*pigmentation, &deserialize_ant_pigmentation_phene, *pigmentation_element, resource_manager); return pigmentation; } diff --git a/src/game/ant/phene/pigmentation.hpp b/src/game/ant/genes/ant-pigmentation-gene.hpp similarity index 69% rename from src/game/ant/phene/pigmentation.hpp rename to src/game/ant/genes/ant-pigmentation-gene.hpp index 1efee8c..fc1c17a 100644 --- a/src/game/ant/phene/pigmentation.hpp +++ b/src/game/ant/genes/ant-pigmentation-gene.hpp @@ -17,24 +17,23 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_PIGMENTATION_HPP -#define ANTKEEPER_GAME_ANT_PHENE_PIGMENTATION_HPP +#ifndef ANTKEEPER_GAME_ANT_PIGMENTATION_GENE_HPP +#define ANTKEEPER_GAME_ANT_PIGMENTATION_GENE_HPP +#include "game/ant/genes/polyphenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include /** * Ant pigmentation phene. */ -struct pigmentation +struct ant_pigmentation_phene { /// Pigmentation material - render::material* material; + std::shared_ptr material; }; -} // namespace phene -} // namespace ant +/// Ant pigmentation gene. +using ant_pigmentation_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_PIGMENTATION_HPP +#endif // ANTKEEPER_GAME_ANT_PIGMENTATION_GENE_HPP diff --git a/src/game/ant/gene/loader/pilosity-loader.cpp b/src/game/ant/genes/ant-pilosity-gene.cpp similarity index 61% rename from src/game/ant/gene/loader/pilosity-loader.cpp rename to src/game/ant/genes/ant-pilosity-gene.cpp index ef8dc47..b65946c 100644 --- a/src/game/ant/gene/loader/pilosity-loader.cpp +++ b/src/game/ant/genes/ant-pilosity-gene.cpp @@ -17,16 +17,14 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/pilosity.hpp" +#include +#include "game/ant/genes/ant-pilosity-gene.hpp" #include -using namespace ::ant; - -static void deserialize_pilosity_phene(phene::pilosity& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_pilosity_phene(ant_pilosity_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.density = 0.0f; @@ -36,24 +34,21 @@ static void deserialize_pilosity_phene(phene::pilosity& phene, const json& phene } template <> -gene::pilosity* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto pilosity_element = data->find("pilosity"); - if (pilosity_element == data->end()) + auto pilosity_element = json_data->find("pilosity"); + if (pilosity_element == json_data->end()) throw std::runtime_error("Invalid pilosity gene."); // Allocate gene - gene::pilosity* pilosity = new gene::pilosity(); + std::unique_ptr pilosity = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*pilosity, &deserialize_pilosity_phene, *pilosity_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*pilosity, &deserialize_ant_pilosity_phene, *pilosity_element, resource_manager); return pilosity; } diff --git a/src/game/ant/phene/pilosity.hpp b/src/game/ant/genes/ant-pilosity-gene.hpp similarity index 73% rename from src/game/ant/phene/pilosity.hpp rename to src/game/ant/genes/ant-pilosity-gene.hpp index 0ee6b5b..d7618c8 100644 --- a/src/game/ant/phene/pilosity.hpp +++ b/src/game/ant/genes/ant-pilosity-gene.hpp @@ -17,22 +17,21 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_PILOSITY_HPP -#define ANTKEEPER_GAME_ANT_PHENE_PILOSITY_HPP +#ifndef ANTKEEPER_GAME_ANT_PILOSITY_GENE_HPP +#define ANTKEEPER_GAME_ANT_PILOSITY_GENE_HPP -namespace ant { -namespace phene { +#include "game/ant/genes/polyphenic-ant-gene.hpp" /** * Ant pilosity phene. */ -struct pilosity +struct ant_pilosity_phene { /// Hair density. float density; }; -} // namespace phene -} // namespace ant +/// Ant pilosity gene. +using ant_pilosity_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_PILOSITY_HPP +#endif // ANTKEEPER_GAME_ANT_PILOSITY_GENE_HPP diff --git a/src/game/ant/gene/loader/sculpturing-loader.cpp b/src/game/ant/genes/ant-sculpturing-gene.cpp similarity index 61% rename from src/game/ant/gene/loader/sculpturing-loader.cpp rename to src/game/ant/genes/ant-sculpturing-gene.cpp index 29c07b8..097a4c6 100644 --- a/src/game/ant/gene/loader/sculpturing-loader.cpp +++ b/src/game/ant/genes/ant-sculpturing-gene.cpp @@ -17,24 +17,22 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/sculpturing.hpp" +#include +#include "game/ant/genes/ant-sculpturing-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_sculpturing_phene(phene::sculpturing& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_sculpturing_phene(ant_sculpturing_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.normal_map = nullptr; phene.roughness = 0.0f; // Load normal map if (auto element = phene_element.find("normal_map"); element != phene_element.end()) - phene.normal_map = resource_manager->load(element->get()); + phene.normal_map = resource_manager.load(element->get()); // Parse roughness if (auto element = phene_element.find("roughness"); element != phene_element.end()) @@ -42,24 +40,21 @@ static void deserialize_sculpturing_phene(phene::sculpturing& phene, const json& } template <> -gene::sculpturing* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto sculpturing_element = data->find("sculpturing"); - if (sculpturing_element == data->end()) + auto sculpturing_element = json_data->find("sculpturing"); + if (sculpturing_element == json_data->end()) throw std::runtime_error("Invalid sculpturing gene."); // Allocate gene - gene::sculpturing* sculpturing = new gene::sculpturing(); + std::unique_ptr sculpturing = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*sculpturing, &deserialize_sculpturing_phene, *sculpturing_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*sculpturing, &deserialize_ant_sculpturing_phene, *sculpturing_element, resource_manager); return sculpturing; } diff --git a/src/game/ant/phene/sculpturing.hpp b/src/game/ant/genes/ant-sculpturing-gene.hpp similarity index 70% rename from src/game/ant/phene/sculpturing.hpp rename to src/game/ant/genes/ant-sculpturing-gene.hpp index efc89d9..25132b3 100644 --- a/src/game/ant/phene/sculpturing.hpp +++ b/src/game/ant/genes/ant-sculpturing-gene.hpp @@ -17,27 +17,26 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_SCULPTURING_HPP -#define ANTKEEPER_GAME_ANT_PHENE_SCULPTURING_HPP +#ifndef ANTKEEPER_GAME_ANT_SCULPTURING_GENE_HPP +#define ANTKEEPER_GAME_ANT_SCULPTURING_GENE_HPP +#include "game/ant/genes/polyphenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include /** * Ant surface sculpturing phene. */ -struct sculpturing +struct ant_sculpturing_phene { /// Surface roughness. float roughness; /// Surface culpturing normal map. - gl::texture_2d* normal_map; + std::shared_ptr normal_map; }; -} // namespace phene -} // namespace ant +/// Polyphenic sculpturing gene. +using ant_sculpturing_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_SCULPTURING_HPP +#endif // ANTKEEPER_GAME_ANT_SCULPTURING_GENE_HPP diff --git a/src/game/ant/gene/loader/sting-loader.cpp b/src/game/ant/genes/ant-sting-gene.cpp similarity index 63% rename from src/game/ant/gene/loader/sting-loader.cpp rename to src/game/ant/genes/ant-sting-gene.cpp index d2838ec..cbbcf75 100644 --- a/src/game/ant/gene/loader/sting-loader.cpp +++ b/src/game/ant/genes/ant-sting-gene.cpp @@ -17,17 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/sting.hpp" +#include +#include "game/ant/genes/ant-sting-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_sting_phene(phene::sting& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_sting_phene(ant_sting_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.present = false; phene.model = nullptr; @@ -40,29 +38,26 @@ static void deserialize_sting_phene(phene::sting& phene, const json& phene_eleme { // Load sting model if (auto element = phene_element.find("model"); element != phene_element.end()) - phene.model = resource_manager->load(element->get()); + phene.model = resource_manager.load(element->get()); } } template <> -gene::sting* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto sting_element = data->find("sting"); - if (sting_element == data->end()) + auto sting_element = json_data->find("sting"); + if (sting_element == json_data->end()) throw std::runtime_error("Invalid sting gene."); // Allocate gene - gene::sting* sting = new gene::sting(); + std::unique_ptr sting = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*sting, &deserialize_sting_phene, *sting_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*sting, &deserialize_ant_sting_phene, *sting_element, resource_manager); return sting; } diff --git a/src/game/ant/phene/sting.hpp b/src/game/ant/genes/ant-sting-gene.hpp similarity index 73% rename from src/game/ant/phene/sting.hpp rename to src/game/ant/genes/ant-sting-gene.hpp index 0f95cc0..f533e9f 100644 --- a/src/game/ant/phene/sting.hpp +++ b/src/game/ant/genes/ant-sting-gene.hpp @@ -17,27 +17,26 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_STING_HPP -#define ANTKEEPER_GAME_ANT_PHENE_STING_HPP +#ifndef ANTKEEPER_GAME_ANT_STING_GENE_HPP +#define ANTKEEPER_GAME_ANT_STING_GENE_HPP +#include "game/ant/genes/polyphenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include /** * Trait that describes the sting of an ant. */ -struct sting +struct ant_sting_phene { /// Indicates whether a sting present or not. bool present; /// 3D model of the sting. - render::model* model; + std::shared_ptr model; }; -} // namespace phene -} // namespace ant +/// Polyphenic sting gene. +using ant_sting_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_STING_HPP +#endif // ANTKEEPER_GAME_ANT_STING_GENE_HPP diff --git a/src/game/ant/gene/loader/waist-loader.cpp b/src/game/ant/genes/ant-waist-gene.cpp similarity index 80% rename from src/game/ant/gene/loader/waist-loader.cpp rename to src/game/ant/genes/ant-waist-gene.cpp index 781c56f..959f8de 100644 --- a/src/game/ant/gene/loader/waist-loader.cpp +++ b/src/game/ant/genes/ant-waist-gene.cpp @@ -17,17 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/waist.hpp" +#include +#include "game/ant/genes/ant-waist-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_waist_phene(phene::waist& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_waist_phene(ant_waist_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.model = nullptr; phene.petiole_present = false; @@ -43,7 +41,7 @@ static void deserialize_waist_phene(phene::waist& phene, const json& phene_eleme // Load waist model if (auto element = phene_element.find("model"); element != phene_element.end()) - phene.model = resource_manager->load(element->get()); + phene.model = resource_manager.load(element->get()); // Parse petiole present if (auto element = phene_element.find("petiole_present"); element != phene_element.end()) @@ -93,24 +91,21 @@ static void deserialize_waist_phene(phene::waist& phene, const json& phene_eleme } template <> -gene::waist* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto waist_element = data->find("waist"); - if (waist_element == data->end()) + auto waist_element = json_data->find("waist"); + if (waist_element == json_data->end()) throw std::runtime_error("Invalid waist gene."); // Allocate gene - gene::waist* waist = new gene::waist(); + std::unique_ptr waist = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*waist, &deserialize_waist_phene, *waist_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*waist, &deserialize_ant_waist_phene, *waist_element, resource_manager); return waist; } diff --git a/src/game/ant/phene/waist.hpp b/src/game/ant/genes/ant-waist-gene.hpp similarity index 82% rename from src/game/ant/phene/waist.hpp rename to src/game/ant/genes/ant-waist-gene.hpp index 117db76..648cb71 100644 --- a/src/game/ant/phene/waist.hpp +++ b/src/game/ant/genes/ant-waist-gene.hpp @@ -17,23 +17,22 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_WAIST_HPP -#define ANTKEEPER_GAME_ANT_PHENE_WAIST_HPP +#ifndef ANTKEEPER_GAME_ANT_WAIST_GENE_HPP +#define ANTKEEPER_GAME_ANT_WAIST_GENE_HPP +#include "game/ant/genes/polyphenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include /** * Trait that describes the waist (petiole plus postpetiole) of an ant. * * @see https://www.antwiki.org/wiki/Morphological_Measurements */ -struct waist +struct ant_waist_phene { /// 3D model of the waist. - render::model* model; + std::shared_ptr model; //// Petiole presence. bool petiole_present; @@ -66,7 +65,7 @@ struct waist float postpetiole_spinescence; }; -} // namespace phene -} // namespace ant +/// Polyphenic waist gene. +using ant_waist_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_WAIST_HPP +#endif // ANTKEEPER_GAME_ANT_WAIST_GENE_HPP diff --git a/src/game/ant/gene/loader/wings-loader.cpp b/src/game/ant/genes/ant-wings-gene.cpp similarity index 75% rename from src/game/ant/gene/loader/wings-loader.cpp rename to src/game/ant/genes/ant-wings-gene.cpp index 212b94b..195c4e6 100644 --- a/src/game/ant/gene/loader/wings-loader.cpp +++ b/src/game/ant/genes/ant-wings-gene.cpp @@ -17,17 +17,15 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/gene/loader/gene-loader.hpp" +#include "game/ant/genes/ant-gene-loader.hpp" #include #include -#include -#include "game/ant/gene/wings.hpp" +#include +#include "game/ant/genes/ant-wings-gene.hpp" #include #include -using namespace ::ant; - -static void deserialize_wings_phene(phene::wings& phene, const json& phene_element, resource_manager* resource_manager) +static void deserialize_ant_wings_phene(ant_wings_phene& phene, const json& phene_element, resource_manager& resource_manager) { phene.present = false; phene.forewings_model = nullptr; @@ -47,11 +45,11 @@ static void deserialize_wings_phene(phene::wings& phene, const json& phene_eleme { // Load forewings model if (auto element = phene_element.find("forewings_model"); element != phene_element.end()) - phene.forewings_model = resource_manager->load(element->get()); + phene.forewings_model = resource_manager.load(element->get()); // Load hindwings model if (auto element = phene_element.find("hindwings_model"); element != phene_element.end()) - phene.hindwings_model = resource_manager->load(element->get()); + phene.hindwings_model = resource_manager.load(element->get()); // Parse forewing length if (auto element = phene_element.find("forewing_length"); element != phene_element.end()) @@ -80,24 +78,21 @@ static void deserialize_wings_phene(phene::wings& phene, const json& phene_eleme } template <> -gene::wings* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate gene file - auto wings_element = data->find("wings"); - if (wings_element == data->end()) + auto wings_element = json_data->find("wings"); + if (wings_element == json_data->end()) throw std::runtime_error("Invalid wings gene."); // Allocate gene - gene::wings* wings = new gene::wings(); + std::unique_ptr wings = std::make_unique(); // Deserialize gene - gene::deserialize_gene(*wings, &deserialize_wings_phene, *wings_element, resource_manager); - - // Free JSON data - delete data; + deserialize_ant_gene(*wings, &deserialize_ant_wings_phene, *wings_element, resource_manager); return wings; } diff --git a/src/game/ant/phene/wings.hpp b/src/game/ant/genes/ant-wings-gene.hpp similarity index 79% rename from src/game/ant/phene/wings.hpp rename to src/game/ant/genes/ant-wings-gene.hpp index a497979..d4c3688 100644 --- a/src/game/ant/phene/wings.hpp +++ b/src/game/ant/genes/ant-wings-gene.hpp @@ -17,24 +17,23 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_PHENE_WINGS_HPP -#define ANTKEEPER_GAME_ANT_PHENE_WINGS_HPP +#ifndef ANTKEEPER_GAME_ANT_WINGS_GENE_HPP +#define ANTKEEPER_GAME_ANT_WINGS_GENE_HPP +#include "game/ant/genes/polyphenic-ant-gene.hpp" #include - -namespace ant { -namespace phene { +#include /** * Ant wings phene. */ -struct wings +struct ant_wings_phene { /// 3D model of the forewings. - render::model* forewings_model; + std::shared_ptr forewings_model; /// 3D model of the hindwings. - render::model* hindwings_model; + std::shared_ptr hindwings_model; /// Wings presence. bool present; @@ -58,7 +57,7 @@ struct wings float hindwing_venation; }; -} // namespace phene -} // namespace ant +/// Polyphenic wings gene. +using ant_wings_gene = polyphenic_ant_gene; -#endif // ANTKEEPER_GAME_ANT_PHENE_WINGS_HPP +#endif // ANTKEEPER_GAME_ANT_WINGS_GENE_HPP diff --git a/src/game/ant/gene/monophenic-gene.hpp b/src/game/ant/genes/monophenic-ant-gene.hpp similarity index 74% rename from src/game/ant/gene/monophenic-gene.hpp rename to src/game/ant/genes/monophenic-ant-gene.hpp index 22d1556..ebcaa55 100644 --- a/src/game/ant/gene/monophenic-gene.hpp +++ b/src/game/ant/genes/monophenic-ant-gene.hpp @@ -17,21 +17,18 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_GENE_MONOPHENIC_GENE_HPP -#define ANTKEEPER_GAME_ANT_GENE_MONOPHENIC_GENE_HPP +#ifndef ANTKEEPER_GAME_MONOPHENIC_ANT_GENE_HPP +#define ANTKEEPER_GAME_MONOPHENIC_ANT_GENE_HPP #include -namespace ant { -namespace gene { - /** - * Gene with a single phene. + * Ant gene with a single phene. * - * @tparam T Phene type. + * @tparam T Ant phene type. */ template -struct monophenic_gene +struct monophenic_ant_gene { /// Gene name. std::string name; @@ -40,7 +37,4 @@ struct monophenic_gene T phene; }; -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_MONOPHENIC_GENE_HPP +#endif // ANTKEEPER_GAME_MONOPHENIC_ANT_GENE_HPP diff --git a/src/game/ant/gene/polyphenic-gene.hpp b/src/game/ant/genes/polyphenic-ant-gene.hpp similarity index 72% rename from src/game/ant/gene/polyphenic-gene.hpp rename to src/game/ant/genes/polyphenic-ant-gene.hpp index a5e12d2..64fa6a5 100644 --- a/src/game/ant/gene/polyphenic-gene.hpp +++ b/src/game/ant/genes/polyphenic-ant-gene.hpp @@ -17,34 +17,28 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_ANT_GENE_POLYPHENIC_GENE_HPP -#define ANTKEEPER_GAME_ANT_GENE_POLYPHENIC_GENE_HPP +#ifndef ANTKEEPER_GAME_POLYPHENIC_ANT_GENE_HPP +#define ANTKEEPER_GAME_POLYPHENIC_ANT_GENE_HPP -#include "game/ant/caste.hpp" +#include "game/ant/ant-caste.hpp" #include #include -namespace ant { -namespace gene { - /** - * Gene with caste-specific phenes. + * Ant gene with caste-specific phenes. * - * @tparam T Phene type. + * @tparam T Ant phene type. * * @see https://en.wikipedia.org/wiki/Polyphenism */ template -struct polyphenic_gene +struct polyphenic_ant_gene { /// Gene name. std::string name; /// Caste-specific phene definitions. - std::unordered_map phenes; + std::unordered_map phenes; }; -} // namespace gene -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENE_POLYPHENIC_GENE_HPP +#endif // ANTKEEPER_GAME_POLYPHENIC_ANT_GENE_HPP diff --git a/src/game/ant/genome.cpp b/src/game/ant/genome.cpp deleted file mode 100644 index 962a3bb..0000000 --- a/src/game/ant/genome.cpp +++ /dev/null @@ -1,49 +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 . - */ - -#include "game/ant/genome.hpp" - -namespace ant { - -genome::genome(): - antennae(nullptr), - body_size(nullptr), - cocoon(nullptr), - diet(nullptr), - egg(nullptr), - eyes(nullptr), - foraging_time(nullptr), - founding_mode(nullptr), - gaster(nullptr), - head(nullptr), - larva(nullptr), - legs(nullptr), - mandibles(nullptr), - mesosoma(nullptr), - nest_site(nullptr), - ocelli(nullptr), - pigmentation(nullptr), - pilosity(nullptr), - sculpturing(nullptr), - sting(nullptr), - waist(nullptr), - wings(nullptr) -{} - -} // namespace ant diff --git a/src/game/ant/genome.hpp b/src/game/ant/genome.hpp deleted file mode 100644 index 6733e1f..0000000 --- a/src/game/ant/genome.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_GENOME_HPP -#define ANTKEEPER_GAME_ANT_GENOME_HPP - -#include "game/ant/gene/antennae.hpp" -#include "game/ant/gene/body-size.hpp" -#include "game/ant/gene/cocoon.hpp" -#include "game/ant/gene/diet.hpp" -#include "game/ant/gene/egg.hpp" -#include "game/ant/gene/eyes.hpp" -#include "game/ant/gene/foraging-time.hpp" -#include "game/ant/gene/founding-mode.hpp" -#include "game/ant/gene/gaster.hpp" -#include "game/ant/gene/head.hpp" -#include "game/ant/gene/larva.hpp" -#include "game/ant/gene/legs.hpp" -#include "game/ant/gene/mandibles.hpp" -#include "game/ant/gene/mesosoma.hpp" -#include "game/ant/gene/nest-site.hpp" -#include "game/ant/gene/ocelli.hpp" -#include "game/ant/gene/pigmentation.hpp" -#include "game/ant/gene/pilosity.hpp" -#include "game/ant/gene/sculpturing.hpp" -#include "game/ant/gene/sting.hpp" -#include "game/ant/gene/waist.hpp" -#include "game/ant/gene/wings.hpp" - -namespace ant { - -/** - * Complete set of ant genes. - */ -struct genome -{ - /// Constructs an empty genome. - genome(); - - const gene::antennae* antennae; - const gene::body_size* body_size; - const gene::cocoon* cocoon; - const gene::diet* diet; - const gene::egg* egg; - const gene::eyes* eyes; - const gene::foraging_time* foraging_time; - const gene::founding_mode* founding_mode; - const gene::gaster* gaster; - const gene::head* head; - const gene::larva* larva; - const gene::legs* legs; - const gene::mandibles* mandibles; - const gene::mesosoma* mesosoma; - const gene::nest_site* nest_site; - const gene::ocelli* ocelli; - const gene::pigmentation* pigmentation; - const gene::pilosity* pilosity; - const gene::sculpturing* sculpturing; - const gene::sting* sting; - const gene::waist* waist; - const gene::wings* wings; -}; - -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_GENOME_HPP diff --git a/src/game/ant/phenome.hpp b/src/game/ant/phenome.hpp deleted file mode 100644 index 9902466..0000000 --- a/src/game/ant/phenome.hpp +++ /dev/null @@ -1,92 +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 . - */ - -#ifndef ANTKEEPER_GAME_ANT_PHENOME_HPP -#define ANTKEEPER_GAME_ANT_PHENOME_HPP - -#include "game/ant/phene/antennae.hpp" -#include "game/ant/phene/body-size.hpp" -#include "game/ant/phene/cocoon.hpp" -#include "game/ant/phene/diet.hpp" -#include "game/ant/phene/egg.hpp" -#include "game/ant/phene/eyes.hpp" -#include "game/ant/phene/foraging-time.hpp" -#include "game/ant/phene/founding-mode.hpp" -#include "game/ant/phene/gaster.hpp" -#include "game/ant/phene/head.hpp" -#include "game/ant/phene/larva.hpp" -#include "game/ant/phene/legs.hpp" -#include "game/ant/phene/mandibles.hpp" -#include "game/ant/phene/mesosoma.hpp" -#include "game/ant/phene/nest-site.hpp" -#include "game/ant/phene/ocelli.hpp" -#include "game/ant/phene/pigmentation.hpp" -#include "game/ant/phene/pilosity.hpp" -#include "game/ant/phene/sculpturing.hpp" -#include "game/ant/phene/sting.hpp" -#include "game/ant/phene/waist.hpp" -#include "game/ant/phene/wings.hpp" -#include "game/ant/genome.hpp" -#include "game/ant/caste.hpp" - -namespace ant { - -/** - * Complete set of ant phenes. - */ -struct phenome -{ - /** - * Constructs a phenome for a given caste. - * - * @param genome Ant genome. - * @param caste Ant caste. - */ - phenome(const genome& genome, caste caste); - - /// Constructs an empty phenome. - phenome(); - - const phene::antennae* antennae; - const phene::body_size* body_size; - const phene::cocoon* cocoon; - const phene::diet* diet; - const phene::egg* egg; - const phene::eyes* eyes; - const phene::foraging_time* foraging_time; - const phene::founding_mode* founding_mode; - const phene::gaster* gaster; - const phene::head* head; - const phene::larva* larva; - const phene::legs* legs; - const phene::mandibles* mandibles; - const phene::mesosoma* mesosoma; - const phene::nest_site* nest_site; - const phene::ocelli* ocelli; - const phene::pigmentation* pigmentation; - const phene::pilosity* pilosity; - const phene::sculpturing* sculpturing; - const phene::sting* sting; - const phene::waist* waist; - const phene::wings* wings; -}; - -} // namespace ant - -#endif // ANTKEEPER_GAME_ANT_PHENOME_HPP diff --git a/src/game/components/caste-component.hpp b/src/game/components/ant-caste-component.hpp similarity index 77% rename from src/game/components/caste-component.hpp rename to src/game/components/ant-caste-component.hpp index b9b2d40..8d8e59c 100644 --- a/src/game/components/caste-component.hpp +++ b/src/game/components/ant-caste-component.hpp @@ -17,20 +17,18 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_CASTE_COMPONENT_HPP -#define ANTKEEPER_GAME_CASTE_COMPONENT_HPP +#ifndef ANTKEEPER_GAME_ANT_CASTE_COMPONENT_HPP +#define ANTKEEPER_GAME_ANT_CASTE_COMPONENT_HPP -#include "game/ant/caste.hpp" +#include "game/ant/ant-caste.hpp" - -struct caste_component +struct ant_caste_component { /// Caste type. - ::ant::caste type; + ant_caste type; /// Subcaste type. - //::ant::subcaste subtype; + //ant_subcaste subtype; }; - -#endif // ANTKEEPER_GAME_CASTE_COMPONENT_HPP +#endif // ANTKEEPER_GAME_ANT_CASTE_COMPONENT_HPP diff --git a/src/game/components/model-component.hpp b/src/game/components/model-component.hpp index ca33a2c..5c36991 100644 --- a/src/game/components/model-component.hpp +++ b/src/game/components/model-component.hpp @@ -22,12 +22,12 @@ #include #include - +#include struct model_component { - render::model* render_model; - std::unordered_map materials; + std::shared_ptr render_model; + std::unordered_map> materials; int instance_count; unsigned int layers; }; diff --git a/src/engine/resources/file-buffer.hpp b/src/game/components/name-component.hpp similarity index 76% rename from src/engine/resources/file-buffer.hpp rename to src/game/components/name-component.hpp index 752a8a4..b6220c3 100644 --- a/src/engine/resources/file-buffer.hpp +++ b/src/game/components/name-component.hpp @@ -17,12 +17,18 @@ * along with Antkeeper source code. If not, see . */ -#ifndef FILE_BUFFER_HPP -#define FILE_BUFFER_HPP +#ifndef ANTKEEPER_NAME_COMPONENT_HPP +#define ANTKEEPER_NAME_COMPONENT_HPP -#include -#include +#include -typedef std::vector file_buffer; +/** + * Supplies a human-readable name. + */ +struct name_component +{ + /// UTF-8 encoded name. + std::string name; +}; -#endif // FILE_BUFFER_HPP +#endif // ANTKEEPER_NAME_COMPONENT_HPP diff --git a/src/game/control-profile.cpp b/src/game/control-profile.cpp index 95a040b..d90b6e8 100644 --- a/src/game/control-profile.cpp +++ b/src/game/control-profile.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include /** @@ -84,7 +85,7 @@ void serializer<::control_profile>::serialize(const ::control_profile& profile, } // Write settings - serializer>().serialize(profile.settings, ctx); + serializer>().serialize(profile.settings, ctx); } /** @@ -109,7 +110,7 @@ void deserializer<::control_profile>::deserialize(::control_profile& profile, de for (std::uint64_t i = 0; i < size; ++i) { // Read key - std::uint32_t key = 0; + hash::fnv1a32_t key; ctx.read32(reinterpret_cast(&key), 1); // Read mapping type @@ -174,5 +175,15 @@ void deserializer<::control_profile>::deserialize(::control_profile& profile, de } // Read settings - deserializer>().deserialize(profile.settings, ctx); + deserializer>().deserialize(profile.settings, ctx); +} + +template <> +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) +{ + std::unique_ptr profile = std::make_unique(); + + deserializer().deserialize(*profile, ctx); + + return profile; } diff --git a/src/game/control-profile.hpp b/src/game/control-profile.hpp index c0b86e5..126d2e5 100644 --- a/src/game/control-profile.hpp +++ b/src/game/control-profile.hpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include @@ -31,10 +31,10 @@ struct control_profile { public: /// Input mappings. - std::multimap> mappings; + std::multimap> mappings; /// Profile-specific settings. - dict settings; + dict settings; }; diff --git a/src/game/controls.cpp b/src/game/controls.cpp index f0b3b64..994aa31 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -23,11 +23,10 @@ #include "game/control-profile.hpp" #include "game/states/pause-menu-state.hpp" #include -#include +#include #include #include -using namespace hash::literals; void reset_control_profile(::control_profile& profile) { @@ -38,103 +37,103 @@ void reset_control_profile(::control_profile& profile) settings.clear(); // Fullscreen - mappings.emplace("fullscreen"_fnv1a32, new input::key_mapping(nullptr, input::scancode::f11, 0, false)); - mappings.emplace("fullscreen"_fnv1a32, new input::key_mapping(nullptr, input::scancode::enter, input::modifier_key::alt, false)); + mappings.emplace("fullscreen", std::make_unique(nullptr, input::scancode::f11, 0, false)); + mappings.emplace("fullscreen", std::make_unique(nullptr, input::scancode::enter, input::modifier_key::alt, false)); // Screenshot - mappings.emplace("screenshot"_fnv1a32, new input::key_mapping(nullptr, input::scancode::f12, 0, false)); - mappings.emplace("screenshot"_fnv1a32, new input::key_mapping(nullptr, input::scancode::print_screen, 0, false)); + mappings.emplace("screenshot", std::make_unique(nullptr, input::scancode::f12, 0, false)); + mappings.emplace("screenshot", std::make_unique(nullptr, input::scancode::print_screen, 0, false)); // Menu up - mappings.emplace("menu_up"_fnv1a32, new input::key_mapping(nullptr, input::scancode::up, 0, true)); - mappings.emplace("menu_up"_fnv1a32, new input::key_mapping(nullptr, input::scancode::w, 0, true)); - mappings.emplace("menu_up"_fnv1a32, new input::key_mapping(nullptr, input::scancode::i, 0, true)); - mappings.emplace("menu_up"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_y, true)); - mappings.emplace("menu_up"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::right_stick_y, true)); - mappings.emplace("menu_up"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::dpad_up)); + mappings.emplace("menu_up", std::make_unique(nullptr, input::scancode::up, 0, true)); + mappings.emplace("menu_up", std::make_unique(nullptr, input::scancode::w, 0, true)); + mappings.emplace("menu_up", std::make_unique(nullptr, input::scancode::i, 0, true)); + mappings.emplace("menu_up", std::make_unique(nullptr, input::gamepad_axis::left_stick_y, true)); + mappings.emplace("menu_up", std::make_unique(nullptr, input::gamepad_axis::right_stick_y, true)); + mappings.emplace("menu_up", std::make_unique(nullptr, input::gamepad_button::dpad_up)); // Menu down - mappings.emplace("menu_down"_fnv1a32, new input::key_mapping(nullptr, input::scancode::down, 0, true)); - mappings.emplace("menu_down"_fnv1a32, new input::key_mapping(nullptr, input::scancode::s, 0, true)); - mappings.emplace("menu_down"_fnv1a32, new input::key_mapping(nullptr, input::scancode::k, 0, true)); - mappings.emplace("menu_down"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_y, false)); - mappings.emplace("menu_down"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::right_stick_y, false)); - mappings.emplace("menu_down"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::dpad_down)); + mappings.emplace("menu_down", std::make_unique(nullptr, input::scancode::down, 0, true)); + mappings.emplace("menu_down", std::make_unique(nullptr, input::scancode::s, 0, true)); + mappings.emplace("menu_down", std::make_unique(nullptr, input::scancode::k, 0, true)); + mappings.emplace("menu_down", std::make_unique(nullptr, input::gamepad_axis::left_stick_y, false)); + mappings.emplace("menu_down", std::make_unique(nullptr, input::gamepad_axis::right_stick_y, false)); + mappings.emplace("menu_down", std::make_unique(nullptr, input::gamepad_button::dpad_down)); // Menu left - mappings.emplace("menu_left"_fnv1a32, new input::key_mapping(nullptr, input::scancode::left, 0, true)); - mappings.emplace("menu_left"_fnv1a32, new input::key_mapping(nullptr, input::scancode::a, 0, true)); - mappings.emplace("menu_left"_fnv1a32, new input::key_mapping(nullptr, input::scancode::j, 0, true)); - mappings.emplace("menu_left"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_x, true)); - mappings.emplace("menu_left"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::right_stick_x, true)); - mappings.emplace("menu_left"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::dpad_left)); - mappings.emplace("menu_left"_fnv1a32, new input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::x, true)); - mappings.emplace("menu_left"_fnv1a32, new input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::y, true)); + mappings.emplace("menu_left", std::make_unique(nullptr, input::scancode::left, 0, true)); + mappings.emplace("menu_left", std::make_unique(nullptr, input::scancode::a, 0, true)); + mappings.emplace("menu_left", std::make_unique(nullptr, input::scancode::j, 0, true)); + mappings.emplace("menu_left", std::make_unique(nullptr, input::gamepad_axis::left_stick_x, true)); + mappings.emplace("menu_left", std::make_unique(nullptr, input::gamepad_axis::right_stick_x, true)); + mappings.emplace("menu_left", std::make_unique(nullptr, input::gamepad_button::dpad_left)); + mappings.emplace("menu_left", std::make_unique(nullptr, input::mouse_scroll_axis::x, true)); + mappings.emplace("menu_left", std::make_unique(nullptr, input::mouse_scroll_axis::y, true)); // Menu right - mappings.emplace("menu_right"_fnv1a32, new input::key_mapping(nullptr, input::scancode::right, 0, true)); - mappings.emplace("menu_right"_fnv1a32, new input::key_mapping(nullptr, input::scancode::d, 0, true)); - mappings.emplace("menu_right"_fnv1a32, new input::key_mapping(nullptr, input::scancode::l, 0, true)); - mappings.emplace("menu_right"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_x, false)); - mappings.emplace("menu_right"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::right_stick_x, false)); - mappings.emplace("menu_right"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::dpad_right)); - mappings.emplace("menu_right"_fnv1a32, new input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::x, false)); - mappings.emplace("menu_right"_fnv1a32, new input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::y, false)); + mappings.emplace("menu_right", std::make_unique(nullptr, input::scancode::right, 0, true)); + mappings.emplace("menu_right", std::make_unique(nullptr, input::scancode::d, 0, true)); + mappings.emplace("menu_right", std::make_unique(nullptr, input::scancode::l, 0, true)); + mappings.emplace("menu_right", std::make_unique(nullptr, input::gamepad_axis::left_stick_x, false)); + mappings.emplace("menu_right", std::make_unique(nullptr, input::gamepad_axis::right_stick_x, false)); + mappings.emplace("menu_right", std::make_unique(nullptr, input::gamepad_button::dpad_right)); + mappings.emplace("menu_right", std::make_unique(nullptr, input::mouse_scroll_axis::x, false)); + mappings.emplace("menu_right", std::make_unique(nullptr, input::mouse_scroll_axis::y, false)); // Menu select - mappings.emplace("menu_select"_fnv1a32, new input::key_mapping(nullptr, input::scancode::enter, 0, false)); - mappings.emplace("menu_select"_fnv1a32, new input::key_mapping(nullptr, input::scancode::space, 0, false)); - mappings.emplace("menu_select"_fnv1a32, new input::key_mapping(nullptr, input::scancode::e, 0, false)); - mappings.emplace("menu_select"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::a)); + mappings.emplace("menu_select", std::make_unique(nullptr, input::scancode::enter, 0, false)); + mappings.emplace("menu_select", std::make_unique(nullptr, input::scancode::space, 0, false)); + mappings.emplace("menu_select", std::make_unique(nullptr, input::scancode::e, 0, false)); + mappings.emplace("menu_select", std::make_unique(nullptr, input::gamepad_button::a)); // Menu back - mappings.emplace("menu_back"_fnv1a32, new input::key_mapping(nullptr, input::scancode::escape, 0, false)); - mappings.emplace("menu_back"_fnv1a32, new input::key_mapping(nullptr, input::scancode::backspace, 0, false)); - mappings.emplace("menu_back"_fnv1a32, new input::key_mapping(nullptr, input::scancode::q, 0, false)); - mappings.emplace("menu_back"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::b)); - mappings.emplace("menu_back"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::back)); + mappings.emplace("menu_back", std::make_unique(nullptr, input::scancode::escape, 0, false)); + mappings.emplace("menu_back", std::make_unique(nullptr, input::scancode::backspace, 0, false)); + mappings.emplace("menu_back", std::make_unique(nullptr, input::scancode::q, 0, false)); + mappings.emplace("menu_back", std::make_unique(nullptr, input::gamepad_button::b)); + mappings.emplace("menu_back", std::make_unique(nullptr, input::gamepad_button::back)); // Menu modifier - mappings.emplace("menu_modifier"_fnv1a32, new input::key_mapping(nullptr, input::scancode::left_shift, 0, false)); - mappings.emplace("menu_modifier"_fnv1a32, new input::key_mapping(nullptr, input::scancode::right_shift, 0, false)); + mappings.emplace("menu_modifier", std::make_unique(nullptr, input::scancode::left_shift, 0, false)); + mappings.emplace("menu_modifier", std::make_unique(nullptr, input::scancode::right_shift, 0, false)); // Move forward - mappings.emplace("move_forward"_fnv1a32, new input::key_mapping(nullptr, input::scancode::w, 0, false)); - mappings.emplace("move_forward"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_y, true)); + mappings.emplace("move_forward", std::make_unique(nullptr, input::scancode::w, 0, false)); + mappings.emplace("move_forward", std::make_unique(nullptr, input::gamepad_axis::left_stick_y, true)); // Move back - mappings.emplace("move_back"_fnv1a32, new input::key_mapping(nullptr, input::scancode::s, 0, false)); - mappings.emplace("move_back"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_y, false)); + mappings.emplace("move_back", std::make_unique(nullptr, input::scancode::s, 0, false)); + mappings.emplace("move_back", std::make_unique(nullptr, input::gamepad_axis::left_stick_y, false)); // Move left - mappings.emplace("move_left"_fnv1a32, new input::key_mapping(nullptr, input::scancode::a, 0, false)); - mappings.emplace("move_left"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_x, true)); + mappings.emplace("move_left", std::make_unique(nullptr, input::scancode::a, 0, false)); + mappings.emplace("move_left", std::make_unique(nullptr, input::gamepad_axis::left_stick_x, true)); // Move right - mappings.emplace("move_right"_fnv1a32, new input::key_mapping(nullptr, input::scancode::d, 0, false)); - mappings.emplace("move_right"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_x, false)); + mappings.emplace("move_right", std::make_unique(nullptr, input::scancode::d, 0, false)); + mappings.emplace("move_right", std::make_unique(nullptr, input::gamepad_axis::left_stick_x, false)); // Move up - mappings.emplace("move_up"_fnv1a32, new input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::y, false)); - mappings.emplace("move_up"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::right_trigger, false)); + mappings.emplace("move_up", std::make_unique(nullptr, input::mouse_scroll_axis::y, false)); + mappings.emplace("move_up", std::make_unique(nullptr, input::gamepad_axis::right_trigger, false)); // Move down - mappings.emplace("move_down"_fnv1a32, new input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::y, true)); - mappings.emplace("move_down"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_trigger, false)); + mappings.emplace("move_down", std::make_unique(nullptr, input::mouse_scroll_axis::y, true)); + mappings.emplace("move_down", std::make_unique(nullptr, input::gamepad_axis::left_trigger, false)); // Pause - mappings.emplace("pause"_fnv1a32, new input::key_mapping(nullptr, input::scancode::escape, 0, false)); - mappings.emplace("pause"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::start)); + mappings.emplace("pause", std::make_unique(nullptr, input::scancode::escape, 0, false)); + mappings.emplace("pause", std::make_unique(nullptr, input::gamepad_button::start)); // Pick mate - mappings.emplace("pick_mate"_fnv1a32, new input::mouse_button_mapping(nullptr, input::mouse_button::left)); - mappings.emplace("pick_mate"_fnv1a32, new input::mouse_button_mapping(nullptr, input::mouse_button::middle)); - mappings.emplace("pick_mate"_fnv1a32, new input::mouse_button_mapping(nullptr, input::mouse_button::right)); + mappings.emplace("pick_mate", std::make_unique(nullptr, input::mouse_button::left)); + mappings.emplace("pick_mate", std::make_unique(nullptr, input::mouse_button::middle)); + mappings.emplace("pick_mate", std::make_unique(nullptr, input::mouse_button::right)); } void apply_control_profile(::game& ctx, const ::control_profile& profile) { - auto add_mappings = [&profile](input::action_map& map, input::action& action, std::uint32_t key) + auto add_mappings = [&profile](input::action_map& map, input::action& action, hash::fnv1a32_t key) { auto range = profile.mappings.equal_range(key); for (auto i = range.first; i != range.second; ++i) @@ -145,36 +144,36 @@ void apply_control_profile(::game& ctx, const ::control_profile& profile) // Window controls ctx.window_action_map.remove_mappings(); - add_mappings(ctx.window_action_map, ctx.fullscreen_action, "fullscreen"_fnv1a32); - add_mappings(ctx.window_action_map, ctx.screenshot_action, "screenshot"_fnv1a32); + add_mappings(ctx.window_action_map, ctx.fullscreen_action, "fullscreen"); + add_mappings(ctx.window_action_map, ctx.screenshot_action, "screenshot"); // Menu controls ctx.menu_action_map.remove_mappings(); - add_mappings(ctx.menu_action_map, ctx.menu_up_action, "menu_up"_fnv1a32); - add_mappings(ctx.menu_action_map, ctx.menu_down_action, "menu_down"_fnv1a32); - add_mappings(ctx.menu_action_map, ctx.menu_left_action, "menu_left"_fnv1a32); - add_mappings(ctx.menu_action_map, ctx.menu_right_action, "menu_right"_fnv1a32); - add_mappings(ctx.menu_action_map, ctx.menu_select_action, "menu_select"_fnv1a32); - add_mappings(ctx.menu_action_map, ctx.menu_back_action, "menu_back"_fnv1a32); - add_mappings(ctx.menu_action_map, ctx.menu_modifier_action, "menu_modifier"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_up_action, "menu_up"); + add_mappings(ctx.menu_action_map, ctx.menu_down_action, "menu_down"); + add_mappings(ctx.menu_action_map, ctx.menu_left_action, "menu_left"); + add_mappings(ctx.menu_action_map, ctx.menu_right_action, "menu_right"); + add_mappings(ctx.menu_action_map, ctx.menu_select_action, "menu_select"); + add_mappings(ctx.menu_action_map, ctx.menu_back_action, "menu_back"); + add_mappings(ctx.menu_action_map, ctx.menu_modifier_action, "menu_modifier"); // Movement controls ctx.movement_action_map.remove_mappings(); - add_mappings(ctx.movement_action_map, ctx.move_forward_action, "move_forward"_fnv1a32); - add_mappings(ctx.movement_action_map, ctx.move_back_action, "move_back"_fnv1a32); - add_mappings(ctx.movement_action_map, ctx.move_left_action, "move_left"_fnv1a32); - add_mappings(ctx.movement_action_map, ctx.move_right_action, "move_right"_fnv1a32); - add_mappings(ctx.movement_action_map, ctx.move_up_action, "move_up"_fnv1a32); - add_mappings(ctx.movement_action_map, ctx.move_down_action, "move_down"_fnv1a32); - add_mappings(ctx.movement_action_map, ctx.pause_action, "pause"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.move_forward_action, "move_forward"); + add_mappings(ctx.movement_action_map, ctx.move_back_action, "move_back"); + add_mappings(ctx.movement_action_map, ctx.move_left_action, "move_left"); + add_mappings(ctx.movement_action_map, ctx.move_right_action, "move_right"); + add_mappings(ctx.movement_action_map, ctx.move_up_action, "move_up"); + add_mappings(ctx.movement_action_map, ctx.move_down_action, "move_down"); + add_mappings(ctx.movement_action_map, ctx.pause_action, "pause"); // Nuptial flight controls - add_mappings(ctx.nuptial_flight_action_map, ctx.pick_mate_action, "pick_mate"_fnv1a32); + add_mappings(ctx.nuptial_flight_action_map, ctx.pick_mate_action, "pick_mate"); } void update_control_profile(::game& ctx, ::control_profile& profile) { - auto add_mappings = [&profile](const input::action_map& map, const input::action& action, std::uint32_t key) + auto add_mappings = [&profile](const input::action_map& map, const input::action& action, hash::fnv1a32_t key) { auto gamepad_axis_mappings = map.get_gamepad_axis_mappings(action); auto gamepad_button_mappings = map.get_gamepad_button_mappings(action); @@ -185,56 +184,56 @@ void update_control_profile(::game& ctx, ::control_profile& profile) for (const auto& mapping: gamepad_axis_mappings) { - profile.mappings.emplace(key, new input::gamepad_axis_mapping(mapping)); + profile.mappings.emplace(key, std::make_unique(mapping)); } for (const auto& mapping: gamepad_button_mappings) { - profile.mappings.emplace(key, new input::gamepad_button_mapping(mapping)); + profile.mappings.emplace(key, std::make_unique(mapping)); } for (const auto& mapping: key_mappings) { - profile.mappings.emplace(key, new input::key_mapping(mapping)); + profile.mappings.emplace(key, std::make_unique(mapping)); } for (const auto& mapping: mouse_button_mappings) { - profile.mappings.emplace(key, new input::mouse_button_mapping(mapping)); + profile.mappings.emplace(key, std::make_unique(mapping)); } for (const auto& mapping: mouse_motion_mappings) { - profile.mappings.emplace(key, new input::mouse_motion_mapping(mapping)); + profile.mappings.emplace(key, std::make_unique(mapping)); } for (const auto& mapping: mouse_scroll_mappings) { - profile.mappings.emplace(key, new input::mouse_scroll_mapping(mapping)); + profile.mappings.emplace(key, std::make_unique(mapping)); } }; profile.mappings.clear(); // Window controls - add_mappings(ctx.window_action_map, ctx.fullscreen_action, "fullscreen"_fnv1a32); - add_mappings(ctx.window_action_map, ctx.screenshot_action, "screenshot"_fnv1a32); + add_mappings(ctx.window_action_map, ctx.fullscreen_action, "fullscreen"); + add_mappings(ctx.window_action_map, ctx.screenshot_action, "screenshot"); // Menu controls - add_mappings(ctx.menu_action_map, ctx.menu_up_action, "menu_up"_fnv1a32); - add_mappings(ctx.menu_action_map, ctx.menu_down_action, "menu_down"_fnv1a32); - add_mappings(ctx.menu_action_map, ctx.menu_left_action, "menu_left"_fnv1a32); - add_mappings(ctx.menu_action_map, ctx.menu_right_action, "menu_right"_fnv1a32); - add_mappings(ctx.menu_action_map, ctx.menu_select_action, "menu_select"_fnv1a32); - add_mappings(ctx.menu_action_map, ctx.menu_back_action, "menu_back"_fnv1a32); - add_mappings(ctx.menu_action_map, ctx.menu_modifier_action, "menu_modifier"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_up_action, "menu_up"); + add_mappings(ctx.menu_action_map, ctx.menu_down_action, "menu_down"); + add_mappings(ctx.menu_action_map, ctx.menu_left_action, "menu_left"); + add_mappings(ctx.menu_action_map, ctx.menu_right_action, "menu_right"); + add_mappings(ctx.menu_action_map, ctx.menu_select_action, "menu_select"); + add_mappings(ctx.menu_action_map, ctx.menu_back_action, "menu_back"); + add_mappings(ctx.menu_action_map, ctx.menu_modifier_action, "menu_modifier"); // Movement controls - add_mappings(ctx.movement_action_map, ctx.move_forward_action, "move_forward"_fnv1a32); - add_mappings(ctx.movement_action_map, ctx.move_back_action, "move_back"_fnv1a32); - add_mappings(ctx.movement_action_map, ctx.move_left_action, "move_left"_fnv1a32); - add_mappings(ctx.movement_action_map, ctx.move_right_action, "move_right"_fnv1a32); - add_mappings(ctx.movement_action_map, ctx.move_up_action, "move_up"_fnv1a32); - add_mappings(ctx.movement_action_map, ctx.move_down_action, "move_down"_fnv1a32); - add_mappings(ctx.movement_action_map, ctx.pause_action, "pause"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.move_forward_action, "move_forward"); + add_mappings(ctx.movement_action_map, ctx.move_back_action, "move_back"); + add_mappings(ctx.movement_action_map, ctx.move_left_action, "move_left"); + add_mappings(ctx.movement_action_map, ctx.move_right_action, "move_right"); + add_mappings(ctx.movement_action_map, ctx.move_up_action, "move_up"); + add_mappings(ctx.movement_action_map, ctx.move_down_action, "move_down"); + add_mappings(ctx.movement_action_map, ctx.pause_action, "pause"); // Nuptial flight controls - add_mappings(ctx.nuptial_flight_action_map, ctx.pick_mate_action, "pick_mate"_fnv1a32); + add_mappings(ctx.nuptial_flight_action_map, ctx.pick_mate_action, "pick_mate"); } void setup_window_controls(::game& ctx) diff --git a/src/game/controls.hpp b/src/game/controls.hpp index f4e9bee..61061bc 100644 --- a/src/game/controls.hpp +++ b/src/game/controls.hpp @@ -21,7 +21,7 @@ #define ANTKEEPER_GAME_CONTROLS_HPP #include "game/game.hpp" -#include +#include #include #include diff --git a/src/game/ecoregion-loader.cpp b/src/game/ecoregion-loader.cpp index ffa7c71..a21423b 100644 --- a/src/game/ecoregion-loader.cpp +++ b/src/game/ecoregion-loader.cpp @@ -20,22 +20,23 @@ #include "game/ecoregion.hpp" #include #include -#include +#include +#include #include template <> -::ecoregion* resource_loader<::ecoregion>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { // Load JSON data - json* data = resource_loader::load(resource_manager, file, path); + auto json_data = resource_loader::load(resource_manager, ctx); // Validate ecoregion file - auto ecoregion_element = data->find("ecoregion"); - if (ecoregion_element == data->end()) + auto ecoregion_element = json_data->find("ecoregion"); + if (ecoregion_element == json_data->end()) throw std::runtime_error("Invalid ecoregion file."); // Allocate and init ecoregion - ::ecoregion* ecoregion = new ::ecoregion(); + std::unique_ptr<::ecoregion> ecoregion = std::make_unique<::ecoregion>(); ecoregion->elevation = 0.0f; ecoregion->latitude = 0.0f; ecoregion->longitude = 0.0f; @@ -58,11 +59,11 @@ template <> if (auto terrain_element = ecoregion_element->find("terrain"); terrain_element != ecoregion_element->end()) { if (auto element = terrain_element->find("material"); element != terrain_element->end()) - ecoregion->terrain_material = resource_manager->load(element->get()); + ecoregion->terrain_material = resource_manager.load(element->get()); if (auto element = terrain_element->find("albedo"); element != terrain_element->end()) ecoregion->terrain_albedo = {(*element)[0].get(), (*element)[1].get(), (*element)[2].get()}; if (auto element = terrain_element->find("horizon_material"); element != terrain_element->end()) - ecoregion->horizon_material = resource_manager->load(element->get()); + ecoregion->horizon_material = resource_manager.load(element->get()); } // Load gene pools @@ -73,7 +74,7 @@ template <> { // Allocate gene pool ecoregion->gene_pools.resize(ecoregion->gene_pools.size() + 1); - ::ant::gene_pool& gene_pool = ecoregion->gene_pools.back(); + ant_gene_pool& gene_pool = ecoregion->gene_pools.back(); // Read gene pool name if (auto name_element = gene_pool_element->find("name"); name_element != gene_pool_element->end()) @@ -88,12 +89,12 @@ template <> for (auto antennae_element = antennae_elements->begin(); antennae_element != antennae_elements->end(); ++antennae_element) { float weight = 0.0f; - const ::ant::gene::antennae* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = antennae_element->find("weight"); weight_element != antennae_element->end()) weight = weight_element->get(); if (auto gene_element = antennae_element->find("gene"); gene_element != antennae_element->end()) - gene = resource_manager->load<::ant::gene::antennae>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -109,12 +110,12 @@ template <> for (auto body_size_element = body_size_elements->begin(); body_size_element != body_size_elements->end(); ++body_size_element) { float weight = 0.0f; - const ::ant::gene::body_size* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = body_size_element->find("weight"); weight_element != body_size_element->end()) weight = weight_element->get(); if (auto gene_element = body_size_element->find("gene"); gene_element != body_size_element->end()) - gene = resource_manager->load<::ant::gene::body_size>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -130,12 +131,12 @@ template <> for (auto cocoon_element = cocoon_elements->begin(); cocoon_element != cocoon_elements->end(); ++cocoon_element) { float weight = 0.0f; - const ::ant::gene::cocoon* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = cocoon_element->find("weight"); weight_element != cocoon_element->end()) weight = weight_element->get(); if (auto gene_element = cocoon_element->find("gene"); gene_element != cocoon_element->end()) - gene = resource_manager->load<::ant::gene::cocoon>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -151,12 +152,12 @@ template <> for (auto diet_element = diet_elements->begin(); diet_element != diet_elements->end(); ++diet_element) { float weight = 0.0f; - const ::ant::gene::diet* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = diet_element->find("weight"); weight_element != diet_element->end()) weight = weight_element->get(); if (auto gene_element = diet_element->find("gene"); gene_element != diet_element->end()) - gene = resource_manager->load<::ant::gene::diet>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -172,12 +173,12 @@ template <> for (auto egg_element = egg_elements->begin(); egg_element != egg_elements->end(); ++egg_element) { float weight = 0.0f; - const ::ant::gene::egg* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = egg_element->find("weight"); weight_element != egg_element->end()) weight = weight_element->get(); if (auto gene_element = egg_element->find("gene"); gene_element != egg_element->end()) - gene = resource_manager->load<::ant::gene::egg>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -193,12 +194,12 @@ template <> for (auto eyes_element = eyes_elements->begin(); eyes_element != eyes_elements->end(); ++eyes_element) { float weight = 0.0f; - const ::ant::gene::eyes* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = eyes_element->find("weight"); weight_element != eyes_element->end()) weight = weight_element->get(); if (auto gene_element = eyes_element->find("gene"); gene_element != eyes_element->end()) - gene = resource_manager->load<::ant::gene::eyes>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -214,12 +215,12 @@ template <> for (auto foraging_time_element = foraging_time_elements->begin(); foraging_time_element != foraging_time_elements->end(); ++foraging_time_element) { float weight = 0.0f; - const ::ant::gene::foraging_time* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = foraging_time_element->find("weight"); weight_element != foraging_time_element->end()) weight = weight_element->get(); if (auto gene_element = foraging_time_element->find("gene"); gene_element != foraging_time_element->end()) - gene = resource_manager->load<::ant::gene::foraging_time>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -235,12 +236,12 @@ template <> for (auto founding_mode_element = founding_mode_elements->begin(); founding_mode_element != founding_mode_elements->end(); ++founding_mode_element) { float weight = 0.0f; - const ::ant::gene::founding_mode* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = founding_mode_element->find("weight"); weight_element != founding_mode_element->end()) weight = weight_element->get(); if (auto gene_element = founding_mode_element->find("gene"); gene_element != founding_mode_element->end()) - gene = resource_manager->load<::ant::gene::founding_mode>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -256,12 +257,12 @@ template <> for (auto gaster_element = gaster_elements->begin(); gaster_element != gaster_elements->end(); ++gaster_element) { float weight = 0.0f; - const ::ant::gene::gaster* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = gaster_element->find("weight"); weight_element != gaster_element->end()) weight = weight_element->get(); if (auto gene_element = gaster_element->find("gene"); gene_element != gaster_element->end()) - gene = resource_manager->load<::ant::gene::gaster>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -277,12 +278,12 @@ template <> for (auto head_element = head_elements->begin(); head_element != head_elements->end(); ++head_element) { float weight = 0.0f; - const ::ant::gene::head* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = head_element->find("weight"); weight_element != head_element->end()) weight = weight_element->get(); if (auto gene_element = head_element->find("gene"); gene_element != head_element->end()) - gene = resource_manager->load<::ant::gene::head>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -298,12 +299,12 @@ template <> for (auto larva_element = larva_elements->begin(); larva_element != larva_elements->end(); ++larva_element) { float weight = 0.0f; - const ::ant::gene::larva* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = larva_element->find("weight"); weight_element != larva_element->end()) weight = weight_element->get(); if (auto gene_element = larva_element->find("gene"); gene_element != larva_element->end()) - gene = resource_manager->load<::ant::gene::larva>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -319,12 +320,12 @@ template <> for (auto legs_element = legs_elements->begin(); legs_element != legs_elements->end(); ++legs_element) { float weight = 0.0f; - const ::ant::gene::legs* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = legs_element->find("weight"); weight_element != legs_element->end()) weight = weight_element->get(); if (auto gene_element = legs_element->find("gene"); gene_element != legs_element->end()) - gene = resource_manager->load<::ant::gene::legs>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -340,12 +341,12 @@ template <> for (auto mandibles_element = mandibles_elements->begin(); mandibles_element != mandibles_elements->end(); ++mandibles_element) { float weight = 0.0f; - const ::ant::gene::mandibles* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = mandibles_element->find("weight"); weight_element != mandibles_element->end()) weight = weight_element->get(); if (auto gene_element = mandibles_element->find("gene"); gene_element != mandibles_element->end()) - gene = resource_manager->load<::ant::gene::mandibles>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -361,12 +362,12 @@ template <> for (auto mesosoma_element = mesosoma_elements->begin(); mesosoma_element != mesosoma_elements->end(); ++mesosoma_element) { float weight = 0.0f; - const ::ant::gene::mesosoma* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = mesosoma_element->find("weight"); weight_element != mesosoma_element->end()) weight = weight_element->get(); if (auto gene_element = mesosoma_element->find("gene"); gene_element != mesosoma_element->end()) - gene = resource_manager->load<::ant::gene::mesosoma>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -382,12 +383,12 @@ template <> for (auto nest_site_element = nest_site_elements->begin(); nest_site_element != nest_site_elements->end(); ++nest_site_element) { float weight = 0.0f; - const ::ant::gene::nest_site* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = nest_site_element->find("weight"); weight_element != nest_site_element->end()) weight = weight_element->get(); if (auto gene_element = nest_site_element->find("gene"); gene_element != nest_site_element->end()) - gene = resource_manager->load<::ant::gene::nest_site>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -403,12 +404,12 @@ template <> for (auto ocelli_element = ocelli_elements->begin(); ocelli_element != ocelli_elements->end(); ++ocelli_element) { float weight = 0.0f; - const ::ant::gene::ocelli* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = ocelli_element->find("weight"); weight_element != ocelli_element->end()) weight = weight_element->get(); if (auto gene_element = ocelli_element->find("gene"); gene_element != ocelli_element->end()) - gene = resource_manager->load<::ant::gene::ocelli>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -424,12 +425,12 @@ template <> for (auto pigmentation_element = pigmentation_elements->begin(); pigmentation_element != pigmentation_elements->end(); ++pigmentation_element) { float weight = 0.0f; - const ::ant::gene::pigmentation* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = pigmentation_element->find("weight"); weight_element != pigmentation_element->end()) weight = weight_element->get(); if (auto gene_element = pigmentation_element->find("gene"); gene_element != pigmentation_element->end()) - gene = resource_manager->load<::ant::gene::pigmentation>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -445,12 +446,12 @@ template <> for (auto pilosity_element = pilosity_elements->begin(); pilosity_element != pilosity_elements->end(); ++pilosity_element) { float weight = 0.0f; - const ::ant::gene::pilosity* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = pilosity_element->find("weight"); weight_element != pilosity_element->end()) weight = weight_element->get(); if (auto gene_element = pilosity_element->find("gene"); gene_element != pilosity_element->end()) - gene = resource_manager->load<::ant::gene::pilosity>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -466,12 +467,12 @@ template <> for (auto sculpturing_element = sculpturing_elements->begin(); sculpturing_element != sculpturing_elements->end(); ++sculpturing_element) { float weight = 0.0f; - const ::ant::gene::sculpturing* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = sculpturing_element->find("weight"); weight_element != sculpturing_element->end()) weight = weight_element->get(); if (auto gene_element = sculpturing_element->find("gene"); gene_element != sculpturing_element->end()) - gene = resource_manager->load<::ant::gene::sculpturing>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -487,12 +488,12 @@ template <> for (auto sting_element = sting_elements->begin(); sting_element != sting_elements->end(); ++sting_element) { float weight = 0.0f; - const ::ant::gene::sting* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = sting_element->find("weight"); weight_element != sting_element->end()) weight = weight_element->get(); if (auto gene_element = sting_element->find("gene"); gene_element != sting_element->end()) - gene = resource_manager->load<::ant::gene::sting>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -508,12 +509,12 @@ template <> for (auto waist_element = waist_elements->begin(); waist_element != waist_elements->end(); ++waist_element) { float weight = 0.0f; - const ::ant::gene::waist* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = waist_element->find("weight"); weight_element != waist_element->end()) weight = weight_element->get(); if (auto gene_element = waist_element->find("gene"); gene_element != waist_element->end()) - gene = resource_manager->load<::ant::gene::waist>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -529,12 +530,12 @@ template <> for (auto wings_element = wings_elements->begin(); wings_element != wings_elements->end(); ++wings_element) { float weight = 0.0f; - const ::ant::gene::wings* gene = nullptr; + std::shared_ptr gene; if (auto weight_element = wings_element->find("weight"); weight_element != wings_element->end()) weight = weight_element->get(); if (auto gene_element = wings_element->find("gene"); gene_element != wings_element->end()) - gene = resource_manager->load<::ant::gene::wings>(gene_element->get()); + gene = resource_manager.load(gene_element->get()); if (gene) { @@ -547,8 +548,5 @@ template <> } } - // Free JSON data - delete data; - return ecoregion; } diff --git a/src/game/ecoregion.hpp b/src/game/ecoregion.hpp index dcb0d59..e3d7169 100644 --- a/src/game/ecoregion.hpp +++ b/src/game/ecoregion.hpp @@ -22,10 +22,10 @@ #include #include -#include "game/ant/gene-pool.hpp" +#include "game/ant/ant-gene-pool.hpp" #include #include - +#include /** * @@ -45,16 +45,16 @@ struct ecoregion float longitude; /// Terrain material. - render::material* terrain_material; + std::shared_ptr terrain_material; /// Terrain albedo. float3 terrain_albedo; /// Horizon material. - render::material* horizon_material; + std::shared_ptr horizon_material; /// Array of gene pools. - std::vector gene_pools; + std::vector gene_pools; /// Discrete probability distribution of gene pools. std::discrete_distribution gene_pool_distribution; diff --git a/src/game/fonts.cpp b/src/game/fonts.cpp index d114810..601c297 100644 --- a/src/game/fonts.cpp +++ b/src/game/fonts.cpp @@ -28,14 +28,13 @@ #include "game/strings.hpp" #include -using namespace hash::literals; - - -static void build_bitmap_font(const type::typeface& typeface, float size, const std::unordered_set& charset, type::bitmap_font& font, render::material& material, gl::shader_program* shader_program) +static void build_bitmap_font(const type::typeface& typeface, float size, const std::unordered_set& charset, type::bitmap_font& font, render::material& material, std::shared_ptr shader_template) { // Get font metrics for given size if (type::font_metrics metrics; typeface.get_metrics(size, metrics)) + { font.set_font_metrics(metrics); + } // Format font bitmap image& font_bitmap = font.get_bitmap(); @@ -46,7 +45,9 @@ static void build_bitmap_font(const type::typeface& typeface, float size, const { // Skip missing glyphs if (!typeface.get_charset().contains(code)) + { continue; + } // Add glyph to font type::bitmap_glyph& glyph = font[code]; @@ -58,14 +59,14 @@ static void build_bitmap_font(const type::typeface& typeface, float size, const font.pack(); // Create font texture from bitmap - gl::texture_2d* font_texture = new gl::texture_2d(font_bitmap.get_width(), font_bitmap.get_height(), gl::pixel_type::uint_8, gl::pixel_format::r, gl::color_space::linear, font_bitmap.data()); + std::shared_ptr font_texture = std::make_shared(font_bitmap.width(), font_bitmap.height(), gl::pixel_type::uint_8, gl::pixel_format::r, gl::color_space::linear, font_bitmap.data()); font_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); font_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); // Create font material - material.set_blend_mode(render::blend_mode::translucent); - material.add_property("font_bitmap")->set_value(font_texture); - material.set_shader_program(shader_program); + material.set_blend_mode(render::material_blend_mode::translucent); + material.set_variable("font_bitmap", std::make_shared(1, font_texture)); + material.set_shader_template(shader_template); } void load_fonts(::game& ctx) @@ -74,7 +75,7 @@ void load_fonts(::game& ctx) bool dyslexia_font_loaded = false; if (ctx.dyslexia_font) { - const auto dyslexia_font_path = get_string(ctx, "font_dyslexia"_fnv1a32); + const auto dyslexia_font_path = get_string(ctx, "font_dyslexia"); ctx.typefaces["dyslexia"] = ctx.resource_manager->load(dyslexia_font_path); dyslexia_font_loaded = true; } @@ -90,9 +91,9 @@ void load_fonts(::game& ctx) else { // Load standard typefaces - const auto serif_font_path = get_string(ctx, "font_serif"_fnv1a32); - const auto sans_serif_font_path = get_string(ctx, "font_sans_serif"_fnv1a32); - const auto monospace_font_path = get_string(ctx, "font_monospace"_fnv1a32); + const auto serif_font_path = get_string(ctx, "font_serif"); + const auto sans_serif_font_path = get_string(ctx, "font_sans_serif"); + const auto monospace_font_path = get_string(ctx, "font_monospace"); ctx.typefaces["serif"] = ctx.resource_manager->load(serif_font_path); ctx.typefaces["sans_serif"] = ctx.resource_manager->load(sans_serif_font_path); @@ -122,7 +123,7 @@ void load_fonts(::game& ctx) */ // Load bitmap font shader - gl::shader_program* bitmap_font_shader = ctx.resource_manager->load("bitmap-font.glsl"); + std::shared_ptr font_shader_template = ctx.resource_manager->load("bitmap-font.glsl"); // Point size to pixel size conversion factor const float dpi = ctx.window_manager->get_display(0).get_dpi(); @@ -131,19 +132,19 @@ void load_fonts(::game& ctx) // Build debug font if (auto it = ctx.typefaces.find("monospace"); it != ctx.typefaces.end()) { - build_bitmap_font(*it->second, ctx.debug_font_size_pt * pt_to_px, it->second->get_charset(), ctx.debug_font, ctx.debug_font_material, bitmap_font_shader); + build_bitmap_font(*it->second, ctx.debug_font_size_pt * pt_to_px, it->second->get_charset(), ctx.debug_font, *ctx.debug_font_material, font_shader_template); } // Build menu font if (auto it = ctx.typefaces.find("sans_serif"); it != ctx.typefaces.end()) { - build_bitmap_font(*it->second, ctx.menu_font_size_pt * pt_to_px, it->second->get_charset(), ctx.menu_font, ctx.menu_font_material, bitmap_font_shader); + build_bitmap_font(*it->second, ctx.menu_font_size_pt * pt_to_px, it->second->get_charset(), ctx.menu_font, *ctx.menu_font_material, font_shader_template); } // Build title font if (auto it = ctx.typefaces.find("serif"); it != ctx.typefaces.end()) { - build_bitmap_font(*it->second, ctx.title_font_size_pt * pt_to_px, it->second->get_charset(), ctx.title_font, ctx.title_font_material, bitmap_font_shader); + build_bitmap_font(*it->second, ctx.title_font_size_pt * pt_to_px, it->second->get_charset(), ctx.title_font, *ctx.title_font_material, font_shader_template); } } diff --git a/src/game/game.cpp b/src/game/game.cpp index a92e30c..c54ce2c 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -72,7 +72,7 @@ #include #include #include -#include +#include #include #include #include @@ -85,7 +85,6 @@ #include #include #include -#include #include #include #include @@ -136,24 +135,30 @@ game::~game() { debug::log::trace("Booting down..."); + // Exit all active game states + while (!state_machine.empty()) + { + state_machine.pop(); + } + // Update window settings const auto& windowed_position = window->get_windowed_position(); const auto& windowed_size = window->get_windowed_size(); const bool maximized = window->is_maximized(); const bool fullscreen = window->is_fullscreen(); - (*settings)["window_x"_fnv1a32] = windowed_position.x(); - (*settings)["window_y"_fnv1a32] = windowed_position.y(); - (*settings)["window_w"_fnv1a32] = windowed_size.x(); - (*settings)["window_h"_fnv1a32] = windowed_size.y(); - (*settings)["maximized"_fnv1a32] = maximized; - (*settings)["fullscreen"_fnv1a32] = fullscreen; + (*settings)["window_x"] = windowed_position.x(); + (*settings)["window_y"] = windowed_position.y(); + (*settings)["window_w"] = windowed_size.x(); + (*settings)["window_h"] = windowed_size.y(); + (*settings)["maximized"] = maximized; + (*settings)["fullscreen"] = fullscreen; // Destruct window window.reset(); // Save settings - resource_manager->set_write_dir(shared_config_path); - resource_manager->save(settings.get(), "settings.cfg"); + resource_manager->set_write_path(shared_config_path); + resource_manager->save(*settings, "settings.cfg"); // Destruct input and window managers input_manager.reset(); @@ -361,9 +366,8 @@ void game::setup_resources() // Mount data package path resource_manager->mount(data_package_path); - // Include resource search paths in order of priority - resource_manager->include("/controls/"); - resource_manager->include("/"); + // Mount controls path + resource_manager->mount(shared_config_path / "controls"); } void game::load_settings() @@ -371,24 +375,18 @@ void game::load_settings() if (option_reset) { // Command-line reset option found, reset settings - settings = std::make_shared>(); - resource_manager->set_write_dir(shared_config_path); - resource_manager->save(settings.get(), "settings.cfg"); + settings = std::make_shared>(); + resource_manager->set_write_path(shared_config_path); + resource_manager->save(*settings, "settings.cfg"); debug::log::info("Settings reset"); } else { - settings = std::make_shared>(); - - dict* loaded_settings = resource_manager->load>("settings.cfg"); - if (loaded_settings) - { - /// @TODO: don't copy loaded settings, rather return a shared_ptr from the resource manager - *settings = *loaded_settings; - } - else + settings = resource_manager->load>("settings.cfg"); + if (!settings) { debug::log::info("Settings not found"); + settings = std::make_shared>(); } } } @@ -396,7 +394,7 @@ void game::load_settings() void game::setup_window() { // Construct window manager - window_manager.reset(app::window_manager::instance()); + window_manager = app::window_manager::instance(); // Default window settings std::string window_title = config::application_name; @@ -410,17 +408,17 @@ void game::setup_window() // Read window settings bool resize = false; - read_or_write_setting(*this, "window_title"_fnv1a32, window_title); - read_or_write_setting(*this, "window_x"_fnv1a32, window_x); - read_or_write_setting(*this, "window_y"_fnv1a32, window_y); - if (!read_or_write_setting(*this, "window_w"_fnv1a32, window_w) || - !read_or_write_setting(*this, "window_h"_fnv1a32, window_h)) + read_or_write_setting(*this, "window_title", window_title); + read_or_write_setting(*this, "window_x", window_x); + read_or_write_setting(*this, "window_y", window_y); + if (!read_or_write_setting(*this, "window_w", window_w) || + !read_or_write_setting(*this, "window_h", window_h)) { resize = true; } - read_or_write_setting(*this, "maximized"_fnv1a32, maximized); - read_or_write_setting(*this, "fullscreen"_fnv1a32, fullscreen); - read_or_write_setting(*this, "v_sync"_fnv1a32, v_sync); + read_or_write_setting(*this, "maximized", maximized); + read_or_write_setting(*this, "fullscreen", fullscreen); + read_or_write_setting(*this, "v_sync", v_sync); // If window size not set, resize and reposition relative to default display if (resize) @@ -491,12 +489,12 @@ void game::setup_audio() captions_size = 1.0f; // Read audio settings - read_or_write_setting(*this, "master_volume"_fnv1a32, master_volume); - read_or_write_setting(*this, "ambience_volume"_fnv1a32, ambience_volume); - read_or_write_setting(*this, "effects_volume"_fnv1a32, effects_volume); - read_or_write_setting(*this, "mono_audio"_fnv1a32, mono_audio); - read_or_write_setting(*this, "captions"_fnv1a32, captions); - read_or_write_setting(*this, "captions_size"_fnv1a32, captions_size); + read_or_write_setting(*this, "master_volume", master_volume); + read_or_write_setting(*this, "ambience_volume", ambience_volume); + read_or_write_setting(*this, "effects_volume", effects_volume); + read_or_write_setting(*this, "mono_audio", mono_audio); + read_or_write_setting(*this, "captions", captions); + read_or_write_setting(*this, "captions_size", captions_size); // Open audio device debug::log::trace("Opening audio device..."); @@ -560,7 +558,7 @@ void game::setup_audio() void game::setup_input() { // Construct input manager - input_manager.reset(app::input_manager::instance()); + input_manager = app::input_manager::instance(); // Process initial input events, such as connecting gamepads input_manager->update(); @@ -642,7 +640,7 @@ void game::load_strings() language_tag = "en"; // Read strings settings - read_or_write_setting(*this, "language_tag"_fnv1a32, language_tag); + read_or_write_setting(*this, "language_tag", language_tag); // Slugify language tag std::string language_slug = language_tag; @@ -664,11 +662,11 @@ void game::load_strings() debug::log::info("Language tag: {}", language_tag); // Change window title - const std::string window_title = get_string(*this, "window_title"_fnv1a32); + const std::string window_title = get_string(*this, "window_title"); window->set_title(window_title); // Update window title setting - (*settings)["window_title"_fnv1a32] = window_title; + (*settings)["window_title"] = window_title; debug::log::trace("Loaded strings"); } @@ -683,38 +681,37 @@ void game::setup_rendering() shadow_map_resolution = 4096; // Read rendering settings - read_or_write_setting(*this, "render_scale"_fnv1a32, render_scale); - read_or_write_setting(*this, "anti_aliasing_method"_fnv1a32, *reinterpret_cast*>(&anti_aliasing_method)); - read_or_write_setting(*this, "shadow_map_resolution"_fnv1a32, shadow_map_resolution); + read_or_write_setting(*this, "render_scale", render_scale); + read_or_write_setting(*this, "anti_aliasing_method", *reinterpret_cast*>(&anti_aliasing_method)); + read_or_write_setting(*this, "shadow_map_resolution", shadow_map_resolution); // Create framebuffers ::graphics::create_framebuffers(*this); - // Load blue noise texture - gl::texture_2d* blue_noise_map = resource_manager->load("blue-noise.tex"); - // Load fallback material - fallback_material = resource_manager->load("fallback.mtl"); + auto fallback_material = resource_manager->load("fallback.mtl"); // Setup common render passes { // Construct bloom pass bloom_pass = std::make_unique(window->get_rasterizer(), resource_manager.get()); - bloom_pass->set_source_texture(hdr_color_texture); + bloom_pass->set_source_texture(hdr_color_texture.get()); bloom_pass->set_mip_chain_length(5); + //bloom_pass->set_mip_chain_length(0); bloom_pass->set_filter_radius(0.005f); - common_final_pass = std::make_unique(window->get_rasterizer(), ldr_framebuffer_a, resource_manager.get()); - common_final_pass->set_color_texture(hdr_color_texture); + common_final_pass = std::make_unique(window->get_rasterizer(), ldr_framebuffer_a.get(), resource_manager.get()); + common_final_pass->set_color_texture(hdr_color_texture.get()); common_final_pass->set_bloom_texture(bloom_pass->get_bloom_texture()); common_final_pass->set_bloom_weight(0.04f); - common_final_pass->set_blue_noise_texture(blue_noise_map); + //common_final_pass->set_bloom_weight(0.0f); + common_final_pass->set_blue_noise_texture(resource_manager->load("blue-noise.tex")); fxaa_pass = std::make_unique(window->get_rasterizer(), &window->get_rasterizer()->get_default_framebuffer(), resource_manager.get()); - fxaa_pass->set_source_texture(ldr_color_texture_a); + fxaa_pass->set_source_texture(ldr_color_texture_a.get()); resample_pass = std::make_unique(window->get_rasterizer(), &window->get_rasterizer()->get_default_framebuffer(), resource_manager.get()); - resample_pass->set_source_texture(ldr_color_texture_b); + resample_pass->set_source_texture(ldr_color_texture_b.get()); resample_pass->set_enabled(false); // Configure anti-aliasing according to settings @@ -740,12 +737,12 @@ void game::setup_rendering() // Setup underground compositor { - underground_clear_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer); + underground_clear_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer.get()); underground_clear_pass->set_cleared_buffers(true, true, false); underground_clear_pass->set_clear_color({1, 0, 1, 0}); underground_clear_pass->set_clear_depth(-1.0f); - underground_material_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer, resource_manager.get()); + underground_material_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer.get(), resource_manager.get()); underground_material_pass->set_fallback_material(fallback_material); underground_compositor = std::make_unique(); @@ -759,27 +756,26 @@ void game::setup_rendering() // Setup surface compositor { - surface_shadow_map_clear_pass = std::make_unique(window->get_rasterizer(), shadow_map_framebuffer); + surface_shadow_map_clear_pass = std::make_unique(window->get_rasterizer(), shadow_map_framebuffer.get()); surface_shadow_map_clear_pass->set_cleared_buffers(false, true, false); surface_shadow_map_clear_pass->set_clear_depth(1.0f); surface_shadow_map_pass = std::make_unique(window->get_rasterizer(), resource_manager.get()); - surface_clear_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer); - surface_clear_pass->set_cleared_buffers(false, true, true); + surface_clear_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer.get()); + surface_clear_pass->set_clear_color({0.0f, 0.0f, 0.0f, 1.0f}); + surface_clear_pass->set_cleared_buffers(true, true, true); surface_clear_pass->set_clear_depth(-1.0f); - sky_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer, resource_manager.get()); - sky_pass->set_enabled(false); + sky_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer.get(), resource_manager.get()); sky_pass->set_magnification(3.0f); - ground_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer, resource_manager.get()); - ground_pass->set_enabled(false); + ground_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer.get(), resource_manager.get()); - surface_material_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer, resource_manager.get()); + surface_material_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer.get(), resource_manager.get()); surface_material_pass->set_fallback_material(fallback_material); - surface_outline_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer, resource_manager.get()); + surface_outline_pass = std::make_unique(window->get_rasterizer(), hdr_framebuffer.get(), resource_manager.get()); surface_outline_pass->set_outline_width(0.25f); surface_outline_pass->set_outline_color(float4{1.0f, 1.0f, 1.0f, 1.0f}); @@ -788,7 +784,7 @@ void game::setup_rendering() surface_compositor->add_pass(surface_shadow_map_pass.get()); surface_compositor->add_pass(surface_clear_pass.get()); surface_compositor->add_pass(sky_pass.get()); - surface_compositor->add_pass(ground_pass.get()); + //surface_compositor->add_pass(ground_pass.get()); surface_compositor->add_pass(surface_material_pass.get()); //surface_compositor->add_pass(surface_outline_pass.get()); surface_compositor->add_pass(bloom_pass.get()); @@ -813,7 +809,7 @@ void game::setup_rendering() std::size_t billboard_vertex_stride = sizeof(float) * billboard_vertex_size; std::size_t billboard_vertex_count = 6; - billboard_vbo = std::make_unique(sizeof(float) * billboard_vertex_size * billboard_vertex_count, billboard_vertex_data); + billboard_vbo = std::make_unique(gl::buffer_usage::static_draw, sizeof(float) * billboard_vertex_size * billboard_vertex_count, std::as_bytes(std::span{billboard_vertex_data})); billboard_vao = std::make_unique(); std::size_t attribute_offset = 0; @@ -918,11 +914,16 @@ void game::setup_ui() dyslexia_font = false; // Read UI settings - read_or_write_setting(*this, "font_scale"_fnv1a32, font_scale); - read_or_write_setting(*this, "debug_font_size_pt"_fnv1a32, debug_font_size_pt); - read_or_write_setting(*this, "menu_font_size_pt"_fnv1a32, menu_font_size_pt); - read_or_write_setting(*this, "title_font_size_pt"_fnv1a32, title_font_size_pt); - read_or_write_setting(*this, "dyslexia_font"_fnv1a32, dyslexia_font); + read_or_write_setting(*this, "font_scale", font_scale); + read_or_write_setting(*this, "debug_font_size_pt", debug_font_size_pt); + read_or_write_setting(*this, "menu_font_size_pt", menu_font_size_pt); + read_or_write_setting(*this, "title_font_size_pt", title_font_size_pt); + read_or_write_setting(*this, "dyslexia_font", dyslexia_font); + + // Allocate font materials + debug_font_material = std::make_shared(); + menu_font_material = std::make_shared(); + title_font_material = std::make_shared(); // Load fonts debug::log::trace("Loading fonts..."); @@ -957,41 +958,41 @@ void game::setup_ui() ui_camera->update_tweens(); // Menu BG material - menu_bg_material.set_shader_program(resource_manager->load("ui-element-untextured.glsl")); - auto menu_bg_tint = menu_bg_material.add_property("tint"); - menu_bg_tint->set_value(float4{0.0f, 0.0f, 0.0f, 0.5f}); - menu_bg_material.set_blend_mode(render::blend_mode::translucent); - menu_bg_material.update_tweens(); + menu_bg_material = std::make_shared(); + menu_bg_material->set_shader_template(resource_manager->load("ui-element-untextured.glsl")); + std::shared_ptr menu_bg_tint = std::make_shared(1, float4{0.0f, 0.0f, 0.0f, 0.5f}); + menu_bg_material->set_variable("tint", menu_bg_tint); + menu_bg_material->set_blend_mode(render::material_blend_mode::translucent); // Menu BG billboard menu_bg_billboard = std::make_unique(); menu_bg_billboard->set_active(false); - menu_bg_billboard->set_material(&menu_bg_material); + menu_bg_billboard->set_material(menu_bg_material); menu_bg_billboard->set_scale({std::ceil(viewport_size.x() * 0.5f), std::ceil(viewport_size.y() * 0.5f), 1.0f}); menu_bg_billboard->set_translation({std::floor(viewport_size.x() * 0.5f), std::floor(viewport_size.y() * 0.5f), -100.0f}); menu_bg_billboard->update_tweens(); // Create fade transition fade_transition = std::make_unique(); - fade_transition->get_material()->set_shader_program(resource_manager->load("fade-transition.glsl")); - fade_transition_color = fade_transition->get_material()->add_property("color"); - fade_transition_color->set_value({0, 0, 0}); + fade_transition->get_material()->set_shader_template(resource_manager->load("fade-transition.glsl")); + fade_transition_color = std::make_shared(1, float3{0, 0, 0}); + fade_transition->get_material()->set_variable("color", fade_transition_color); fade_transition->get_billboard()->set_translation({0, 0, 98}); fade_transition->get_billboard()->update_tweens(); // Create inner radial transition radial_transition_inner = std::make_unique(); - radial_transition_inner->get_material()->set_shader_program(resource_manager->load("radial-transition-inner.glsl")); + radial_transition_inner->get_material()->set_shader_template(resource_manager->load("radial-transition-inner.glsl")); // Create outer radial transition radial_transition_outer = std::make_unique(); - radial_transition_outer->get_material()->set_shader_program(resource_manager->load("radial-transition-outer.glsl")); + radial_transition_outer->get_material()->set_shader_template(resource_manager->load("radial-transition-outer.glsl")); // Menu BG animations { auto menu_bg_frame_callback = [menu_bg_tint](int channel, const float& opacity) { - menu_bg_tint->set_value(float4{0.0f, 0.0f, 0.0f, opacity}); + menu_bg_tint->set(float4{0.0f, 0.0f, 0.0f, opacity}); }; // Menu BG fade in animation @@ -1008,8 +1009,8 @@ void game::setup_ui() { ui_scene->add_object(menu_bg_billboard.get()); - menu_bg_tint->set_value(float4{0.0f, 0.0f, 0.0f, 0.0f}); - menu_bg_tint->update_tweens(); + menu_bg_tint->set(float4{0.0f, 0.0f, 0.0f, 0.0f}); + //menu_bg_tint->update_tweens(); menu_bg_billboard->set_active(true); } ); @@ -1190,26 +1191,24 @@ void game::setup_controls() // Default control profile settings control_profile_filename = "controls.cfg"; - control_profile = nullptr; // Read control profile settings - if (read_or_write_setting(*this, "control_profile"_fnv1a32, control_profile_filename)) + if (read_or_write_setting(*this, "control_profile", control_profile_filename)) { - /// @TODO: return a shared_ptr from the resource manager control_profile = resource_manager->load<::control_profile>(control_profile_filename); } if (!control_profile) { // Allocate control profile - control_profile = new ::control_profile(); + control_profile = std::make_shared<::control_profile>(); // Reset control profile to default settings. ::reset_control_profile(*control_profile); // Save control profile - resource_manager->set_write_dir(controls_path); - resource_manager->save(control_profile, control_profile_filename); + resource_manager->set_write_path(controls_path); + resource_manager->save(*control_profile, control_profile_filename); } // Apply control profile @@ -1233,7 +1232,7 @@ void game::setup_debugging() const auto& viewport_size = window->get_viewport_size(); frame_time_text = std::make_unique(); - frame_time_text->set_material(&debug_font_material); + frame_time_text->set_material(debug_font_material); frame_time_text->set_color({1.0f, 1.0f, 0.0f, 1.0f}); frame_time_text->set_font(&debug_font); frame_time_text->set_translation({std::round(0.0f), std::round(viewport_size.y() - debug_font.get_font_metrics().size), 99.0f}); @@ -1248,7 +1247,7 @@ void game::setup_loop() double update_frequency = 60.0; // Read loop settings - read_or_write_setting(*this, "update_frequency"_fnv1a32, update_frequency); + read_or_write_setting(*this, "update_frequency", update_frequency); // Set update frequency loop.set_update_frequency(update_frequency); diff --git a/src/game/game.hpp b/src/game/game.hpp index 4dd72f6..573648c 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -43,9 +43,8 @@ #include #include #include -#include +#include #include -#include #include #include #include @@ -155,7 +154,7 @@ public: std::filesystem::path controls_path; // Persistent settings - std::shared_ptr> settings; + std::shared_ptr> settings; // Window management and window event handling std::unique_ptr window_manager; @@ -176,22 +175,22 @@ public: // Localization and internationalization std::string language_tag; - i18n::string_map* string_map; + std::shared_ptr string_map; // Fonts - std::unordered_map typefaces; + std::unordered_map> typefaces; type::bitmap_font debug_font; type::bitmap_font menu_font; type::bitmap_font title_font; - render::material debug_font_material; - render::material menu_font_material; - render::material title_font_material; + std::shared_ptr debug_font_material; + std::shared_ptr menu_font_material; + std::shared_ptr title_font_material; // Action maps, actions, and action event handling std::string control_profile_filename; - ::control_profile* control_profile; + std::shared_ptr<::control_profile> control_profile; - std::unordered_map actions; + std::unordered_map actions; input::action_map window_action_map; input::action_map menu_action_map; input::action_map movement_action_map; @@ -221,7 +220,6 @@ public: std::vector> menu_mouse_subscriptions; std::vector> movement_action_subscriptions; - // Debugging math::moving_average average_frame_time; std::unique_ptr frame_time_text; @@ -243,15 +241,15 @@ public: ::loop loop; // Framebuffers - gl::texture_2d* hdr_color_texture; - gl::texture_2d* hdr_depth_texture; - gl::framebuffer* hdr_framebuffer; - gl::texture_2d* ldr_color_texture_a; - gl::framebuffer* ldr_framebuffer_a; - gl::texture_2d* ldr_color_texture_b; - gl::framebuffer* ldr_framebuffer_b; - gl::texture_2d* shadow_map_depth_texture; - gl::framebuffer* shadow_map_framebuffer; + std::unique_ptr hdr_color_texture; + std::unique_ptr hdr_depth_texture; + std::unique_ptr hdr_framebuffer; + std::unique_ptr ldr_color_texture_a; + std::unique_ptr ldr_framebuffer_a; + std::unique_ptr ldr_color_texture_b; + std::unique_ptr ldr_framebuffer_b; + std::unique_ptr shadow_map_depth_texture; + std::unique_ptr shadow_map_framebuffer; // Rendering //gl::rasterizer* rasterizer; @@ -260,7 +258,6 @@ public: int shadow_map_resolution; std::unique_ptr billboard_vbo; std::unique_ptr billboard_vao; - render::material* fallback_material; std::unique_ptr ui_clear_pass; std::unique_ptr ui_material_pass; std::unique_ptr ui_compositor; @@ -283,12 +280,16 @@ public: // Scene utilities scene::collection* active_scene; + std::unique_ptr sun_light; + std::unique_ptr moon_light; + std::unique_ptr sky_light; + std::unique_ptr bounce_light; // UI std::unique_ptr ui_scene; std::unique_ptr ui_camera; std::unique_ptr menu_bg_billboard; - render::material menu_bg_material; + std::shared_ptr menu_bg_material; std::unique_ptr> menu_fade_animation; std::unique_ptr> menu_bg_fade_in_animation; std::unique_ptr> menu_bg_fade_out_animation; @@ -321,7 +322,7 @@ public: std::unique_ptr> radial_transition_in; std::unique_ptr> radial_transition_out; std::unique_ptr fade_transition; - render::material_property* fade_transition_color; + std::shared_ptr fade_transition_color; std::unique_ptr radial_transition_inner; std::unique_ptr radial_transition_outer; std::unique_ptr> equip_tool_animation; @@ -339,7 +340,7 @@ public: // Entities std::unique_ptr entity_registry; - std::unordered_map entities; + std::unordered_map entities; // Systems std::unique_ptr<::behavior_system> behavior_system; diff --git a/src/game/graphics.cpp b/src/game/graphics.cpp index 71f2b24..aad086a 100644 --- a/src/game/graphics.cpp +++ b/src/game/graphics.cpp @@ -48,41 +48,41 @@ void create_framebuffers(::game& ctx) ctx.render_resolution = {static_cast(viewport_size.x() * ctx.render_scale + 0.5f), static_cast(viewport_size.y() * ctx.render_scale + 0.5f)}; // Create HDR framebuffer (32F color, 32F depth) - ctx.hdr_color_texture = new gl::texture_2d(ctx.render_resolution.x(), ctx.render_resolution.y(), gl::pixel_type::float_32, gl::pixel_format::rgb); + ctx.hdr_color_texture = std::make_unique(ctx.render_resolution.x(), ctx.render_resolution.y(), gl::pixel_type::float_32, gl::pixel_format::rgb); ctx.hdr_color_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); ctx.hdr_color_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); ctx.hdr_color_texture->set_max_anisotropy(0.0f); - ctx.hdr_depth_texture = new gl::texture_2d(ctx.render_resolution.x(), ctx.render_resolution.y(), gl::pixel_type::float_32, gl::pixel_format::ds); + ctx.hdr_depth_texture = std::make_unique(ctx.render_resolution.x(), ctx.render_resolution.y(), gl::pixel_type::float_32, gl::pixel_format::ds); ctx.hdr_depth_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); ctx.hdr_depth_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); ctx.hdr_depth_texture->set_max_anisotropy(0.0f); - ctx.hdr_framebuffer = new gl::framebuffer(ctx.render_resolution.x(), ctx.render_resolution.y()); - ctx.hdr_framebuffer->attach(gl::framebuffer_attachment_type::color, ctx.hdr_color_texture); - ctx.hdr_framebuffer->attach(gl::framebuffer_attachment_type::depth, ctx.hdr_depth_texture); - ctx.hdr_framebuffer->attach(gl::framebuffer_attachment_type::stencil, ctx.hdr_depth_texture); + ctx.hdr_framebuffer = std::make_unique(ctx.render_resolution.x(), ctx.render_resolution.y()); + ctx.hdr_framebuffer->attach(gl::framebuffer_attachment_type::color, ctx.hdr_color_texture.get()); + ctx.hdr_framebuffer->attach(gl::framebuffer_attachment_type::depth, ctx.hdr_depth_texture.get()); + ctx.hdr_framebuffer->attach(gl::framebuffer_attachment_type::stencil, ctx.hdr_depth_texture.get()); // Create LDR framebuffers (8-bit color, no depth) - ctx.ldr_color_texture_a = new gl::texture_2d(ctx.render_resolution.x(), ctx.render_resolution.y(), gl::pixel_type::uint_8, gl::pixel_format::rgb); + ctx.ldr_color_texture_a = std::make_unique(ctx.render_resolution.x(), ctx.render_resolution.y(), gl::pixel_type::uint_8, gl::pixel_format::rgb); ctx.ldr_color_texture_a->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); ctx.ldr_color_texture_a->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); ctx.ldr_color_texture_a->set_max_anisotropy(0.0f); - ctx.ldr_framebuffer_a = new gl::framebuffer(ctx.render_resolution.x(), ctx.render_resolution.y()); - ctx.ldr_framebuffer_a->attach(gl::framebuffer_attachment_type::color, ctx.ldr_color_texture_a); + ctx.ldr_framebuffer_a = std::make_unique(ctx.render_resolution.x(), ctx.render_resolution.y()); + ctx.ldr_framebuffer_a->attach(gl::framebuffer_attachment_type::color, ctx.ldr_color_texture_a.get()); - ctx.ldr_color_texture_b = new gl::texture_2d(ctx.render_resolution.x(), ctx.render_resolution.y(), gl::pixel_type::uint_8, gl::pixel_format::rgb); + ctx.ldr_color_texture_b = std::make_unique(ctx.render_resolution.x(), ctx.render_resolution.y(), gl::pixel_type::uint_8, gl::pixel_format::rgb); ctx.ldr_color_texture_b->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); ctx.ldr_color_texture_b->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); ctx.ldr_color_texture_b->set_max_anisotropy(0.0f); - ctx.ldr_framebuffer_b = new gl::framebuffer(ctx.render_resolution.x(), ctx.render_resolution.y()); - ctx.ldr_framebuffer_b->attach(gl::framebuffer_attachment_type::color, ctx.ldr_color_texture_b); + ctx.ldr_framebuffer_b = std::make_unique(ctx.render_resolution.x(), ctx.render_resolution.y()); + ctx.ldr_framebuffer_b->attach(gl::framebuffer_attachment_type::color, ctx.ldr_color_texture_b.get()); // Create shadow map framebuffer - ctx.shadow_map_depth_texture = new gl::texture_2d(ctx.shadow_map_resolution, ctx.shadow_map_resolution, gl::pixel_type::float_32, gl::pixel_format::d); + ctx.shadow_map_depth_texture = std::make_unique(ctx.shadow_map_resolution, ctx.shadow_map_resolution, gl::pixel_type::float_32, gl::pixel_format::d); ctx.shadow_map_depth_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); ctx.shadow_map_depth_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); ctx.shadow_map_depth_texture->set_max_anisotropy(0.0f); - ctx.shadow_map_framebuffer = new gl::framebuffer(ctx.shadow_map_resolution, ctx.shadow_map_resolution); - ctx.shadow_map_framebuffer->attach(gl::framebuffer_attachment_type::depth, ctx.shadow_map_depth_texture); + ctx.shadow_map_framebuffer = std::make_unique(ctx.shadow_map_resolution, ctx.shadow_map_resolution); + ctx.shadow_map_framebuffer->attach(gl::framebuffer_attachment_type::depth, ctx.shadow_map_depth_texture.get()); debug::log::trace("Created framebuffers"); } @@ -92,29 +92,20 @@ void destroy_framebuffers(::game& ctx) debug::log::trace("Destroying framebuffers..."); // Delete HDR framebuffer and its attachments - delete ctx.hdr_framebuffer; - ctx.hdr_framebuffer = nullptr; - delete ctx.hdr_color_texture; - ctx.hdr_color_texture = nullptr; - delete ctx.hdr_depth_texture; - ctx.hdr_depth_texture = nullptr; + ctx.hdr_framebuffer.reset(); + ctx.hdr_color_texture.reset(); + ctx.hdr_depth_texture.reset(); // Delete LDR framebuffers and attachments - delete ctx.ldr_framebuffer_a; - ctx.ldr_framebuffer_a = nullptr; - delete ctx.ldr_color_texture_a; - ctx.ldr_color_texture_a = nullptr; + ctx.ldr_framebuffer_a.reset(); + ctx.ldr_color_texture_a.reset(); - delete ctx.ldr_framebuffer_b; - ctx.ldr_framebuffer_b = nullptr; - delete ctx.ldr_color_texture_b; - ctx.ldr_color_texture_b = nullptr; + ctx.ldr_framebuffer_b.reset(); + ctx.ldr_color_texture_b.reset(); // Delete shadow map framebuffer and its attachments - delete ctx.shadow_map_framebuffer; - ctx.shadow_map_framebuffer = nullptr; - delete ctx.shadow_map_depth_texture; - ctx.shadow_map_depth_texture = nullptr; + ctx.shadow_map_framebuffer.reset(); + ctx.shadow_map_depth_texture.reset(); debug::log::trace("Destroyed framebuffers"); } @@ -189,13 +180,11 @@ void save_screenshot(::game& ctx) [frame, path = std::move(screenshot_filepath_string)] { stbi_flip_vertically_on_write(1); - stbi_write_png(path.c_str(), frame->get_width(), frame->get_height(), frame->get_channel_count(), frame->data(), frame->get_width() * frame->get_channel_count()); + stbi_write_png(path.c_str(), frame->width(), frame->height(), frame->channel_count(), frame->data(), frame->width() * frame->channel_count()); debug::log::debug("Saved screenshot to \"{}\"", path); } ).detach(); - - } void select_anti_aliasing_method(::game& ctx, render::anti_aliasing_method method) @@ -228,12 +217,12 @@ void reroute_framebuffers(::game& ctx) { if (ctx.resample_pass->is_enabled()) { - ctx.common_final_pass->set_framebuffer(ctx.ldr_framebuffer_a); - ctx.fxaa_pass->set_framebuffer(ctx.ldr_framebuffer_b); + ctx.common_final_pass->set_framebuffer(ctx.ldr_framebuffer_a.get()); + ctx.fxaa_pass->set_framebuffer(ctx.ldr_framebuffer_b.get()); } else { - ctx.common_final_pass->set_framebuffer(ctx.ldr_framebuffer_a); + ctx.common_final_pass->set_framebuffer(ctx.ldr_framebuffer_a.get()); ctx.fxaa_pass->set_framebuffer(&ctx.window->get_rasterizer()->get_default_framebuffer()); } } @@ -241,7 +230,7 @@ void reroute_framebuffers(::game& ctx) { if (ctx.resample_pass->is_enabled()) { - ctx.common_final_pass->set_framebuffer(ctx.ldr_framebuffer_b); + ctx.common_final_pass->set_framebuffer(ctx.ldr_framebuffer_b.get()); } else { diff --git a/src/game/load.cpp b/src/game/load.cpp index db9ebc9..fef80be 100644 --- a/src/game/load.cpp +++ b/src/game/load.cpp @@ -19,26 +19,26 @@ #include "game/load.hpp" #include -#include +#include #include namespace load { void colony(::game& ctx, const std::filesystem::path& path) { - const std::string path_string = path.string(); + // const std::string path_string = path.string(); - debug::log::trace("Loading colony from \"{}\"...", path_string); - try - { - json* data = ctx.resource_manager->load(path); + // debug::log::trace("Loading colony from \"{}\"...", path_string); + // try + // { + // json* data = ctx.resource_manager->load(path); - debug::log::trace("Loaded colony from \"{}\"", path_string); - } - catch (...) - { - debug::log::error("Failed to load colony from \"{}\"", path_string); - } + // debug::log::trace("Loaded colony from \"{}\"", path_string); + // } + // catch (...) + // { + // debug::log::error("Failed to load colony from \"{}\"", path_string); + // } } } // namespace load diff --git a/src/game/loaders/control-profile-loader.cpp b/src/game/loaders/control-profile-loader.cpp deleted file mode 100644 index 06a7560..0000000 --- a/src/game/loaders/control-profile-loader.cpp +++ /dev/null @@ -1,41 +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 . - */ - -#include -#include -#include -#include "game/control-profile.hpp" - -template <> -::control_profile* resource_loader<::control_profile>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - ::control_profile* profile = new ::control_profile(); - - deserialize_context ctx(file); - deserializer<::control_profile>().deserialize(*profile, ctx); - - return profile; -} - -template <> -void resource_loader<::control_profile>::save(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path, const ::control_profile* profile) -{ - serialize_context ctx(file); - serializer<::control_profile>().serialize(*profile, ctx); -} diff --git a/src/game/loaders/entity-archetype-loader.cpp b/src/game/loaders/entity-archetype-loader.cpp index be5c9ff..8486a63 100644 --- a/src/game/loaders/entity-archetype-loader.cpp +++ b/src/game/loaders/entity-archetype-loader.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "game/components/atmosphere-component.hpp" #include "game/components/behavior-component.hpp" #include "game/components/collision-component.hpp" @@ -32,7 +33,7 @@ #include "game/components/celestial-body-component.hpp" #include #include -#include +#include #include static bool load_component_atmosphere(entity::archetype& archetype, const json& element) @@ -155,7 +156,7 @@ static bool load_component_collision(entity::archetype& archetype, resource_mana if (element.contains("file")) { - component.mesh = resource_manager.load(element["file"].get()); + //component.mesh = resource_manager.load(element["file"].get()); } archetype.stamps.push_back @@ -307,24 +308,18 @@ static bool load_component(entity::archetype& archetype, resource_manager& resou } template <> -entity::archetype* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +std::unique_ptr resource_loader::load(::resource_manager& resource_manager, deserialize_context& ctx) { + // Load JSON data + auto json_data = resource_loader::load(resource_manager, ctx); + // Allocate archetype - entity::archetype* archetype = new entity::archetype(); - - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - std::string buffer; - buffer.resize(size); - PHYSFS_readBytes(file, &buffer[0], size); + std::unique_ptr archetype = std::make_unique(); - // Parse JSON data from file buffer - json data = nlohmann::json::parse(buffer, nullptr, true, true); - // Load components from table rows - for (json::const_iterator element = data.cbegin(); element != data.cend(); ++element) + for (json::const_iterator element = json_data->cbegin(); element != json_data->cend(); ++element) { - if (!load_component(*archetype, *resource_manager, element)) + if (!load_component(*archetype, resource_manager, element)) { throw std::runtime_error("Failed to load component \"" + element.key() + "\""); } diff --git a/src/game/menu.cpp b/src/game/menu.cpp index 5a32c33..d19c647 100644 --- a/src/game/menu.cpp +++ b/src/game/menu.cpp @@ -47,12 +47,12 @@ void update_text_font(::game& ctx) { for (auto [name, value]: ctx.menu_item_texts) { - name->set_material(&ctx.menu_font_material); + name->set_material(ctx.menu_font_material); name->set_font(&ctx.menu_font); if (value) { - value->set_material(&ctx.menu_font_material); + value->set_material(ctx.menu_font_material); value->set_font(&ctx.menu_font); } } @@ -197,12 +197,6 @@ void remove_text_from_ui(::game& ctx) void delete_text(::game& ctx) { - for (auto [name, value]: ctx.menu_item_texts) - { - delete name; - if (value) - delete value; - } ctx.menu_item_texts.clear(); } diff --git a/src/game/settings.hpp b/src/game/settings.hpp index 4b67b89..7c31eda 100644 --- a/src/game/settings.hpp +++ b/src/game/settings.hpp @@ -36,7 +36,7 @@ * @return `true` if the setting was read, `false` if the setting was written. */ template -bool read_or_write_setting(::game& ctx, std::uint32_t key, T& value) +bool read_or_write_setting(::game& ctx, hash::fnv1a32_t key, T& value) { if (auto i = ctx.settings->find(key); i != ctx.settings->end()) { @@ -46,14 +46,14 @@ bool read_or_write_setting(::game& ctx, std::uint32_t key, T& value) } catch (const std::bad_any_cast&) { - debug::log::error("Setting type mismatch ({:x}={})", key, value); + debug::log::error("Setting type mismatch ({:x}={})", key.value, value); i->second = value; return false; } } else { - debug::log::trace("Setting key not found ({:x}={})", key, value); + debug::log::trace("Setting key not found ({:x}={})", key.value, value); (*ctx.settings)[key] = value; return false; } diff --git a/src/game/spawn.cpp b/src/game/spawn.cpp index bd5ec17..5cacdec 100644 --- a/src/game/spawn.cpp +++ b/src/game/spawn.cpp @@ -22,7 +22,7 @@ #include "game/components/model-component.hpp" -entity::id spawn_ant_egg(::game& ctx, const ant::genome& genome, bool fertilized, const float3& position) +entity::id spawn_ant_egg(::game& ctx, const ant_genome& genome, bool fertilized, const float3& position) { // Create entity entity::id egg_eid = ctx.entity_registry->create(); @@ -45,7 +45,7 @@ entity::id spawn_ant_egg(::game& ctx, const ant::genome& genome, bool fertilized return egg_eid; } -entity::id spawn_ant_larva(::game& ctx, const ant::genome& genome, const float3& position) +entity::id spawn_ant_larva(::game& ctx, const ant_genome& genome, const float3& position) { // Create entity entity::id larva_eid = ctx.entity_registry->create(); @@ -68,7 +68,7 @@ entity::id spawn_ant_larva(::game& ctx, const ant::genome& genome, const float3& return larva_eid; } -entity::id spawn_worker_ant(::game& ctx, const ant::genome& genome, const float3& position) +entity::id spawn_worker_ant(::game& ctx, const ant_genome& genome, const float3& position) { return entt::null; } diff --git a/src/game/spawn.hpp b/src/game/spawn.hpp index 6988154..1950105 100644 --- a/src/game/spawn.hpp +++ b/src/game/spawn.hpp @@ -21,7 +21,7 @@ #define ANTKEEPER_GAME_SPAWN_HPP #include "game/game.hpp" -#include "game/ant/genome.hpp" +#include "game/ant/ant-genome.hpp" #include @@ -35,7 +35,7 @@ * * @return Entity ID of the spawned ant egg. */ -entity::id spawn_ant_egg(::game& ctx, const ant::genome& genome, bool fertilized, const float3& position); +entity::id spawn_ant_egg(::game& ctx, const ant_genome& genome, bool fertilized, const float3& position); /** * Spawns an ant larva. @@ -46,7 +46,7 @@ entity::id spawn_ant_egg(::game& ctx, const ant::genome& genome, bool fertilized * * @return Entity ID of the spawned ant larva. */ -entity::id spawn_ant_larva(::game& ctx, const ant::genome& genome, const float3& position); +entity::id spawn_ant_larva(::game& ctx, const ant_genome& genome, const float3& position); /** * Spawns a worker ant. @@ -57,7 +57,7 @@ entity::id spawn_ant_larva(::game& ctx, const ant::genome& genome, const float3& * * @return Entity ID of the spawned worker ant. */ -entity::id spawn_worker_ant(::game& ctx, const ant::genome& genome, const float3& position); +entity::id spawn_worker_ant(::game& ctx, const ant_genome& genome, const float3& position); #endif // ANTKEEPER_GAME_SPAWN_HPP diff --git a/src/game/states/collection-menu-state.cpp b/src/game/states/collection-menu-state.cpp index 6d202fb..f9cd094 100644 --- a/src/game/states/collection-menu-state.cpp +++ b/src/game/states/collection-menu-state.cpp @@ -41,22 +41,24 @@ collection_menu_state::collection_menu_state(::game& ctx): ctx.ui_clear_pass->set_cleared_buffers(true, true, false); // Construct box material - box_material.set_blend_mode(render::blend_mode::translucent); - box_material.set_shader_program(ctx.resource_manager->load("ui-element-untextured.glsl")); - box_material.add_property("tint")->set_value(float4{0.5f, 0.5f, 0.5f, 1}); - box_material.update_tweens(); + box_material = std::make_shared(); + box_material->set_blend_mode(render::material_blend_mode::translucent); + box_material->set_shader_template(ctx.resource_manager->load("ui-element-untextured.glsl")); + box_material->set_variable("tint", std::make_shared(1, float4{0.5f, 0.5f, 0.5f, 1})); + //box_material.update_tweens(); // Construct box billboard - box_billboard.set_material(&box_material); + box_billboard.set_material(box_material); // Construct selection material - selection_material.set_blend_mode(render::blend_mode::translucent); - selection_material.set_shader_program(ctx.resource_manager->load("ui-element-untextured.glsl")); - selection_material.add_property("tint")->set_value(float4{1, 1, 1, 1}); - selection_material.update_tweens(); + selection_material = std::make_shared(); + selection_material->set_blend_mode(render::material_blend_mode::translucent); + selection_material->set_shader_template(ctx.resource_manager->load("ui-element-untextured.glsl")); + box_material->set_variable("tint", std::make_shared(1, float4{1, 1, 1, 1})); + //selection_material.update_tweens(); // Construct selection billboard - selection_billboard.set_material(&selection_material); + selection_billboard.set_material(selection_material); // Add box and selection billboard to UI scene ctx.ui_scene->add_object(&box_billboard); diff --git a/src/game/states/collection-menu-state.hpp b/src/game/states/collection-menu-state.hpp index 1248f67..dafdd8b 100644 --- a/src/game/states/collection-menu-state.hpp +++ b/src/game/states/collection-menu-state.hpp @@ -38,11 +38,11 @@ public: private: void resize_box(); - render::material selection_material; + std::shared_ptr selection_material; scene::billboard selection_billboard; animation selection_snap_animation; - render::material box_material; + std::shared_ptr box_material; scene::billboard box_billboard; std::shared_ptr mouse_moved_subscription; diff --git a/src/game/states/controls-menu-state.cpp b/src/game/states/controls-menu-state.cpp index 0d092d9..195a181 100644 --- a/src/game/states/controls-menu-state.cpp +++ b/src/game/states/controls-menu-state.cpp @@ -36,19 +36,19 @@ controls_menu_state::controls_menu_state(::game& ctx): debug::log::trace("Entering controls menu state..."); // Construct menu item texts - scene::text* keyboard_text = new scene::text(); - scene::text* gamepad_text = new scene::text(); - scene::text* back_text = new scene::text(); + keyboard_text = std::make_unique(); + gamepad_text = std::make_unique(); + back_text = std::make_unique(); // Build list of menu item texts - ctx.menu_item_texts.push_back({keyboard_text, nullptr}); - ctx.menu_item_texts.push_back({gamepad_text, nullptr}); - ctx.menu_item_texts.push_back({back_text, nullptr}); + ctx.menu_item_texts.push_back({keyboard_text.get(), nullptr}); + ctx.menu_item_texts.push_back({gamepad_text.get(), nullptr}); + ctx.menu_item_texts.push_back({back_text.get(), nullptr}); // Set content of menu item texts - keyboard_text->set_content(get_string(ctx, "controls_menu_keyboard"_fnv1a32)); - gamepad_text->set_content(get_string(ctx, "controls_menu_gamepad"_fnv1a32)); - back_text->set_content(get_string(ctx, "back"_fnv1a32)); + keyboard_text->set_content(get_string(ctx, "controls_menu_keyboard")); + gamepad_text->set_content(get_string(ctx, "controls_menu_gamepad")); + back_text->set_content(get_string(ctx, "back")); // Init menu item index ::menu::init_menu_item_index(ctx, "controls"); diff --git a/src/game/states/controls-menu-state.hpp b/src/game/states/controls-menu-state.hpp index 5bfd87d..fc4b002 100644 --- a/src/game/states/controls-menu-state.hpp +++ b/src/game/states/controls-menu-state.hpp @@ -21,13 +21,19 @@ #define ANTKEEPER_CONTROLS_MENU_STATE_HPP #include "game/states/game-state.hpp" - +#include +#include class controls_menu_state: public game_state { public: controls_menu_state(::game& ctx); virtual ~controls_menu_state(); + +private: + std::unique_ptr keyboard_text; + std::unique_ptr gamepad_text; + std::unique_ptr back_text; }; #endif // ANTKEEPER_CONTROLS_MENU_STATE_HPP diff --git a/src/game/states/credits-state.cpp b/src/game/states/credits-state.cpp index 2a9a7c5..7d3b2de 100644 --- a/src/game/states/credits-state.cpp +++ b/src/game/states/credits-state.cpp @@ -29,7 +29,6 @@ #include #include -using namespace hash::literals; using namespace math::glsl; credits_state::credits_state(::game& ctx): @@ -41,10 +40,10 @@ credits_state::credits_state(::game& ctx): const vec2 viewport_center = viewport_size * 0.5f; // Construct credits text - credits_text.set_material(&ctx.menu_font_material); + credits_text.set_material(ctx.menu_font_material); credits_text.set_font(&ctx.menu_font); credits_text.set_color({1.0f, 1.0f, 1.0f, 0.0f}); - credits_text.set_content(get_string(ctx, "credits"_fnv1a32)); + credits_text.set_content(get_string(ctx, "credits")); // Align credits text const auto& credits_aabb = static_cast&>(credits_text.get_local_bounds()); diff --git a/src/game/states/extras-menu-state.cpp b/src/game/states/extras-menu-state.cpp index 15be8ad..370d175 100644 --- a/src/game/states/extras-menu-state.cpp +++ b/src/game/states/extras-menu-state.cpp @@ -36,16 +36,16 @@ extras_menu_state::extras_menu_state(::game& ctx): debug::log::trace("Entering extras menu state..."); // Construct menu item texts - scene::text* credits_text = new scene::text(); - scene::text* back_text = new scene::text(); + credits_text = std::make_unique(); + back_text = std::make_unique(); // Build list of menu item texts - ctx.menu_item_texts.push_back({credits_text, nullptr}); - ctx.menu_item_texts.push_back({back_text, nullptr}); + ctx.menu_item_texts.push_back({credits_text.get(), nullptr}); + ctx.menu_item_texts.push_back({back_text.get(), nullptr}); // Set content of menu item texts - credits_text->set_content(get_string(ctx, "extras_menu_credits"_fnv1a32)); - back_text->set_content(get_string(ctx, "back"_fnv1a32)); + credits_text->set_content(get_string(ctx, "extras_menu_credits")); + back_text->set_content(get_string(ctx, "back")); // Init menu item index ::menu::init_menu_item_index(ctx, "extras"); diff --git a/src/game/states/extras-menu-state.hpp b/src/game/states/extras-menu-state.hpp index d1c93f4..1673026 100644 --- a/src/game/states/extras-menu-state.hpp +++ b/src/game/states/extras-menu-state.hpp @@ -21,13 +21,18 @@ #define ANTKEEPER_EXTRAS_MENU_STATE_HPP #include "game/states/game-state.hpp" - +#include +#include class extras_menu_state: public game_state { public: extras_menu_state(::game& ctx); virtual ~extras_menu_state(); + +private: + std::unique_ptr credits_text; + std::unique_ptr back_text; }; #endif // ANTKEEPER_EXTRAS_MENU_STATE_HPP diff --git a/src/game/states/gamepad-config-menu-state.cpp b/src/game/states/gamepad-config-menu-state.cpp index 5851ca8..68b4efe 100644 --- a/src/game/states/gamepad-config-menu-state.cpp +++ b/src/game/states/gamepad-config-menu-state.cpp @@ -39,22 +39,22 @@ gamepad_config_menu_state::gamepad_config_menu_state(::game& ctx): debug::log::trace("Entering gamepad config menu state..."); // Add control menu items - add_control_item(ctx.movement_action_map, ctx.move_forward_action, "control_move_forward"_fnv1a32); - add_control_item(ctx.movement_action_map, ctx.move_back_action, "control_move_back"_fnv1a32); - add_control_item(ctx.movement_action_map, ctx.move_left_action, "control_move_left"_fnv1a32); - add_control_item(ctx.movement_action_map, ctx.move_right_action, "control_move_right"_fnv1a32); - add_control_item(ctx.movement_action_map, ctx.move_up_action, "control_move_up"_fnv1a32); - add_control_item(ctx.movement_action_map, ctx.move_down_action, "control_move_down"_fnv1a32); - add_control_item(ctx.movement_action_map, ctx.pause_action, "control_pause"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_forward_action, "control_move_forward"); + add_control_item(ctx.movement_action_map, ctx.move_back_action, "control_move_back"); + add_control_item(ctx.movement_action_map, ctx.move_left_action, "control_move_left"); + add_control_item(ctx.movement_action_map, ctx.move_right_action, "control_move_right"); + add_control_item(ctx.movement_action_map, ctx.move_up_action, "control_move_up"); + add_control_item(ctx.movement_action_map, ctx.move_down_action, "control_move_down"); + add_control_item(ctx.movement_action_map, ctx.pause_action, "control_pause"); // Construct menu item texts - scene::text* back_text = new scene::text(); + back_text = std::make_unique(); // Build list of menu item texts - ctx.menu_item_texts.push_back({back_text, nullptr}); + ctx.menu_item_texts.push_back({back_text.get(), nullptr}); // Set content of menu item texts - back_text->set_content(get_string(ctx, "back"_fnv1a32)); + back_text->set_content(get_string(ctx, "back")); // Init menu item index ::menu::init_menu_item_index(ctx, "gamepad_config"); @@ -128,8 +128,8 @@ gamepad_config_menu_state::~gamepad_config_menu_state() ::update_control_profile(ctx, *ctx.control_profile); // Save control profile - ctx.resource_manager->set_write_dir(ctx.controls_path); - ctx.resource_manager->save(ctx.control_profile, ctx.control_profile_filename); + ctx.resource_manager->set_write_path(ctx.controls_path); + ctx.resource_manager->save(*ctx.control_profile, ctx.control_profile_filename); } debug::log::trace("Exited gamepad config menu state"); @@ -148,59 +148,59 @@ std::string gamepad_config_menu_state::get_mapping_string(const input::action_ma case input::gamepad_axis::left_stick_x: if (gamepad_axis_mapping.direction) { - mapping_string = get_string(ctx, "gamepad_left_stick_left"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_left_stick_left"); } else { - mapping_string = get_string(ctx, "gamepad_left_stick_right"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_left_stick_right"); } break; case input::gamepad_axis::left_stick_y: if (gamepad_axis_mapping.direction) { - mapping_string = get_string(ctx, "gamepad_left_stick_up"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_left_stick_up"); } else { - mapping_string = get_string(ctx, "gamepad_left_stick_down"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_left_stick_down"); } break; case input::gamepad_axis::right_stick_x: if (gamepad_axis_mapping.direction) { - mapping_string = get_string(ctx, "gamepad_right_stick_left"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_right_stick_left"); } else { - mapping_string = get_string(ctx, "gamepad_right_stick_right"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_right_stick_right"); } break; case input::gamepad_axis::right_stick_y: if (gamepad_axis_mapping.direction) { - mapping_string = get_string(ctx, "gamepad_right_stick_up"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_right_stick_up"); } else { - mapping_string = get_string(ctx, "gamepad_right_stick_down"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_right_stick_down"); } break; case input::gamepad_axis::left_trigger: - mapping_string = get_string(ctx, "gamepad_left_trigger"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_left_trigger"); break; case input::gamepad_axis::right_trigger: - mapping_string = get_string(ctx, "gamepad_right_trigger"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_right_trigger"); break; default: { const char sign = (gamepad_axis_mapping.direction) ? '-' : '+'; - const std::string format_string = get_string(ctx, "gamepad_axis_n_format"_fnv1a32); + const std::string format_string = get_string(ctx, "gamepad_axis_n_format"); mapping_string = std::vformat(format_string, std::make_format_args(std::to_underlying(gamepad_axis_mapping.axis), sign)); break; } @@ -212,68 +212,68 @@ std::string gamepad_config_menu_state::get_mapping_string(const input::action_ma switch (gamepad_button_mapping.button) { case input::gamepad_button::a: - mapping_string = get_string(ctx, "gamepad_button_a"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_a"); break; case input::gamepad_button::b: - mapping_string = get_string(ctx, "gamepad_button_b"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_b"); break; case input::gamepad_button::x: - mapping_string = get_string(ctx, "gamepad_button_x"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_x"); break; case input::gamepad_button::y: - mapping_string = get_string(ctx, "gamepad_button_y"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_y"); break; case input::gamepad_button::back: - mapping_string = get_string(ctx, "gamepad_button_back"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_back"); break; case input::gamepad_button::guide: - mapping_string = get_string(ctx, "gamepad_button_guide"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_guide"); break; case input::gamepad_button::start: - mapping_string = get_string(ctx, "gamepad_button_start"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_start"); break; case input::gamepad_button::left_stick: - mapping_string = get_string(ctx, "gamepad_button_left_stick"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_left_stick"); break; case input::gamepad_button::right_stick: - mapping_string = get_string(ctx, "gamepad_button_right_stick"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_right_stick"); break; case input::gamepad_button::left_shoulder: - mapping_string = get_string(ctx, "gamepad_button_left_shoulder"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_left_shoulder"); break; case input::gamepad_button::right_shoulder: - mapping_string = get_string(ctx, "gamepad_button_right_shoulder"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_right_shoulder"); break; case input::gamepad_button::dpad_up: - mapping_string = get_string(ctx, "gamepad_button_dpad_up"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_dpad_up"); break; case input::gamepad_button::dpad_down: - mapping_string = get_string(ctx, "gamepad_button_dpad_down"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_dpad_down"); break; case input::gamepad_button::dpad_left: - mapping_string = get_string(ctx, "gamepad_button_dpad_left"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_dpad_left"); break; case input::gamepad_button::dpad_right: - mapping_string = get_string(ctx, "gamepad_button_dpad_right"_fnv1a32); + mapping_string = get_string(ctx, "gamepad_button_dpad_right"); break; default: { - const std::string format_string = get_string(ctx, "gamepad_button_n_format"_fnv1a32); + const std::string format_string = get_string(ctx, "gamepad_button_n_format"); mapping_string = std::vformat(format_string, std::make_format_args(std::to_underlying(gamepad_button_mapping.button))); break; } @@ -281,27 +281,27 @@ std::string gamepad_config_menu_state::get_mapping_string(const input::action_ma } else { - mapping_string = get_string(ctx, "control_unmapped"_fnv1a32); + mapping_string = get_string(ctx, "control_unmapped"); } return mapping_string; } -void gamepad_config_menu_state::add_control_item(input::action_map& action_map, input::action& control, std::uint32_t control_name_hash) +void gamepad_config_menu_state::add_control_item(input::action_map& action_map, input::action& control, hash::fnv1a32_t control_name_hash) { // Construct texts - scene::text* name_text = new scene::text(); - scene::text* value_text = new scene::text(); + auto name_text = std::make_unique(); + auto value_text = std::make_unique(); // Add texts to list of menu item texts - ctx.menu_item_texts.push_back({name_text, value_text}); + ctx.menu_item_texts.push_back({name_text.get(), value_text.get()}); // Set control name and mapping texts name_text->set_content(get_string(ctx, control_name_hash)); value_text->set_content(get_mapping_string(action_map, control)); // Callback invoked when an input has been mapped to the control - auto input_mapped_callback = [this, &ctx = this->ctx, action_map = &action_map, control = &control, value_text](const auto& event) + auto input_mapped_callback = [this, &ctx = this->ctx, action_map = &action_map, control = &control, value_text = value_text.get()](const auto& event) { if (event.mapping.get_mapping_type() != input::mapping_type::key) { @@ -331,10 +331,10 @@ void gamepad_config_menu_state::add_control_item(input::action_map& action_map, }; // Callback invoked when the control menu item has been selected - auto select_callback = [this, &ctx = this->ctx, action_map = &action_map, control = &control, value_text, input_mapped_callback]() + auto select_callback = [this, &ctx = this->ctx, action_map = &action_map, control = &control, value_text = value_text.get(), input_mapped_callback]() { // Set control mapping text to "..." - value_text->set_content(get_string(ctx, "control_mapping"_fnv1a32)); + value_text->set_content(get_string(ctx, "control_mapping")); ::menu::align_text(ctx); ::menu::update_text_tweens(ctx); @@ -363,6 +363,9 @@ void gamepad_config_menu_state::add_control_item(input::action_map& action_map, ); }; + control_item_texts.emplace_back(std::move(name_text)); + control_item_texts.emplace_back(std::move(value_text)); + // Register menu item callbacks ctx.menu_select_callbacks.push_back(select_callback); ctx.menu_left_callbacks.push_back(nullptr); diff --git a/src/game/states/gamepad-config-menu-state.hpp b/src/game/states/gamepad-config-menu-state.hpp index 32f880d..37e3e81 100644 --- a/src/game/states/gamepad-config-menu-state.hpp +++ b/src/game/states/gamepad-config-menu-state.hpp @@ -24,7 +24,9 @@ #include #include #include +#include #include +#include #include @@ -36,12 +38,15 @@ public: private: std::string get_mapping_string(const input::action_map& action_map, const input::action& control); - void add_control_item(input::action_map& action_map, input::action& control, std::uint32_t control_name_hash); + void add_control_item(input::action_map& action_map, input::action& control, hash::fnv1a32_t control_name_hash); std::shared_ptr gamepad_axis_mapped_subscription; std::shared_ptr gamepad_button_mapped_subscription; std::shared_ptr key_mapped_subscription; + std::unique_ptr back_text; + std::vector> control_item_texts; + bool action_remapped; }; diff --git a/src/game/states/graphics-menu-state.cpp b/src/game/states/graphics-menu-state.cpp index 2140a20..9ee9b33 100644 --- a/src/game/states/graphics-menu-state.cpp +++ b/src/game/states/graphics-menu-state.cpp @@ -38,37 +38,37 @@ graphics_menu_state::graphics_menu_state(::game& ctx): debug::log::trace("Entering graphics menu state..."); // Construct menu item texts - scene::text* fullscreen_name_text = new scene::text(); - scene::text* fullscreen_value_text = new scene::text(); - scene::text* resolution_name_text = new scene::text(); - scene::text* resolution_value_text = new scene::text(); - scene::text* v_sync_name_text = new scene::text(); - scene::text* v_sync_value_text = new scene::text(); - scene::text* aa_method_name_text = new scene::text(); - scene::text* aa_method_value_text = new scene::text(); - scene::text* font_scale_name_text = new scene::text(); - scene::text* font_scale_value_text = new scene::text(); - scene::text* dyslexia_font_name_text = new scene::text(); - scene::text* dyslexia_font_value_text = new scene::text(); - scene::text* back_text = new scene::text(); + fullscreen_name_text = std::make_unique(); + fullscreen_value_text = std::make_unique(); + resolution_name_text = std::make_unique(); + resolution_value_text = std::make_unique(); + v_sync_name_text = std::make_unique(); + v_sync_value_text = std::make_unique(); + aa_method_name_text = std::make_unique(); + aa_method_value_text = std::make_unique(); + font_scale_name_text = std::make_unique(); + font_scale_value_text = std::make_unique(); + dyslexia_font_name_text = std::make_unique(); + dyslexia_font_value_text = std::make_unique(); + back_text = std::make_unique(); // Build list of menu item texts - ctx.menu_item_texts.push_back({fullscreen_name_text, fullscreen_value_text}); - ctx.menu_item_texts.push_back({resolution_name_text, resolution_value_text}); - ctx.menu_item_texts.push_back({v_sync_name_text, v_sync_value_text}); - ctx.menu_item_texts.push_back({aa_method_name_text, aa_method_value_text}); - ctx.menu_item_texts.push_back({font_scale_name_text, font_scale_value_text}); - ctx.menu_item_texts.push_back({dyslexia_font_name_text, dyslexia_font_value_text}); - ctx.menu_item_texts.push_back({back_text, nullptr}); + ctx.menu_item_texts.push_back({fullscreen_name_text.get(), fullscreen_value_text.get()}); + ctx.menu_item_texts.push_back({resolution_name_text.get(), resolution_value_text.get()}); + ctx.menu_item_texts.push_back({v_sync_name_text.get(), v_sync_value_text.get()}); + ctx.menu_item_texts.push_back({aa_method_name_text.get(), aa_method_value_text.get()}); + ctx.menu_item_texts.push_back({font_scale_name_text.get(), font_scale_value_text.get()}); + ctx.menu_item_texts.push_back({dyslexia_font_name_text.get(), dyslexia_font_value_text.get()}); + ctx.menu_item_texts.push_back({back_text.get(), nullptr}); // Set content of menu item texts - fullscreen_name_text->set_content(get_string(ctx, "graphics_menu_fullscreen"_fnv1a32)); - resolution_name_text->set_content(get_string(ctx, "graphics_menu_resolution"_fnv1a32)); - v_sync_name_text->set_content(get_string(ctx, "graphics_menu_v_sync"_fnv1a32)); - aa_method_name_text->set_content(get_string(ctx, "graphics_menu_aa_method"_fnv1a32)); - font_scale_name_text->set_content(get_string(ctx, "graphics_menu_font_scale"_fnv1a32)); - dyslexia_font_name_text->set_content(get_string(ctx, "graphics_menu_dyslexia_font"_fnv1a32)); - back_text->set_content(get_string(ctx, "back"_fnv1a32)); + fullscreen_name_text->set_content(get_string(ctx, "graphics_menu_fullscreen")); + resolution_name_text->set_content(get_string(ctx, "graphics_menu_resolution")); + v_sync_name_text->set_content(get_string(ctx, "graphics_menu_v_sync")); + aa_method_name_text->set_content(get_string(ctx, "graphics_menu_aa_method")); + font_scale_name_text->set_content(get_string(ctx, "graphics_menu_font_scale")); + dyslexia_font_name_text->set_content(get_string(ctx, "graphics_menu_dyslexia_font")); + back_text->set_content(get_string(ctx, "back")); update_value_text_content(); // Init menu item index @@ -93,7 +93,7 @@ graphics_menu_state::graphics_menu_state(::game& ctx): ::menu::update_text_tweens(ctx); // Update fullscreen settings - (*ctx.settings)["fullscreen"_fnv1a32] = fullscreen; + (*ctx.settings)["fullscreen"] = fullscreen; }; auto increase_resolution_callback = [this, &ctx]() @@ -109,7 +109,7 @@ graphics_menu_state::graphics_menu_state(::game& ctx): ctx.render_scale = 2.0f; // Update render scale setting - (*ctx.settings)["render_scale"_fnv1a32] = ctx.render_scale; + (*ctx.settings)["render_scale"] = ctx.render_scale; // Resize framebuffers ::graphics::change_render_resolution(ctx, ctx.render_scale); @@ -133,7 +133,7 @@ graphics_menu_state::graphics_menu_state(::game& ctx): ctx.render_scale = 0.25f; // Update render scale setting - (*ctx.settings)["render_scale"_fnv1a32] = ctx.render_scale; + (*ctx.settings)["render_scale"] = ctx.render_scale; // Resize framebuffers ::graphics::change_render_resolution(ctx, ctx.render_scale); @@ -149,7 +149,7 @@ graphics_menu_state::graphics_menu_state(::game& ctx): bool v_sync = !ctx.window->get_v_sync(); // Update v-sync setting - (*ctx.settings)["v_sync"_fnv1a32] = v_sync; + (*ctx.settings)["v_sync"] = v_sync; ctx.window->set_v_sync(v_sync); @@ -172,7 +172,7 @@ graphics_menu_state::graphics_menu_state(::game& ctx): } // Update anti-aliasing method setting - (*ctx.settings)["anti_aliasing_method"_fnv1a32] = std::to_underlying(ctx.anti_aliasing_method); + (*ctx.settings)["anti_aliasing_method"] = std::to_underlying(ctx.anti_aliasing_method); ::graphics::select_anti_aliasing_method(ctx, ctx.anti_aliasing_method); @@ -199,7 +199,7 @@ graphics_menu_state::graphics_menu_state(::game& ctx): } // Update anti-aliasing method setting - (*ctx.settings)["anti_aliasing_method"_fnv1a32] = std::to_underlying(ctx.anti_aliasing_method); + (*ctx.settings)["anti_aliasing_method"] = std::to_underlying(ctx.anti_aliasing_method); ::graphics::select_anti_aliasing_method(ctx, ctx.anti_aliasing_method); @@ -225,7 +225,7 @@ graphics_menu_state::graphics_menu_state(::game& ctx): ctx.font_scale = 2.0f; // Update font scale setting - (*ctx.settings)["font_scale"_fnv1a32] = ctx.font_scale; + (*ctx.settings)["font_scale"] = ctx.font_scale; // Update value text this->update_value_text_content(); @@ -254,7 +254,7 @@ graphics_menu_state::graphics_menu_state(::game& ctx): ctx.font_scale = 0.1f; // Update font scale setting - (*ctx.settings)["font_scale"_fnv1a32] = ctx.font_scale; + (*ctx.settings)["font_scale"] = ctx.font_scale; // Update value text this->update_value_text_content(); @@ -278,7 +278,7 @@ graphics_menu_state::graphics_menu_state(::game& ctx): this->update_value_text_content(); // Save dyslexia font setting - (*ctx.settings)["dyslexia_font"_fnv1a32] = ctx.dyslexia_font; + (*ctx.settings)["dyslexia_font"] = ctx.dyslexia_font; // Reload fonts debug::log::trace("Reloading fonts..."); @@ -375,8 +375,8 @@ void graphics_menu_state::update_value_text_content() const float font_scale = ctx.font_scale; const bool dyslexia_font = ctx.dyslexia_font; - const std::string string_on = get_string(ctx, "on"_fnv1a32); - const std::string string_off = get_string(ctx, "off"_fnv1a32); + const std::string string_on = get_string(ctx, "on"); + const std::string string_off = get_string(ctx, "off"); /* const std::string string_quality[4] = @@ -390,8 +390,8 @@ void graphics_menu_state::update_value_text_content() const std::string string_aa_methods[2] = { - get_string(ctx, "graphics_menu_aa_method_none"_fnv1a32), - get_string(ctx, "graphics_menu_aa_method_fxaa"_fnv1a32) + get_string(ctx, "graphics_menu_aa_method_none"), + get_string(ctx, "graphics_menu_aa_method_fxaa") }; std::get<1>(ctx.menu_item_texts[0])->set_content((fullscreen) ? string_on : string_off); diff --git a/src/game/states/graphics-menu-state.hpp b/src/game/states/graphics-menu-state.hpp index 16c35c8..b8ee3c8 100644 --- a/src/game/states/graphics-menu-state.hpp +++ b/src/game/states/graphics-menu-state.hpp @@ -21,7 +21,8 @@ #define ANTKEEPER_GRAPHICS_MENU_STATE_HPP #include "game/states/game-state.hpp" - +#include +#include class graphics_menu_state: public game_state { @@ -31,6 +32,20 @@ public: private: void update_value_text_content(); + + std::unique_ptr fullscreen_name_text; + std::unique_ptr fullscreen_value_text; + std::unique_ptr resolution_name_text; + std::unique_ptr resolution_value_text; + std::unique_ptr v_sync_name_text; + std::unique_ptr v_sync_value_text; + std::unique_ptr aa_method_name_text; + std::unique_ptr aa_method_value_text; + std::unique_ptr font_scale_name_text; + std::unique_ptr font_scale_value_text; + std::unique_ptr dyslexia_font_name_text; + std::unique_ptr dyslexia_font_value_text; + std::unique_ptr back_text; }; #endif // ANTKEEPER_GRAPHICS_MENU_STATE_HPP diff --git a/src/game/states/keyboard-config-menu-state.cpp b/src/game/states/keyboard-config-menu-state.cpp index 0d4a89f..8d5efc7 100644 --- a/src/game/states/keyboard-config-menu-state.cpp +++ b/src/game/states/keyboard-config-menu-state.cpp @@ -39,22 +39,22 @@ keyboard_config_menu_state::keyboard_config_menu_state(::game& ctx): debug::log::trace("Entering keyboard config menu state..."); // Add control menu items - add_control_item(ctx.movement_action_map, ctx.move_forward_action, "control_move_forward"_fnv1a32); - add_control_item(ctx.movement_action_map, ctx.move_back_action, "control_move_back"_fnv1a32); - add_control_item(ctx.movement_action_map, ctx.move_left_action, "control_move_left"_fnv1a32); - add_control_item(ctx.movement_action_map, ctx.move_right_action, "control_move_right"_fnv1a32); - add_control_item(ctx.movement_action_map, ctx.move_up_action, "control_move_up"_fnv1a32); - add_control_item(ctx.movement_action_map, ctx.move_down_action, "control_move_down"_fnv1a32); - add_control_item(ctx.movement_action_map, ctx.pause_action, "control_pause"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_forward_action, "control_move_forward"); + add_control_item(ctx.movement_action_map, ctx.move_back_action, "control_move_back"); + add_control_item(ctx.movement_action_map, ctx.move_left_action, "control_move_left"); + add_control_item(ctx.movement_action_map, ctx.move_right_action, "control_move_right"); + add_control_item(ctx.movement_action_map, ctx.move_up_action, "control_move_up"); + add_control_item(ctx.movement_action_map, ctx.move_down_action, "control_move_down"); + add_control_item(ctx.movement_action_map, ctx.pause_action, "control_pause"); // Construct menu item texts - scene::text* back_text = new scene::text(); + back_text = std::make_unique(); // Build list of menu item texts - ctx.menu_item_texts.push_back({back_text, nullptr}); + ctx.menu_item_texts.push_back({back_text.get(), nullptr}); // Set content of menu item texts - back_text->set_content(get_string(ctx, "back"_fnv1a32)); + back_text->set_content(get_string(ctx, "back")); // Init menu item index ::menu::init_menu_item_index(ctx, "keyboard_config"); @@ -128,8 +128,8 @@ keyboard_config_menu_state::~keyboard_config_menu_state() ::update_control_profile(ctx, *ctx.control_profile); // Save control profile - ctx.resource_manager->set_write_dir(ctx.controls_path); - ctx.resource_manager->save(ctx.control_profile, ctx.control_profile_filename); + ctx.resource_manager->set_write_path(ctx.controls_path); + ctx.resource_manager->save(*ctx.control_profile, ctx.control_profile_filename); } debug::log::trace("Exited keyboard config menu state..."); @@ -147,7 +147,7 @@ std::string keyboard_config_menu_state::get_mapping_string(const input::action_m std::string scancode_string_name = std::format("scancode_{:02x}", std::to_underlying(key_mapping.scancode)); // Set mapping string to scancode string - mapping_string = get_string(ctx, hash::fnv1a32(scancode_string_name.data(), scancode_string_name.length())); + mapping_string = get_string(ctx, hash::fnv1a32(scancode_string_name)); } else if (auto mouse_button_mappings = action_map.get_mouse_button_mappings(control); !mouse_button_mappings.empty()) { @@ -155,20 +155,20 @@ std::string keyboard_config_menu_state::get_mapping_string(const input::action_m switch (mouse_button_mapping.button) { case input::mouse_button::left: - mapping_string = get_string(ctx, "mouse_button_left"_fnv1a32); + mapping_string = get_string(ctx, "mouse_button_left"); break; case input::mouse_button::middle: - mapping_string = get_string(ctx, "mouse_button_middle"_fnv1a32); + mapping_string = get_string(ctx, "mouse_button_middle"); break; case input::mouse_button::right: - mapping_string = get_string(ctx, "mouse_button_right"_fnv1a32); + mapping_string = get_string(ctx, "mouse_button_right"); break; default: { - std::string format_string = get_string(ctx, "mouse_button_n_format"_fnv1a32); + std::string format_string = get_string(ctx, "mouse_button_n_format"); mapping_string = std::vformat(format_string, std::make_format_args(std::to_underlying(mouse_button_mapping.button))); break; } @@ -182,48 +182,48 @@ std::string keyboard_config_menu_state::get_mapping_string(const input::action_m { if (!mouse_scroll_mapping.direction) { - mapping_string = get_string(ctx, "mouse_scroll_left"_fnv1a32); + mapping_string = get_string(ctx, "mouse_scroll_left"); } else { - mapping_string = get_string(ctx, "mouse_scroll_right"_fnv1a32); + mapping_string = get_string(ctx, "mouse_scroll_right"); } } else { if (!mouse_scroll_mapping.direction) { - mapping_string = get_string(ctx, "mouse_scroll_up"_fnv1a32); + mapping_string = get_string(ctx, "mouse_scroll_up"); } else { - mapping_string = get_string(ctx, "mouse_scroll_down"_fnv1a32); + mapping_string = get_string(ctx, "mouse_scroll_down"); } } } else { - mapping_string = get_string(ctx, "control_unmapped"_fnv1a32); + mapping_string = get_string(ctx, "control_unmapped"); } return mapping_string; } -void keyboard_config_menu_state::add_control_item(input::action_map& action_map, input::action& control, std::uint32_t control_name_hash) +void keyboard_config_menu_state::add_control_item(input::action_map& action_map, input::action& control, hash::fnv1a32_t control_name_hash) { // Construct texts - scene::text* name_text = new scene::text(); - scene::text* value_text = new scene::text(); + auto name_text = std::make_unique(); + auto value_text = std::make_unique(); // Add texts to list of menu item texts - ctx.menu_item_texts.push_back({name_text, value_text}); + ctx.menu_item_texts.push_back({name_text.get(), value_text.get()}); // Set control name and mapping texts name_text->set_content(get_string(ctx, control_name_hash)); value_text->set_content(get_mapping_string(action_map, control)); // Callback invoked when an input has been mapped to the control - auto input_mapped_callback = [this, &ctx = this->ctx, action_map = &action_map, control = &control, value_text](const auto& event) + auto input_mapped_callback = [this, &ctx = this->ctx, action_map = &action_map, control = &control, value_text = value_text.get()](const auto& event) { this->action_remapped = true; @@ -255,10 +255,10 @@ void keyboard_config_menu_state::add_control_item(input::action_map& action_map, }; // Callback invoked when the control menu item has been selected - auto select_callback = [this, &ctx = this->ctx, action_map = &action_map, control = &control, value_text, input_mapped_callback]() + auto select_callback = [this, &ctx = this->ctx, action_map = &action_map, control = &control, value_text = value_text.get(), input_mapped_callback]() { // Set control mapping text to "..." - value_text->set_content(get_string(ctx, "control_mapping"_fnv1a32)); + value_text->set_content(get_string(ctx, "control_mapping")); ::menu::align_text(ctx); ::menu::update_text_tweens(ctx); @@ -287,6 +287,9 @@ void keyboard_config_menu_state::add_control_item(input::action_map& action_map, ); }; + control_item_texts.emplace_back(std::move(name_text)); + control_item_texts.emplace_back(std::move(value_text)); + // Register menu item callbacks ctx.menu_select_callbacks.push_back(select_callback); ctx.menu_left_callbacks.push_back(nullptr); diff --git a/src/game/states/keyboard-config-menu-state.hpp b/src/game/states/keyboard-config-menu-state.hpp index 3cd2f3d..bcc5aab 100644 --- a/src/game/states/keyboard-config-menu-state.hpp +++ b/src/game/states/keyboard-config-menu-state.hpp @@ -24,10 +24,11 @@ #include #include #include +#include #include +#include #include - class keyboard_config_menu_state: public game_state { public: @@ -36,12 +37,15 @@ public: private: std::string get_mapping_string(const input::action_map& action_map, const input::action& control); - void add_control_item(input::action_map& action_map, input::action& control, std::uint32_t control_name_hash); + void add_control_item(input::action_map& action_map, input::action& control, hash::fnv1a32_t control_name_hash); std::shared_ptr key_mapped_subscription; std::shared_ptr mouse_button_mapped_subscription; std::shared_ptr mouse_scroll_mapped_subscription; + std::unique_ptr back_text; + std::vector> control_item_texts; + bool action_remapped; }; diff --git a/src/game/states/language-menu-state.cpp b/src/game/states/language-menu-state.cpp index fbb028c..dc69367 100644 --- a/src/game/states/language-menu-state.cpp +++ b/src/game/states/language-menu-state.cpp @@ -57,13 +57,13 @@ language_menu_state::language_menu_state(::game& ctx): } // Construct menu item texts - scene::text* language_name_text = new scene::text(); - scene::text* language_value_text = new scene::text(); - scene::text* back_text = new scene::text(); + language_name_text = std::make_unique(); + language_value_text = std::make_unique(); + back_text = std::make_unique(); // Build list of menu item texts - ctx.menu_item_texts.push_back({language_name_text, language_value_text}); - ctx.menu_item_texts.push_back({back_text, nullptr}); + ctx.menu_item_texts.push_back({language_name_text.get(), language_value_text.get()}); + ctx.menu_item_texts.push_back({back_text.get(), nullptr}); // Set content of menu item texts update_text_content(); @@ -100,7 +100,7 @@ language_menu_state::language_menu_state(::game& ctx): // Update language settings ctx.language_tag = language_tag; - (*ctx.settings)["language_tag"_fnv1a32] = ctx.language_tag; + (*ctx.settings)["language_tag"] = ctx.language_tag; // Log language change debug::log::info("Language tag: {}", ctx.language_tag); @@ -203,7 +203,7 @@ void language_menu_state::update_text_content() auto [language_name, language_value] = ctx.menu_item_texts[0]; auto [back_name, back_value] = ctx.menu_item_texts[1]; - language_name->set_content(get_string(ctx, "language_menu_language"_fnv1a32)); - language_value->set_content(get_string(ctx, "language_name_native"_fnv1a32)); - back_name->set_content(get_string(ctx, "back"_fnv1a32)); + language_name->set_content(get_string(ctx, "language_menu_language")); + language_value->set_content(get_string(ctx, "language_name_native")); + back_name->set_content(get_string(ctx, "back")); } diff --git a/src/game/states/language-menu-state.hpp b/src/game/states/language-menu-state.hpp index c994efc..7bda931 100644 --- a/src/game/states/language-menu-state.hpp +++ b/src/game/states/language-menu-state.hpp @@ -21,6 +21,8 @@ #define ANTKEEPER_LANGUAGE_MENU_STATE_HPP #include "game/states/game-state.hpp" +#include +#include #include #include @@ -36,6 +38,10 @@ private: std::vector language_tags; std::size_t language_index; + + std::unique_ptr language_name_text; + std::unique_ptr language_value_text; + std::unique_ptr back_text; }; #endif // ANTKEEPER_LANGUAGE_MENU_STATE_HPP diff --git a/src/game/states/main-menu-state.cpp b/src/game/states/main-menu-state.cpp index 19ea46a..7111da7 100644 --- a/src/game/states/main-menu-state.cpp +++ b/src/game/states/main-menu-state.cpp @@ -48,7 +48,6 @@ #include #include -using namespace hash::literals; using namespace math::glsl; main_menu_state::main_menu_state(::game& ctx, bool fade_in): @@ -62,18 +61,19 @@ main_menu_state::main_menu_state(::game& ctx, bool fade_in): const vec2 viewport_center = viewport_size * 0.5f; // Construct title text - title_text.set_material(&ctx.title_font_material); - title_text.set_color({1.0f, 1.0f, 1.0f, (fade_in) ? 1.0f : 0.0f}); - title_text.set_font(&ctx.title_font); - title_text.set_content(get_string(ctx, "title_antkeeper"_fnv1a32)); - const auto& title_aabb = static_cast&>(title_text.get_local_bounds()); + title_text = std::make_unique(); + title_text->set_material(ctx.title_font_material); + title_text->set_color({1.0f, 1.0f, 1.0f, (fade_in) ? 1.0f : 0.0f}); + title_text->set_font(&ctx.title_font); + title_text->set_content(get_string(ctx, "title_antkeeper")); + const auto& title_aabb = static_cast&>(title_text->get_local_bounds()); float title_w = title_aabb.max_point.x() - title_aabb.min_point.x(); float title_h = title_aabb.max_point.y() - title_aabb.min_point.y(); - title_text.set_translation({std::round(viewport_center.x() - title_w * 0.5f), std::round(viewport_center.y() - title_h * 0.5f + (viewport_size.y() / 3.0f) / 2.0f), 0.0f}); - title_text.update_tweens(); + title_text->set_translation({std::round(viewport_center.x() - title_w * 0.5f), std::round(viewport_center.y() - title_h * 0.5f + (viewport_size.y() / 3.0f) / 2.0f), 0.0f}); + title_text->update_tweens(); // Add text to UI - ctx.ui_scene->add_object(&title_text); + ctx.ui_scene->add_object(title_text.get()); // Construct title fade animation title_fade_animation.set_interpolator(ease::out_cubic); @@ -82,30 +82,30 @@ main_menu_state::main_menu_state(::game& ctx, bool fade_in): ( [this, &ctx](int channel, const float& opacity) { - float4 color = this->title_text.get_color(); + float4 color = this->title_text->get_color(); color[3] = opacity; - this->title_text.set_color(color); + this->title_text->set_color(color); } ); ctx.animator->add_animation(&title_fade_animation); // Construct menu item texts - scene::text* start_text = new scene::text(); - scene::text* options_text = new scene::text(); - scene::text* extras_text = new scene::text(); - scene::text* quit_text = new scene::text(); + start_text = std::make_unique(); + options_text = std::make_unique(); + extras_text = std::make_unique(); + quit_text = std::make_unique(); // Build list of menu item texts - ctx.menu_item_texts.push_back({start_text, nullptr}); - ctx.menu_item_texts.push_back({options_text, nullptr}); - ctx.menu_item_texts.push_back({extras_text, nullptr}); - ctx.menu_item_texts.push_back({quit_text, nullptr}); + ctx.menu_item_texts.push_back({start_text.get(), nullptr}); + ctx.menu_item_texts.push_back({options_text.get(), nullptr}); + ctx.menu_item_texts.push_back({extras_text.get(), nullptr}); + ctx.menu_item_texts.push_back({quit_text.get(), nullptr}); // Set content of menu item texts - start_text->set_content(get_string(ctx, "main_menu_start"_fnv1a32)); - options_text->set_content(get_string(ctx, "main_menu_options"_fnv1a32)); - extras_text->set_content(get_string(ctx, "main_menu_extras"_fnv1a32)); - quit_text->set_content(get_string(ctx, "main_menu_quit"_fnv1a32)); + start_text->set_content(get_string(ctx, "main_menu_start")); + options_text->set_content(get_string(ctx, "main_menu_options")); + extras_text->set_content(get_string(ctx, "main_menu_extras")); + quit_text->set_content(get_string(ctx, "main_menu_quit")); // Init menu item index ::menu::init_menu_item_index(ctx, "main"); @@ -146,7 +146,7 @@ main_menu_state::main_menu_state(::game& ctx, bool fade_in): // Start fade out to white //ctx.fade_transition_color->set_value({1, 1, 1}); - ctx.fade_transition_color->set_value({0, 0, 0}); + ctx.fade_transition_color->set({0, 0, 0}); ctx.fade_transition->transition(config::new_colony_fade_out_duration, false, ease::out_cubic, false, change_state); }; auto select_options_callback = [this, &ctx]() @@ -292,11 +292,11 @@ main_menu_state::main_menu_state(::game& ctx, bool fade_in): const vec2 viewport_center = viewport_size * 0.5f; // Re-align title text - const auto& title_aabb = static_cast&>(title_text.get_local_bounds()); + const auto& title_aabb = static_cast&>(title_text->get_local_bounds()); float title_w = title_aabb.max_point.x() - title_aabb.min_point.x(); float title_h = title_aabb.max_point.y() - title_aabb.min_point.y(); - title_text.set_translation({std::round(viewport_center.x() - title_w * 0.5f), std::round(viewport_center.y() - title_h * 0.5f + (viewport_size.y() / 3.0f) / 2.0f), 0.0f}); - title_text.update_tweens(); + title_text->set_translation({std::round(viewport_center.x() - title_w * 0.5f), std::round(viewport_center.y() - title_h * 0.5f + (viewport_size.y() / 3.0f) / 2.0f), 0.0f}); + title_text->update_tweens(); ::menu::align_text(ctx, true, false, (-viewport_size.y() / 3.0f) / 2.0f); } @@ -326,7 +326,7 @@ main_menu_state::~main_menu_state() ctx.animator->remove_animation(&title_fade_animation); // Destruct text - ctx.ui_scene->remove_object(&title_text); + ctx.ui_scene->remove_object(title_text.get()); debug::log::trace("Exited main menu state"); } diff --git a/src/game/states/main-menu-state.hpp b/src/game/states/main-menu-state.hpp index 14a3236..08f24fb 100644 --- a/src/game/states/main-menu-state.hpp +++ b/src/game/states/main-menu-state.hpp @@ -38,7 +38,12 @@ private: void fade_in_title(); void fade_out_title(); - scene::text title_text; + std::unique_ptr title_text; + std::unique_ptr start_text; + std::unique_ptr options_text; + std::unique_ptr extras_text; + std::unique_ptr quit_text; + animation title_fade_animation; std::shared_ptr window_resized_subscription; diff --git a/src/game/states/nest-selection-state.cpp b/src/game/states/nest-selection-state.cpp index 7f7ea3a..54bb701 100644 --- a/src/game/states/nest-selection-state.cpp +++ b/src/game/states/nest-selection-state.cpp @@ -17,10 +17,10 @@ * along with Antkeeper source code. If not, see . */ -#include "game/ant/cladogenesis.hpp" -#include "game/ant/genome.hpp" -#include "game/ant/morphogenesis.hpp" -#include "game/ant/phenome.hpp" +#include "game/ant/ant-cladogenesis.hpp" +#include "game/ant/ant-genome.hpp" +#include "game/ant/ant-morphogenesis.hpp" +#include "game/ant/ant-phenome.hpp" #include "game/commands/commands.hpp" #include "game/components/camera-component.hpp" #include "game/components/constraint-stack-component.hpp" @@ -65,9 +65,6 @@ #include #include -using namespace ::ant; - - nest_selection_state::nest_selection_state(::game& ctx): game_state(ctx) { @@ -86,15 +83,15 @@ nest_selection_state::nest_selection_state(::game& ctx): debug::log::trace("Generating genome..."); std::random_device rng; - ant::genome* genome = ant::cladogenesis(ctx.active_ecoregion->gene_pools[0], rng); + std::unique_ptr genome = ant_cladogenesis(ctx.active_ecoregion->gene_pools[0], rng); debug::log::trace("Generated genome"); debug::log::trace("Building worker phenome..."); - ant::phenome worker_phenome = ant::phenome(*genome, ant::caste::worker); + ant_phenome worker_phenome = ant_phenome(*genome, ant_caste::worker); debug::log::trace("Built worker phenome..."); debug::log::trace("Generating worker model..."); - render::model* worker_model = ant::morphogenesis(worker_phenome); + std::shared_ptr worker_model = ant_morphogenesis(worker_phenome); debug::log::trace("Generated worker model"); // Create worker entity(s) @@ -200,7 +197,7 @@ nest_selection_state::nest_selection_state(::game& ctx): ); // Queue fade in - ctx.fade_transition_color->set_value({0, 0, 0}); + ctx.fade_transition_color->set({0, 0, 0}); ctx.function_queue.push(std::bind(&screen_transition::transition, ctx.fade_transition.get(), 1.0f, true, ease::out_sine, true, nullptr)); debug::log::trace("Entered nest selection state"); diff --git a/src/game/states/nuptial-flight-state.cpp b/src/game/states/nuptial-flight-state.cpp index 1c40d49..e0805a9 100644 --- a/src/game/states/nuptial-flight-state.cpp +++ b/src/game/states/nuptial-flight-state.cpp @@ -20,18 +20,19 @@ #include "game/states/nuptial-flight-state.hpp" #include "game/states/pause-menu-state.hpp" #include "game/states/nest-selection-state.hpp" -#include "game/ant/swarm.hpp" +#include "game/ant/ant-swarm.hpp" #include #include "game/systems/camera-system.hpp" #include "game/systems/astronomy-system.hpp" #include "game/systems/atmosphere-system.hpp" #include "game/systems/collision-system.hpp" -#include "game/components/caste-component.hpp" +#include "game/components/ant-caste-component.hpp" #include "game/components/locomotion-component.hpp" #include "game/components/transform-component.hpp" #include "game/components/terrain-component.hpp" #include "game/components/camera-component.hpp" #include "game/components/model-component.hpp" +#include "game/components/name-component.hpp" #include "game/components/constraint-stack-component.hpp" #include "game/components/steering-component.hpp" #include "game/components/picking-component.hpp" @@ -65,6 +66,7 @@ #include #include #include +#include using namespace hash::literals; @@ -102,7 +104,41 @@ nuptial_flight_state::nuptial_flight_state(::game& ctx): ctx.ground_pass->set_enabled(true); // Create mating swarm - swarm_eid = ::ant::create_swarm(ctx); + swarm_eid = create_ant_swarm(ctx); + + // Load name pools + female_name_pool = ctx.resource_manager->load("female-names-en.txt"); + male_name_pool = ctx.resource_manager->load("male-names-en.txt"); + + // Init RNG + std::random_device random_device; + std::mt19937 rng(random_device()); + + // Assign random ant names + std::uniform_int_distribution<> female_name_pool_distribution(0, female_name_pool->lines.size() - 1); + std::uniform_int_distribution<> male_name_pool_distribution(0, male_name_pool->lines.size() - 1); + ctx.entity_registry->view().each + ( + [&](entity::id entity_id, const auto& caste) + { + if (caste.type == ant_caste::male) + { + ctx.entity_registry->emplace_or_replace + ( + entity_id, + male_name_pool->lines[male_name_pool_distribution(rng)] + ); + } + else + { + ctx.entity_registry->emplace_or_replace + ( + entity_id, + female_name_pool->lines[female_name_pool_distribution(rng)] + ); + } + } + ); // Switch to surface camera ctx.underground_camera->set_active(false); @@ -129,33 +165,33 @@ nuptial_flight_state::nuptial_flight_state(::game& ctx): // Create camera rig create_camera_rig(); - // Select random alate - ctx.entity_registry->view().each - ( - [&](entity::id alate_eid, auto& transform, auto& steering) - { - select_entity(alate_eid); - } - ); - - // Satisfy camera rig constraints - satisfy_camera_rig_constraints(); - // Construct selection text - selection_text.set_material(&ctx.menu_font_material); + selection_text.set_material(ctx.menu_font_material); selection_text.set_color({1.0f, 1.0f, 1.0f, 1.0f}); selection_text.set_font(&ctx.menu_font); - //selection_text.set_content(get_string(ctx, "title_antkeeper"_fnv1a32)); - selection_text.set_content("Hello, World!"); const auto& text_aabb = static_cast&>(selection_text.get_local_bounds()); float text_w = text_aabb.max_point.x() - text_aabb.min_point.x(); float text_h = text_aabb.max_point.y() - text_aabb.min_point.y(); - selection_text.set_translation({std::round(viewport_size.x() * 0.5f - text_w * 0.5f), std::round(0.0f), 0.0f}); + selection_text.set_translation({std::round(viewport_size.x() * 0.5f - text_w * 0.5f), std::round(ctx.menu_font.get_font_metrics().size), 0.0f}); selection_text.update_tweens(); // Add text to UI ctx.ui_scene->add_object(&selection_text); + // Select random alate + entity::id random_alate_eid; + ctx.entity_registry->view().each + ( + [&](entity::id entity_id, auto& transform, auto& steering) + { + random_alate_eid = entity_id; + } + ); + select_entity(random_alate_eid); + + // Satisfy camera rig constraints + satisfy_camera_rig_constraints(); + // Setup controls setup_controls(); @@ -171,9 +207,12 @@ nuptial_flight_state::nuptial_flight_state(::game& ctx): ); // Queue fade in - ctx.fade_transition_color->set_value({0, 0, 0}); + ctx.fade_transition_color->set({0, 0, 0}); ctx.function_queue.push(std::bind(&screen_transition::transition, ctx.fade_transition.get(), 1.0f, true, ease::out_sine, true, nullptr)); + // Reset loop timing + ctx.loop.reset(); + debug::log::trace("Entered nuptial flight state"); } @@ -193,7 +232,7 @@ nuptial_flight_state::~nuptial_flight_state() destroy_camera_rig(); - ::ant::destroy_swarm(ctx, swarm_eid); + destroy_ant_swarm(ctx, swarm_eid); debug::log::trace("Exited nuptial flight state"); } @@ -923,39 +962,65 @@ void nuptial_flight_state::select_entity(entity::id entity_id) ); // Update selection text - if (ctx.entity_registry->valid(selected_eid) && ctx.entity_registry->all_of<::caste_component>(selected_eid)) + if (ctx.entity_registry->valid(selected_eid) && ctx.entity_registry->all_of<::ant_caste_component>(selected_eid)) { - std::string format_string = "{}"; + const auto& caste = ctx.entity_registry->get<::ant_caste_component>(selected_eid); - const auto& caste = ctx.entity_registry->get<::caste_component>(selected_eid); - switch (caste.type) + if (ctx.entity_registry->all_of<::name_component>(selected_eid)) { - case ::ant::caste::queen: - format_string = ::get_string(ctx, "ant_label_numbered_queen_format"_fnv1a32); - break; - - case ::ant::caste::worker: - format_string = ::get_string(ctx, "ant_label_numbered_worker_format"_fnv1a32); - break; - - case ::ant::caste::soldier: - format_string = ::get_string(ctx, "ant_label_numbered_soldier_format"_fnv1a32); - break; + const auto& name = ctx.entity_registry->get<::name_component>(selected_eid).name; - case ::ant::caste::male: - format_string = ::get_string(ctx, "ant_label_numbered_male_format"_fnv1a32); - break; + std::string format_string; + switch (caste.type) + { + case ::ant_caste::queen: + format_string = ::get_string(ctx, "named_queen_label_format"); + break; + + case ::ant_caste::worker: + format_string = ::get_string(ctx, "named_worker_label_format"); + break; + + case ::ant_caste::soldier: + format_string = ::get_string(ctx, "named_soldier_label_format"); + break; + + case ::ant_caste::male: + format_string = ::get_string(ctx, "named_male_label_format"); + break; + + default: + //std::unreachable(); + break; + } - default: - //std::unreachable(); - break; + selection_text.set_content(std::vformat(format_string, std::make_format_args(name))); + } + else + { + switch (caste.type) + { + case ::ant_caste::queen: + selection_text.set_content(get_string(ctx, "queen_caste_name")); + break; + + case ::ant_caste::worker: + selection_text.set_content(get_string(ctx, "worker_caste_name")); + break; + + case ::ant_caste::soldier: + selection_text.set_content(get_string(ctx, "soldier_caste_name")); + break; + + case ::ant_caste::male: + selection_text.set_content(get_string(ctx, "male_caste_name")); + break; + + default: + //std::unreachable(); + break; + } } - - selection_text.set_content(std::vformat(format_string, std::make_format_args(std::to_underlying(selected_eid)))); - } - else - { - } } } diff --git a/src/game/states/nuptial-flight-state.hpp b/src/game/states/nuptial-flight-state.hpp index f9d9d7f..77b0f18 100644 --- a/src/game/states/nuptial-flight-state.hpp +++ b/src/game/states/nuptial-flight-state.hpp @@ -25,9 +25,9 @@ #include #include #include +#include #include - class nuptial_flight_state: public game_state { public: @@ -71,6 +71,10 @@ private: // Ants entity::id swarm_eid; + // Name generation + std::shared_ptr female_name_pool; + std::shared_ptr male_name_pool; + // Picking std::uint32_t selected_picking_flag; entity::id selected_eid; diff --git a/src/game/states/options-menu-state.cpp b/src/game/states/options-menu-state.cpp index 7f25a67..34b30d9 100644 --- a/src/game/states/options-menu-state.cpp +++ b/src/game/states/options-menu-state.cpp @@ -42,25 +42,25 @@ options_menu_state::options_menu_state(::game& ctx): debug::log::trace("Entering options menu state..."); // Construct menu item texts - scene::text* controls_text = new scene::text(); - scene::text* graphics_text = new scene::text(); - scene::text* sound_text = new scene::text(); - scene::text* language_text = new scene::text(); - scene::text* back_text = new scene::text(); + controls_text = std::make_unique(); + graphics_text = std::make_unique(); + sound_text = std::make_unique(); + language_text = std::make_unique(); + back_text = std::make_unique(); // Set content of menu item texts - controls_text->set_content(get_string(ctx, "options_menu_controls"_fnv1a32)); - graphics_text->set_content(get_string(ctx, "options_menu_graphics"_fnv1a32)); - sound_text->set_content(get_string(ctx, "options_menu_sound"_fnv1a32)); - language_text->set_content(get_string(ctx, "options_menu_language"_fnv1a32)); - back_text->set_content(get_string(ctx, "back"_fnv1a32)); + controls_text->set_content(get_string(ctx, "options_menu_controls")); + graphics_text->set_content(get_string(ctx, "options_menu_graphics")); + sound_text->set_content(get_string(ctx, "options_menu_sound")); + language_text->set_content(get_string(ctx, "options_menu_language")); + back_text->set_content(get_string(ctx, "back")); // Build list of menu item texts - ctx.menu_item_texts.push_back({controls_text, nullptr}); - ctx.menu_item_texts.push_back({graphics_text, nullptr}); - ctx.menu_item_texts.push_back({sound_text, nullptr}); - ctx.menu_item_texts.push_back({language_text, nullptr}); - ctx.menu_item_texts.push_back({back_text, nullptr}); + ctx.menu_item_texts.push_back({controls_text.get(), nullptr}); + ctx.menu_item_texts.push_back({graphics_text.get(), nullptr}); + ctx.menu_item_texts.push_back({sound_text.get(), nullptr}); + ctx.menu_item_texts.push_back({language_text.get(), nullptr}); + ctx.menu_item_texts.push_back({back_text.get(), nullptr}); // Init menu item index ::menu::init_menu_item_index(ctx, "options"); diff --git a/src/game/states/options-menu-state.hpp b/src/game/states/options-menu-state.hpp index 10c831d..d5c7422 100644 --- a/src/game/states/options-menu-state.hpp +++ b/src/game/states/options-menu-state.hpp @@ -21,6 +21,8 @@ #define ANTKEEPER_OPTIONS_MENU_STATE_HPP #include "game/states/game-state.hpp" +#include +#include class options_menu_state: public game_state @@ -28,6 +30,13 @@ class options_menu_state: public game_state public: options_menu_state(::game& ctx); virtual ~options_menu_state(); + +private: + std::unique_ptr controls_text; + std::unique_ptr graphics_text; + std::unique_ptr sound_text; + std::unique_ptr language_text; + std::unique_ptr back_text; }; #endif // ANTKEEPER_OPTIONS_MENU_STATE_HPP diff --git a/src/game/states/pause-menu-state.cpp b/src/game/states/pause-menu-state.cpp index cc33751..27bc6ea 100644 --- a/src/game/states/pause-menu-state.cpp +++ b/src/game/states/pause-menu-state.cpp @@ -41,22 +41,22 @@ pause_menu_state::pause_menu_state(::game& ctx): debug::log::trace("Entering pause menu state..."); // Construct menu item texts - scene::text* resume_text = new scene::text(); - scene::text* options_text = new scene::text(); - scene::text* main_menu_text = new scene::text(); - scene::text* quit_text = new scene::text(); + resume_text = std::make_unique();; + options_text = std::make_unique();; + main_menu_text = std::make_unique();; + quit_text = std::make_unique();; // Set content of menu item texts - resume_text->set_content(get_string(ctx, "pause_menu_resume"_fnv1a32)); - options_text->set_content(get_string(ctx, "pause_menu_options"_fnv1a32)); - main_menu_text->set_content(get_string(ctx, "pause_menu_main_menu"_fnv1a32)); - quit_text->set_content(get_string(ctx, "pause_menu_quit"_fnv1a32)); + resume_text->set_content(get_string(ctx, "pause_menu_resume")); + options_text->set_content(get_string(ctx, "pause_menu_options")); + main_menu_text->set_content(get_string(ctx, "pause_menu_main_menu")); + quit_text->set_content(get_string(ctx, "pause_menu_quit")); // Build list of menu item texts - ctx.menu_item_texts.push_back({resume_text, nullptr}); - ctx.menu_item_texts.push_back({options_text, nullptr}); - ctx.menu_item_texts.push_back({main_menu_text, nullptr}); - ctx.menu_item_texts.push_back({quit_text, nullptr}); + ctx.menu_item_texts.push_back({resume_text.get(), nullptr}); + ctx.menu_item_texts.push_back({options_text.get(), nullptr}); + ctx.menu_item_texts.push_back({main_menu_text.get(), nullptr}); + ctx.menu_item_texts.push_back({quit_text.get(), nullptr}); // Init menu item index ::menu::init_menu_item_index(ctx, "pause"); @@ -149,7 +149,7 @@ pause_menu_state::pause_menu_state(::game& ctx): ::menu::fade_out(ctx, nullptr); // Fade out to black then return to main menu - ctx.fade_transition_color->set_value({0, 0, 0}); + ctx.fade_transition_color->set({0, 0, 0}); ctx.fade_transition->transition(config::quit_fade_out_duration, false, ease::out_cubic, false, fade_out_callback); }; auto select_quit_callback = [&ctx]() @@ -167,7 +167,7 @@ pause_menu_state::pause_menu_state(::game& ctx): ::menu::fade_out(ctx, nullptr); // Fade out to black then quit - ctx.fade_transition_color->set_value({0, 0, 0}); + ctx.fade_transition_color->set({0, 0, 0}); ctx.fade_transition->transition(config::quit_fade_out_duration, false, ease::out_cubic, false, [&ctx](){ctx.closed=true;}); }; diff --git a/src/game/states/pause-menu-state.hpp b/src/game/states/pause-menu-state.hpp index 53be936..5846eb9 100644 --- a/src/game/states/pause-menu-state.hpp +++ b/src/game/states/pause-menu-state.hpp @@ -21,13 +21,20 @@ #define ANTKEEPER_PAUSE_MENU_STATE_HPP #include "game/states/game-state.hpp" - +#include +#include class pause_menu_state: public game_state { public: pause_menu_state(::game& ctx); virtual ~pause_menu_state(); + +private: + std::unique_ptr resume_text; + std::unique_ptr options_text; + std::unique_ptr main_menu_text; + std::unique_ptr quit_text; }; #endif // ANTKEEPER_PAUSE_MENU_STATE_HPP diff --git a/src/game/states/sound-menu-state.cpp b/src/game/states/sound-menu-state.cpp index 1ff4366..ee521f2 100644 --- a/src/game/states/sound-menu-state.cpp +++ b/src/game/states/sound-menu-state.cpp @@ -35,37 +35,37 @@ sound_menu_state::sound_menu_state(::game& ctx): debug::log::trace("Entering sound menu state..."); // Construct menu item texts - scene::text* master_volume_name_text = new scene::text(); - scene::text* master_volume_value_text = new scene::text(); - scene::text* ambience_volume_name_text = new scene::text(); - scene::text* ambience_volume_value_text = new scene::text(); - scene::text* effects_volume_name_text = new scene::text(); - scene::text* effects_volume_value_text = new scene::text(); - scene::text* mono_audio_name_text = new scene::text(); - scene::text* mono_audio_value_text = new scene::text(); - scene::text* captions_name_text = new scene::text(); - scene::text* captions_value_text = new scene::text(); - scene::text* captions_size_name_text = new scene::text(); - scene::text* captions_size_value_text = new scene::text(); - scene::text* back_text = new scene::text(); + master_volume_name_text = std::make_unique(); + master_volume_value_text = std::make_unique(); + ambience_volume_name_text = std::make_unique(); + ambience_volume_value_text = std::make_unique(); + effects_volume_name_text = std::make_unique(); + effects_volume_value_text = std::make_unique(); + mono_audio_name_text = std::make_unique(); + mono_audio_value_text = std::make_unique(); + captions_name_text = std::make_unique(); + captions_value_text = std::make_unique(); + captions_size_name_text = std::make_unique(); + captions_size_value_text = std::make_unique(); + back_text = std::make_unique(); // Build list of menu item texts - ctx.menu_item_texts.push_back({master_volume_name_text, master_volume_value_text}); - ctx.menu_item_texts.push_back({ambience_volume_name_text, ambience_volume_value_text}); - ctx.menu_item_texts.push_back({effects_volume_name_text, effects_volume_value_text}); - ctx.menu_item_texts.push_back({mono_audio_name_text, mono_audio_value_text}); - ctx.menu_item_texts.push_back({captions_name_text, captions_value_text}); - ctx.menu_item_texts.push_back({captions_size_name_text, captions_size_value_text}); - ctx.menu_item_texts.push_back({back_text, nullptr}); + ctx.menu_item_texts.push_back({master_volume_name_text.get(), master_volume_value_text.get()}); + ctx.menu_item_texts.push_back({ambience_volume_name_text.get(), ambience_volume_value_text.get()}); + ctx.menu_item_texts.push_back({effects_volume_name_text.get(), effects_volume_value_text.get()}); + ctx.menu_item_texts.push_back({mono_audio_name_text.get(), mono_audio_value_text.get()}); + ctx.menu_item_texts.push_back({captions_name_text.get(), captions_value_text.get()}); + ctx.menu_item_texts.push_back({captions_size_name_text.get(), captions_size_value_text.get()}); + ctx.menu_item_texts.push_back({back_text.get(), nullptr}); // Set content of menu item texts - master_volume_name_text->set_content(get_string(ctx, "sound_menu_master_volume"_fnv1a32)); - ambience_volume_name_text->set_content(get_string(ctx, "sound_menu_ambience_volume"_fnv1a32)); - effects_volume_name_text->set_content(get_string(ctx, "sound_menu_effects_volume"_fnv1a32)); - mono_audio_name_text->set_content(get_string(ctx, "sound_menu_mono_audio"_fnv1a32)); - captions_name_text->set_content(get_string(ctx, "sound_menu_captions"_fnv1a32)); - captions_size_name_text->set_content(get_string(ctx, "sound_menu_captions_size"_fnv1a32)); - back_text->set_content(get_string(ctx, "back"_fnv1a32)); + master_volume_name_text->set_content(get_string(ctx, "sound_menu_master_volume")); + ambience_volume_name_text->set_content(get_string(ctx, "sound_menu_ambience_volume")); + effects_volume_name_text->set_content(get_string(ctx, "sound_menu_effects_volume")); + mono_audio_name_text->set_content(get_string(ctx, "sound_menu_mono_audio")); + captions_name_text->set_content(get_string(ctx, "sound_menu_captions")); + captions_size_name_text->set_content(get_string(ctx, "sound_menu_captions_size")); + back_text->set_content(get_string(ctx, "back")); update_value_text_content(); // Init menu item index @@ -241,8 +241,8 @@ sound_menu_state::~sound_menu_state() void sound_menu_state::update_value_text_content() { - const std::string string_on = get_string(ctx, "on"_fnv1a32); - const std::string string_off = get_string(ctx, "off"_fnv1a32); + const std::string string_on = get_string(ctx, "on"); + const std::string string_off = get_string(ctx, "off"); std::get<1>(ctx.menu_item_texts[0])->set_content(std::to_string(static_cast(std::round(ctx.master_volume * 100.0f))) + "%"); std::get<1>(ctx.menu_item_texts[1])->set_content(std::to_string(static_cast(std::round(ctx.ambience_volume * 100.0f))) + "%"); diff --git a/src/game/states/sound-menu-state.hpp b/src/game/states/sound-menu-state.hpp index 15d841f..b9c851c 100644 --- a/src/game/states/sound-menu-state.hpp +++ b/src/game/states/sound-menu-state.hpp @@ -21,7 +21,8 @@ #define ANTKEEPER_SOUND_MENU_STATE_HPP #include "game/states/game-state.hpp" - +#include +#include class sound_menu_state: public game_state { @@ -31,6 +32,20 @@ public: private: void update_value_text_content(); + + std::unique_ptr master_volume_name_text; + std::unique_ptr master_volume_value_text; + std::unique_ptr ambience_volume_name_text; + std::unique_ptr ambience_volume_value_text; + std::unique_ptr effects_volume_name_text; + std::unique_ptr effects_volume_value_text; + std::unique_ptr mono_audio_name_text; + std::unique_ptr mono_audio_value_text; + std::unique_ptr captions_name_text; + std::unique_ptr captions_value_text; + std::unique_ptr captions_size_name_text; + std::unique_ptr captions_size_value_text; + std::unique_ptr back_text; }; #endif // ANTKEEPER_SOUND_MENU_STATE_HPP diff --git a/src/game/states/splash-state.cpp b/src/game/states/splash-state.cpp index db7cf6e..6cc55f0 100644 --- a/src/game/states/splash-state.cpp +++ b/src/game/states/splash-state.cpp @@ -45,21 +45,23 @@ splash_state::splash_state(::game& ctx): ctx.ui_clear_pass->set_cleared_buffers(true, true, false); // Load splash texture - const gl::texture_2d* splash_texture = ctx.resource_manager->load("splash.tex"); + auto splash_texture = ctx.resource_manager->load("splash.tex"); // Get splash texture dimensions auto splash_dimensions = splash_texture->get_dimensions(); // Construct splash billboard material - splash_billboard_material.set_blend_mode(render::blend_mode::translucent); - splash_billboard_material.set_shader_program(ctx.resource_manager->load("ui-element-textured.glsl")); - splash_billboard_material.add_property("background")->set_value(splash_texture); - render::material_property* splash_tint = splash_billboard_material.add_property("tint"); - splash_tint->set_value(float4{1, 1, 1, 0}); - splash_billboard_material.update_tweens(); + splash_billboard_material = std::make_shared(); + splash_billboard_material->set_blend_mode(render::material_blend_mode::translucent); + splash_billboard_material->set_shader_template(ctx.resource_manager->load("ui-element-textured.glsl")); + splash_billboard_material->set_variable("background", std::make_shared(1, splash_texture)); + + auto splash_tint = std::make_shared(1, float4{1, 1, 1, 0}); + splash_billboard_material->set_variable("tint", splash_tint); + //splash_billboard_material.update_tweens(); // Construct splash billboard - splash_billboard.set_material(&splash_billboard_material); + splash_billboard.set_material(splash_billboard_material); splash_billboard.set_scale({(float)std::get<0>(splash_dimensions) * 0.5f, (float)std::get<1>(splash_dimensions) * 0.5f, 1.0f}); splash_billboard.set_translation({std::round(viewport_center.x()), std::round(viewport_center.y()), 0.0f}); splash_billboard.update_tweens(); @@ -88,7 +90,7 @@ splash_state::splash_state(::game& ctx): // Setup animation frame callbacks auto set_splash_opacity = [splash_tint](int channel, const float& opacity) { - splash_tint->set_value(float4{1, 1, 1, opacity}); + splash_tint->set(float4{1, 1, 1, opacity}); }; splash_fade_in_animation.set_frame_callback(set_splash_opacity); splash_fade_out_animation.set_frame_callback(set_splash_opacity); @@ -207,9 +209,6 @@ splash_state::~splash_state() // Remove splash billboard from UI scene ctx.ui_scene->remove_object(&splash_billboard); - // Unload splash texture - ctx.resource_manager->unload("splash.tex"); - // Disable color buffer clearing in UI pass ctx.ui_clear_pass->set_cleared_buffers(false, true, false); diff --git a/src/game/states/splash-state.hpp b/src/game/states/splash-state.hpp index c148f5c..30249df 100644 --- a/src/game/states/splash-state.hpp +++ b/src/game/states/splash-state.hpp @@ -35,7 +35,7 @@ public: virtual ~splash_state(); private: - render::material splash_billboard_material; + std::shared_ptr splash_billboard_material; scene::billboard splash_billboard; animation splash_fade_in_animation; animation splash_fade_out_animation; diff --git a/src/game/strings.cpp b/src/game/strings.cpp index bd1efe7..605c2de 100644 --- a/src/game/strings.cpp +++ b/src/game/strings.cpp @@ -20,14 +20,12 @@ #include "game/strings.hpp" #include - -std::string get_string(const ::game& ctx, std::uint32_t key) +std::string get_string(const ::game& ctx, hash::fnv1a32_t key) { if (auto i = ctx.string_map->find(key); i != ctx.string_map->end()) { return i->second; } - return std::format("${:x}", key); + return std::format("${:x}", key.value); } - diff --git a/src/game/strings.hpp b/src/game/strings.hpp index 3af8574..563ec6b 100644 --- a/src/game/strings.hpp +++ b/src/game/strings.hpp @@ -21,20 +21,18 @@ #define ANTKEEPER_GAME_STRINGS_HPP #include "game/game.hpp" +#include #include #include - /** * Returns a localized string. * - * @param[in] ctx Game context. - * @param[in] key String key. - * @param[out] string String value. + * @param ctx Game context. + * @param key String key. * - * @return `true` if the string was found, `false` otherwise. + * @return String value. */ -[[nodiscard]] std::string get_string(const ::game& ctx, std::uint32_t key); - +[[nodiscard]] std::string get_string(const ::game& ctx, hash::fnv1a32_t key); #endif // ANTKEEPER_GAME_STRINGS_HPP diff --git a/src/game/systems/orbit-system.cpp b/src/game/systems/orbit-system.cpp index 358f27e..b62f3e6 100644 --- a/src/game/systems/orbit-system.cpp +++ b/src/game/systems/orbit-system.cpp @@ -47,7 +47,7 @@ void orbit_system::update(double t, double dt) // Calculate positions of ephemeris items, in meters for (int i: ephemeris_indices) - positions[i] = (*ephemeris)[i].position(time) * 1000.0; + positions[i] = ephemeris->trajectories[i].position(time) * 1000.0; // Propagate orbits registry.view().each( @@ -65,10 +65,10 @@ void orbit_system::update(double t, double dt) }); } -void orbit_system::set_ephemeris(const physics::orbit::ephemeris* ephemeris) +void orbit_system::set_ephemeris(std::shared_ptr> ephemeris) { this->ephemeris = ephemeris; - positions.resize((ephemeris) ? ephemeris->size() : 0); + positions.resize((ephemeris) ? ephemeris->trajectories.size() : 0); } void orbit_system::set_time(double time) diff --git a/src/game/systems/orbit-system.hpp b/src/game/systems/orbit-system.hpp index 128fa99..b91090a 100644 --- a/src/game/systems/orbit-system.hpp +++ b/src/game/systems/orbit-system.hpp @@ -64,18 +64,17 @@ public: * * @param ephemeris Ephemeris. */ - void set_ephemeris(const physics::orbit::ephemeris* ephemeris); + void set_ephemeris(std::shared_ptr> ephemeris); private: void on_orbit_construct(entity::registry& registry, entity::id entity_id); void on_orbit_update(entity::registry& registry, entity::id entity_id); - const physics::orbit::ephemeris* ephemeris; + std::shared_ptr> ephemeris; double time; double time_scale; std::vector positions; std::unordered_set ephemeris_indices; }; - #endif // ANTKEEPER_GAME_ORBIT_SYSTEM_HPP diff --git a/src/game/systems/render-system.cpp b/src/game/systems/render-system.cpp index bb4fd26..996093c 100644 --- a/src/game/systems/render-system.cpp +++ b/src/game/systems/render-system.cpp @@ -60,13 +60,13 @@ void render_system::update(double t, double dt) ( [this](entity::id entity_id, auto& transform, auto& model) { - scene::model_instance* instance = model_instances[entity_id]; + scene::model_instance& instance = *model_instances[entity_id]; - instance->set_transform(transform.world); + instance.set_transform(transform.world); if (transform.warp) { - instance->get_transform_tween().update(); - instance->update_tweens(); + instance.get_transform_tween().update(); + instance.update_tweens(); transform.warp = false; } } @@ -92,13 +92,13 @@ void render_system::update(double t, double dt) ( [this](entity::id entity_id, auto& transform, auto& light) { - scene::light* light_object = lights[entity_id]; + scene::light& light_object = *lights[entity_id]; - light_object->set_transform(transform.world); + light_object.set_transform(transform.world); if (transform.warp) { - light_object->get_transform_tween().update(); - light_object->update_tweens(); + light_object.get_transform_tween().update(); + light_object.update_tweens(); transform.warp = false; } } @@ -134,14 +134,14 @@ void render_system::set_renderer(render::renderer* renderer) scene::model_instance* render_system::get_model_instance(entity::id entity_id) { if (auto it = model_instances.find(entity_id); it != model_instances.end()) - return it->second; + return it->second.get(); return nullptr; } scene::light* render_system::get_light(entity::id entity_id) { if (auto it = lights.find(entity_id); it != lights.end()) - return it->second; + return it->second.get(); return nullptr; } @@ -160,11 +160,11 @@ void render_system::update_model_and_materials(entity::id entity_id, model_compo // Add model instance to its specified layers for (std::size_t i = 0; i < std::min(layers.size(), (sizeof(model.layers) << 3)); ++i) { - layers[i]->remove_object(model_it->second); + layers[i]->remove_object(model_it->second.get()); if ((model.layers >> i) & 1) { - layers[i]->add_object(model_it->second); + layers[i]->add_object(model_it->second.get()); } } } @@ -174,25 +174,25 @@ void render_system::update_light(entity::id entity_id, ::light_component& compon { if (auto light_it = lights.find(entity_id); light_it != lights.end()) { - scene::light* light = light_it->second; + scene::light& light = *light_it->second; - light->set_color(component.color); - light->set_intensity(component.intensity); + light.set_color(component.color); + light.set_intensity(component.intensity); - switch (light->get_light_type()) + switch (light.get_light_type()) { case scene::light_type::point: { - scene::point_light* point = static_cast(light); - point->set_attenuation(component.attenuation); + scene::point_light& point = static_cast(light); + point.set_attenuation(component.attenuation); break; } case scene::light_type::spot: { - scene::spot_light* spot = static_cast(light); - spot->set_attenuation(component.attenuation); - spot->set_cutoff(component.cutoff); + scene::spot_light& spot = static_cast(light); + spot.set_attenuation(component.attenuation); + spot.set_cutoff(component.cutoff); break; } @@ -206,8 +206,7 @@ void render_system::on_model_construct(entity::registry& registry, entity::id en { ::model_component& component = registry.get<::model_component>(entity_id); - scene::model_instance* model_instance = new scene::model_instance(); - model_instances[entity_id] = model_instance; + model_instances[entity_id] = std::make_unique(); update_model_and_materials(entity_id, component); } @@ -221,14 +220,10 @@ void render_system::on_model_destroy(entity::registry& registry, entity::id enti { if (auto it = model_instances.find(entity_id); it != model_instances.end()) { - scene::model_instance* model_instance = it->second; - - // Remove model instance from all layers for (scene::collection* layer: layers) - layer->remove_object(model_instance); + layer->remove_object(it->second.get()); model_instances.erase(it); - delete model_instance; } } @@ -236,24 +231,24 @@ void render_system::on_light_construct(entity::registry& registry, entity::id en { ::light_component& component = registry.get<::light_component>(entity_id); - scene::light* light = nullptr; + std::unique_ptr light; switch (component.type) { case scene::light_type::ambient: - light = new scene::ambient_light(); + light = std::make_unique(); break; case scene::light_type::directional: - light = new scene::directional_light(); + light = std::make_unique(); break; case scene::light_type::point: - light = new scene::point_light(); + light = std::make_unique(); break; case scene::light_type::spot: - light = new scene::spot_light(); + light = std::make_unique(); break; default: @@ -262,9 +257,10 @@ void render_system::on_light_construct(entity::registry& registry, entity::id en if (light) { - lights[entity_id] = light; for (scene::collection* layer: layers) - layer->add_object(light); + layer->add_object(light.get()); + + lights[entity_id] = std::move(light); update_light(entity_id, component); } @@ -280,12 +276,9 @@ void render_system::on_light_destroy(entity::registry& registry, entity::id enti { if (auto it = lights.find(entity_id); it != lights.end()) { - scene::light* light = it->second; - for (scene::collection* layer: layers) - layer->remove_object(light); + layer->remove_object(it->second.get()); lights.erase(it); - delete light; } } diff --git a/src/game/systems/render-system.hpp b/src/game/systems/render-system.hpp index 74b4ad5..8a26bbd 100644 --- a/src/game/systems/render-system.hpp +++ b/src/game/systems/render-system.hpp @@ -30,7 +30,7 @@ #include #include #include - +#include class render_system: public updatable_system { @@ -66,8 +66,8 @@ private: double dt; ::render::renderer* renderer; std::vector layers; - std::unordered_map model_instances; - std::unordered_map lights; + std::unordered_map> model_instances; + std::unordered_map> lights; }; diff --git a/src/game/systems/subterrain-system.cpp b/src/game/systems/subterrain-system.cpp index 94a626c..d427053 100644 --- a/src/game/systems/subterrain-system.cpp +++ b/src/game/systems/subterrain-system.cpp @@ -192,6 +192,7 @@ subterrain_system::subterrain_system(entity::registry& registry, ::resource_mana resource_manager(resource_manager) { +/* // Load subterrain materials subterrain_inside_material = nullptr;//resource_manager->load<::render::material>("subterrain-inside.mtl"); subterrain_outside_material = nullptr;//resource_manager->load<::render::material>("subterrain-outside.mtl"); @@ -276,16 +277,20 @@ subterrain_system::subterrain_system(entity::registry& registry, ::resource_mana subterrain_mesh = new geom::mesh(); first_run = true; + */ } subterrain_system::~subterrain_system() { + /* delete subterrain_model; delete subterrain_mesh; + */ } void subterrain_system::update(double t, double dt) { + /* if (first_run) { first_run = false; @@ -318,15 +323,17 @@ void subterrain_system::update(double t, double dt) regenerate_subterrain_model(); //std::cout << "regenerating subterrain model... done\n"; } + */ } void subterrain_system::set_scene(scene::collection* collection) { - this->collection = collection; + //this->collection = collection; } void subterrain_system::regenerate_subterrain_mesh() { + /* delete subterrain_mesh; subterrain_mesh = new geom::mesh(); subterrain_vertices.clear(); @@ -345,10 +352,12 @@ void subterrain_system::regenerate_subterrain_mesh() //std::cout << "creating mesh...\n"; create_triangle_mesh(*subterrain_mesh, subterrain_vertices, subterrain_triangles); //std::cout << "creating mesh... done\n"; + */ } void subterrain_system::march(::cube_tree* node) { + /* if (!node->is_leaf()) { for (::cube_tree* child: node->children) @@ -401,10 +410,12 @@ void subterrain_system::march(::cube_tree* node) vertex_remap[triangle_buffer[i * 3 + 2]] }); } + */ } void subterrain_system::regenerate_subterrain_model() { + /* float3* face_normals = new float3[subterrain_mesh->get_faces().size()]; calculate_face_normals(face_normals, *subterrain_mesh); @@ -474,10 +485,12 @@ void subterrain_system::regenerate_subterrain_model() // Update model groups subterrain_inside_group->set_index_count(subterrain_mesh->get_faces().size() * 3); subterrain_outside_group->set_index_count(subterrain_mesh->get_faces().size() * 3); + */ } void subterrain_system::dig(const float3& position, float radius) { + /* // Construct region containing the cavity sphere geom::aabb region = {position, position}; for (int i = 0; i < 3; ++i) @@ -505,5 +518,6 @@ void subterrain_system::dig(const float3& position, float radius) node.distances[i] = distance; } }); + */ } diff --git a/src/game/systems/terrain-system.cpp b/src/game/systems/terrain-system.cpp index 69257c1..d594a36 100644 --- a/src/game/systems/terrain-system.cpp +++ b/src/game/systems/terrain-system.cpp @@ -41,8 +41,7 @@ terrain_system::terrain_system(entity::registry& registry): scene_collection(nullptr), patch_base_mesh(nullptr), patch_vertex_size(0), - patch_vertex_stride(0), - patch_vertex_data(nullptr) + patch_vertex_stride(0) { // Specify vertex size and stride // (position + uv + normal + tangent + barycentric + target) @@ -145,8 +144,7 @@ void terrain_system::set_patch_subdivisions(std::size_t n) patch_triangle_count = patch_cell_count * 2; // Resize patch vertex data buffer - delete[] patch_vertex_data; - patch_vertex_data = new float[patch_triangle_count * 3 * patch_vertex_size]; + patch_vertex_data.resize(patch_triangle_count * 3 * patch_vertex_size); // Resize patch buffers @@ -160,7 +158,7 @@ void terrain_system::set_patch_subdivisions(std::size_t n) rebuild_patch_base_mesh(); } -void terrain_system::set_patch_material(::render::material* material) +void terrain_system::set_patch_material(std::shared_ptr<::render::material> material) { patch_material = material; } @@ -214,7 +212,6 @@ float3 terrain_system::get_patch_center(quadtree_node_type node) const void terrain_system::rebuild_patch_base_mesh() { // Rebuild grid - delete patch_base_mesh; patch_base_mesh = geom::meshes::grid_xy(1.0f, patch_subdivisions, patch_subdivisions); // Convert quads to triangle fans @@ -286,7 +283,7 @@ void terrain_system::balance_quadtree() } } -geom::mesh* terrain_system::generate_patch_mesh(quadtree_node_type node) const +std::unique_ptr terrain_system::generate_patch_mesh(quadtree_node_type node) const { // Extract node depth const quadtree_type::node_type node_depth = quadtree_type::depth(node); @@ -310,7 +307,7 @@ geom::mesh* terrain_system::generate_patch_mesh(quadtree_node_type node) const }; // Copy patch base mesh - geom::mesh* patch_mesh = new geom::mesh(*patch_base_mesh); + std::unique_ptr patch_mesh = std::make_unique(*patch_base_mesh); // Modify patch mesh vertex positions for (geom::mesh::vertex* v: patch_mesh->get_vertices()) @@ -483,7 +480,7 @@ geom::mesh* terrain_system::generate_patch_mesh(quadtree_node_type node) const */ // For each row - float* v = patch_vertex_data; + float* v = patch_vertex_data.data(); for (std::size_t i = 1; i < patch_vertex_buffer.size() - 2; ++i) { // For each column @@ -550,6 +547,8 @@ geom::mesh* terrain_system::generate_patch_mesh(quadtree_node_type node) const } } + /* + // Allocate patch model ::render::model* patch_model = new ::render::model(); @@ -635,14 +634,20 @@ geom::mesh* terrain_system::generate_patch_mesh(quadtree_node_type node) const patch_model->set_bounds(patch_bounds); return patch_model; + */ + + return nullptr; } terrain_system::patch* terrain_system::generate_patch(quadtree_node_type node) { + /* patch* node_patch = new patch(); node_patch->mesh = nullptr;//generate_patch_mesh(node); node_patch->model = generate_patch_model(node); node_patch->model_instance = new scene::model_instance(node_patch->model); return node_patch; + */ + return nullptr; } diff --git a/src/game/systems/terrain-system.hpp b/src/game/systems/terrain-system.hpp index c797918..521ed53 100644 --- a/src/game/systems/terrain-system.hpp +++ b/src/game/systems/terrain-system.hpp @@ -66,7 +66,7 @@ public: * * @param material Patch material. */ - void set_patch_material(::render::material* material); + void set_patch_material(std::shared_ptr<::render::material> material); /** * Sets the terrain elevation function. @@ -105,7 +105,7 @@ private: /** * Generates a mesh for a terrain patch given the patch's quadtree node */ - geom::mesh* generate_patch_mesh(quadtree_node_type node) const; + std::unique_ptr generate_patch_mesh(quadtree_node_type node) const; /** * Generates a model for a terrain patch given the patch's mesh. @@ -120,7 +120,7 @@ private: std::size_t patch_triangle_count; std::size_t patch_vertex_size; std::size_t patch_vertex_stride; - float* patch_vertex_data; + mutable std::vector patch_vertex_data; struct patch_vertex { @@ -135,11 +135,11 @@ private: mutable std::vector> patch_vertex_buffer; - ::render::material* patch_material; + std::shared_ptr<::render::material> patch_material; std::function elevation_function; scene::collection* scene_collection; - geom::mesh* patch_base_mesh; + std::unique_ptr patch_base_mesh; /// Quadtree describing level of detail quadtree_type quadtree; diff --git a/src/game/world.cpp b/src/game/world.cpp index 896d3d9..6ec5644 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -59,8 +60,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -220,25 +221,24 @@ void create_stars(::game& ctx) debug::log::trace("Generating fixed stars..."); // Load star catalog - i18n::string_table* star_catalog = nullptr; - star_catalog = ctx.resource_manager->load("hipparcos-7.tsv"); + auto star_catalog = ctx.resource_manager->load("hipparcos-7.tsv"); // Allocate star catalog vertex data std::size_t star_count = 0; - if (star_catalog->size() > 0) - star_count = star_catalog->size() - 1; + if (star_catalog->rows.size() > 0) + star_count = star_catalog->rows.size() - 1; std::size_t star_vertex_size = 7; std::size_t star_vertex_stride = star_vertex_size * sizeof(float); - float* star_vertex_data = new float[star_count * star_vertex_size]; - float* star_vertex = star_vertex_data; + std::vector star_vertex_data(star_count * star_vertex_size); + float* star_vertex = star_vertex_data.data(); // Init starlight illuminance double3 starlight_illuminance = {0, 0, 0}; // Build star catalog vertex data - for (std::size_t i = 1; i < star_catalog->size(); ++i) + for (std::size_t i = 1; i < star_catalog->rows.size(); ++i) { - const i18n::string_table_row& catalog_row = (*star_catalog)[i]; + const auto& row = star_catalog->rows[i]; // Parse star catalog item float ra = 0.0; @@ -247,10 +247,10 @@ void create_stars(::game& ctx) float bv = 0.0; try { - ra = std::stof(catalog_row[1]); - dec = std::stof(catalog_row[2]); - vmag = std::stof(catalog_row[3]); - bv = std::stof(catalog_row[4]); + ra = std::stof(row[1]); + dec = std::stof(row[2]); + vmag = std::stof(row[3]); + bv = std::stof(row[4]); } catch (const std::exception&) { @@ -293,27 +293,21 @@ void create_stars(::game& ctx) starlight_illuminance += illuminance; } - // Unload star catalog - ctx.resource_manager->unload("hipparcos-7.tsv"); - // Allocate stars model - render::model* stars_model = new render::model(); + std::shared_ptr stars_model = std::make_shared(); // Get model VBO and VAO - gl::vertex_buffer* vbo = stars_model->get_vertex_buffer(); - gl::vertex_array* vao = stars_model->get_vertex_array(); + auto& vbo = stars_model->get_vertex_buffer(); + auto& vao = stars_model->get_vertex_array(); // Resize model VBO and upload vertex data - vbo->resize(star_count * star_vertex_stride, star_vertex_data); - - // Free star catalog vertex data - delete[] star_vertex_data; + vbo->resize(star_vertex_data.size(), std::as_bytes(std::span{star_vertex_data})); std::size_t attribute_offset = 0; // Define position vertex attribute gl::vertex_attribute position_attribute; - position_attribute.buffer = vbo; + position_attribute.buffer = vbo.get(); position_attribute.offset = attribute_offset; position_attribute.stride = star_vertex_stride; position_attribute.type = gl::vertex_attribute_type::float_32; @@ -322,7 +316,7 @@ void create_stars(::game& ctx) // Define color vertex attribute gl::vertex_attribute color_attribute; - color_attribute.buffer = vbo; + color_attribute.buffer = vbo.get(); color_attribute.offset = attribute_offset; color_attribute.stride = star_vertex_stride; color_attribute.type = gl::vertex_attribute_type::float_32; @@ -334,14 +328,17 @@ void create_stars(::game& ctx) vao->bind(render::vertex_attribute::color, color_attribute); // Load star material - render::material* star_material = ctx.resource_manager->load("fixed-star.mtl"); + std::shared_ptr star_material = ctx.resource_manager->load("fixed-star.mtl"); // Create model group - render::model_group* stars_model_group = stars_model->add_group("stars"); - stars_model_group->set_material(star_material); - stars_model_group->set_drawing_mode(gl::drawing_mode::points); - stars_model_group->set_start_index(0); - stars_model_group->set_index_count(star_count); + stars_model->get_groups().resize(1); + render::model_group& stars_model_group = stars_model->get_groups().back(); + + stars_model_group.id = "stars"; + stars_model_group.material = star_material; + stars_model_group.drawing_mode = gl::drawing_mode::points; + stars_model_group.start_index = 0; + stars_model_group.index_count = static_cast(star_count); // Pass stars model to sky pass ctx.sky_pass->set_stars_model(stars_model); @@ -358,41 +355,41 @@ void create_sun(::game& ctx) { // Create sun entity - entity::archetype* sun_archetype = ctx.resource_manager->load("sun.ent"); + auto sun_archetype = ctx.resource_manager->load("sun.ent"); entity::id sun_eid = sun_archetype->create(*ctx.entity_registry); ctx.entities["sun"] = sun_eid; // 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(0.005f); - sun_light->set_shadow_cascade_count(4); - sun_light->set_shadow_cascade_coverage(0.15f); - sun_light->set_shadow_cascade_distribution(0.8f); - sun_light->update_tweens(); + ctx.sun_light = std::make_unique(); + ctx.sun_light->set_color({0, 0, 0}); + ctx.sun_light->set_shadow_caster(true); + ctx.sun_light->set_shadow_framebuffer(ctx.shadow_map_framebuffer.get()); + ctx.sun_light->set_shadow_bias(0.005f); + ctx.sun_light->set_shadow_cascade_count(4); + ctx.sun_light->set_shadow_cascade_coverage(0.15f); + ctx.sun_light->set_shadow_cascade_distribution(0.8f); + ctx.sun_light->update_tweens(); // Create sky ambient light scene object - scene::ambient_light* sky_light = new scene::ambient_light(); - sky_light->set_color({0, 0, 0}); - sky_light->update_tweens(); + ctx.sky_light = std::make_unique(); + ctx.sky_light->set_color({0, 0, 0}); + ctx.sky_light->update_tweens(); // Create bounce directional light scene object - scene::directional_light* bounce_light = new scene::directional_light(); - bounce_light->set_color({0, 0, 0}); - bounce_light->look_at({0, 0, 0}, {0, 1, 0}, {1, 0, 0}); - bounce_light->update_tweens(); + ctx.bounce_light = std::make_unique(); + ctx.bounce_light->set_color({0, 0, 0}); + ctx.bounce_light->look_at({0, 0, 0}, {0, 1, 0}, {1, 0, 0}); + ctx.bounce_light->update_tweens(); // Add sun light scene objects to surface scene - ctx.surface_scene->add_object(sun_light); - ctx.surface_scene->add_object(sky_light); - //ctx.surface_scene->add_object(bounce_light); + ctx.surface_scene->add_object(ctx.sun_light.get()); + ctx.surface_scene->add_object(ctx.sky_light.get()); + //ctx.surface_scene->add_object(ctx.bounce_light); // Pass direct sun light scene object to shadow map pass and astronomy system - ctx.astronomy_system->set_sun_light(sun_light); - ctx.astronomy_system->set_sky_light(sky_light); - ctx.astronomy_system->set_bounce_light(bounce_light); + ctx.astronomy_system->set_sun_light(ctx.sun_light.get()); + ctx.astronomy_system->set_sky_light(ctx.sky_light.get()); + ctx.astronomy_system->set_bounce_light(ctx.bounce_light.get()); } debug::log::trace("Generated Sun"); @@ -404,7 +401,7 @@ void create_earth_moon_system(::game& ctx) { // Create Earth-Moon barycenter entity - entity::archetype* em_bary_archetype = ctx.resource_manager->load("em-bary.ent"); + auto em_bary_archetype = ctx.resource_manager->load("em-bary.ent"); entity::id em_bary_eid = em_bary_archetype->create(*ctx.entity_registry); ctx.entities["em_bary"] = em_bary_eid; @@ -424,7 +421,7 @@ void create_earth(::game& ctx) { // Create earth entity - entity::archetype* earth_archetype = ctx.resource_manager->load("earth.ent"); + auto earth_archetype = ctx.resource_manager->load("earth.ent"); entity::id earth_eid = earth_archetype->create(*ctx.entity_registry); ctx.entities["earth"] = earth_eid; @@ -441,7 +438,7 @@ void create_moon(::game& ctx) { // Create lunar entity - entity::archetype* moon_archetype = ctx.resource_manager->load("moon.ent"); + auto moon_archetype = ctx.resource_manager->load("moon.ent"); entity::id moon_eid = moon_archetype->create(*ctx.entity_registry); ctx.entities["moon"] = moon_eid; @@ -452,15 +449,15 @@ void create_moon(::game& ctx) ctx.sky_pass->set_moon_model(ctx.resource_manager->load("moon.mdl")); // Create moon directional light scene object - scene::directional_light* moon_light = new scene::directional_light(); - moon_light->set_color({0, 0, 0}); - moon_light->update_tweens(); + ctx.moon_light = std::make_unique(); + ctx.moon_light->set_color({0, 0, 0}); + ctx.moon_light->update_tweens(); // Add moon light scene objects to surface scene - ctx.surface_scene->add_object(moon_light); + ctx.surface_scene->add_object(ctx.moon_light.get()); // Pass moon light scene object to astronomy system - ctx.astronomy_system->set_moon_light(moon_light); + ctx.astronomy_system->set_moon_light(ctx.moon_light.get()); } debug::log::trace("Generated Moon"); @@ -534,8 +531,8 @@ void enter_ecoregion(::game& ctx, const ecoregion& ecoregion) // Setup sky ctx.sky_pass->set_sky_model(ctx.resource_manager->load("celestial-hemisphere.mdl")); - render::model* terrestrial_hemisphere_model = ctx.resource_manager->load("terrestrial-hemisphere.mdl"); - (*terrestrial_hemisphere_model->get_groups())[0]->set_material(ecoregion.horizon_material); + auto terrestrial_hemisphere_model = ctx.resource_manager->load("terrestrial-hemisphere.mdl"); + terrestrial_hemisphere_model->get_groups().front().material = ecoregion.horizon_material; ctx.ground_pass->set_ground_model(terrestrial_hemisphere_model); // Setup terrain