From c9779bf7663b0732163f18fb4afd2d9fb143ad1e Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Tue, 30 Jun 2020 20:19:33 -0400 Subject: [PATCH] Replace old repo contents with new repo contents --- .gitignore | 5 - CMakeLists.txt | 219 +- COPYING | 674 - LICENSE.md | 675 + README.md | 71 +- build/.gitignore | 2 +- .../animation/animation.cpp} | 22 +- src/antkeeper/animation/animation.hpp | 64 + src/antkeeper/animation/easings.hpp | 284 + src/antkeeper/animation/timeline.cpp | 112 + src/antkeeper/animation/timeline.hpp | 136 + src/antkeeper/animation/tween.hpp | 177 + src/antkeeper/application.cpp | 1055 + src/antkeeper/application.hpp | 386 + src/antkeeper/behavior/behavior-tree.hpp | 193 + src/antkeeper/behavior/ebt.cpp | 54 + src/antkeeper/behavior/ebt.hpp | 66 + src/antkeeper/camera-rig.cpp | 65 + src/antkeeper/camera-rig.hpp | 125 + src/antkeeper/configuration.hpp.in | 44 + src/antkeeper/debug/ansi-codes.hpp | 70 + src/antkeeper/debug/cli.hpp | 153 + src/antkeeper/debug/logger.cpp | 122 + src/antkeeper/debug/logger.hpp | 74 + src/antkeeper/debug/performance-sampler.cpp | 62 + src/antkeeper/debug/performance-sampler.hpp | 66 + .../entity/archetype.hpp} | 26 +- .../entity/components/behavior-component.hpp | 35 + .../entity/components/cavity-component.hpp | 37 + .../entity/components/collision-component.hpp | 40 + .../components/locomotion-component.hpp | 38 + .../entity/components/model-component.hpp | 38 + .../entity/components/nest-component.hpp | 33 + .../entity/components/placement-component.hpp | 35 + .../entity/components/samara-component.hpp | 38 + .../entity/components/terrain-component.hpp | 35 + .../entity/components/tool-component.hpp | 33 + .../entity/components/transform-component.hpp | 36 + src/antkeeper/event/event-dispatcher.cpp | 107 + src/antkeeper/event/event-dispatcher.hpp | 138 + src/antkeeper/event/event-handler.hpp | 83 + src/antkeeper/event/event.hpp | 85 + src/{ => antkeeper}/filesystem.cpp | 59 +- src/{ => antkeeper}/filesystem.hpp | 38 +- src/antkeeper/frame-scheduler.cpp | 85 + src/antkeeper/frame-scheduler.hpp | 91 + src/antkeeper/geometry/aabb.hpp | 201 + src/antkeeper/geometry/bounding-volume.hpp | 94 + src/antkeeper/geometry/convex-hull.hpp | 139 + src/antkeeper/geometry/csg.cpp | 115 + src/antkeeper/geometry/csg.hpp | 88 + src/antkeeper/geometry/intersection.cpp | 184 + src/antkeeper/geometry/intersection.hpp | 54 + src/antkeeper/geometry/mesh-accelerator.cpp | 179 + src/antkeeper/geometry/mesh-accelerator.hpp | 76 + src/antkeeper/geometry/mesh-functions.cpp | 122 + src/antkeeper/geometry/mesh-functions.hpp | 58 + src/antkeeper/geometry/mesh.cpp | 332 + src/antkeeper/geometry/mesh.hpp | 181 + src/antkeeper/geometry/plane.hpp | 112 + src/antkeeper/geometry/ray.hpp | 44 + src/antkeeper/geometry/sphere.hpp | 112 + src/antkeeper/geometry/view-frustum.hpp | 181 + src/antkeeper/input/control-set.cpp | 53 + src/antkeeper/input/control-set.hpp | 81 + src/antkeeper/input/control.cpp | 111 + src/antkeeper/input/control.hpp | 136 + src/antkeeper/input/game-controller.cpp | 70 + src/antkeeper/input/game-controller.hpp | 95 + src/antkeeper/input/input-device.cpp | 30 + src/antkeeper/input/input-device.hpp | 55 + src/antkeeper/input/input-event-router.cpp | 341 + src/antkeeper/input/input-event-router.hpp | 115 + src/antkeeper/input/input-events.cpp | 117 + src/antkeeper/input/input-events.hpp | 173 + src/antkeeper/input/input-mapper.cpp | 155 + src/antkeeper/input/input-mapper.hpp | 103 + src/antkeeper/input/input-mapping.cpp | 141 + src/antkeeper/input/input-mapping.hpp | 198 + src/antkeeper/input/keyboard.cpp | 341 + src/antkeeper/input/keyboard.hpp | 77 + src/antkeeper/input/mouse.cpp | 93 + src/antkeeper/input/mouse.hpp | 131 + src/antkeeper/input/scancode.hpp | 277 + .../input/sdl-game-controller-tables.cpp | 51 + .../input/sdl-game-controller-tables.hpp | 30 + src/antkeeper/input/sdl-scancode-table.cpp | 313 + src/antkeeper/input/sdl-scancode-table.hpp | 28 + src/{ => antkeeper}/main.cpp | 72 +- src/antkeeper/marching-cubes.cpp | 417 + src/antkeeper/marching-cubes.hpp | 58 + src/antkeeper/math.hpp | 75 + src/antkeeper/morton.cpp | 85 + src/antkeeper/morton.hpp | 46 + src/antkeeper/nest.cpp | 99 + src/antkeeper/nest.hpp | 91 + src/antkeeper/octree.hpp | 455 + src/antkeeper/orbit-cam.cpp | 161 + src/antkeeper/orbit-cam.hpp | 130 + src/antkeeper/pheromone-matrix.cpp | 90 + src/antkeeper/pheromone-matrix.hpp | 64 + .../platform/windows}/dpi-aware.manifest | 14 +- src/antkeeper/platform/windows/icon.rc.in | 1 + src/antkeeper/rasterizer/buffer-usage.cpp | 37 + src/antkeeper/rasterizer/buffer-usage.hpp | 37 + src/antkeeper/rasterizer/drawing-mode.hpp | 39 + .../rasterizer/element-array-type.hpp | 31 + src/antkeeper/rasterizer/framebuffer.cpp | 87 + src/antkeeper/rasterizer/framebuffer.hpp | 120 + src/antkeeper/rasterizer/pixel-format.hpp | 36 + .../rasterizer/pixel-type.hpp} | 30 +- src/antkeeper/rasterizer/rasterizer.cpp | 163 + src/antkeeper/rasterizer/rasterizer.hpp | 137 + src/antkeeper/rasterizer/shader-input.cpp | 673 + src/antkeeper/rasterizer/shader-input.hpp | 191 + src/antkeeper/rasterizer/shader-program.cpp | 223 + src/antkeeper/rasterizer/shader-program.hpp | 77 + .../rasterizer/shader-type.hpp} | 24 +- .../rasterizer/shader-variable-type.hpp | 49 + src/antkeeper/rasterizer/shader.cpp | 69 + src/antkeeper/rasterizer/shader.hpp | 55 + src/antkeeper/rasterizer/texture-2d.cpp | 190 + src/antkeeper/rasterizer/texture-2d.hpp | 139 + src/antkeeper/rasterizer/texture-cube.cpp | 34 + src/antkeeper/rasterizer/texture-cube.hpp | 57 + src/antkeeper/rasterizer/texture-filter.hpp | 40 + .../rasterizer/texture-wrapping.hpp} | 25 +- src/antkeeper/rasterizer/vertex-array.cpp | 63 + src/antkeeper/rasterizer/vertex-array.hpp | 48 + .../rasterizer/vertex-attribute-type.hpp | 37 + src/antkeeper/rasterizer/vertex-buffer.cpp | 78 + src/antkeeper/rasterizer/vertex-buffer.hpp | 65 + src/antkeeper/renderer/compositor.cpp | 48 + src/antkeeper/renderer/compositor.hpp | 52 + src/antkeeper/renderer/material-flags.hpp | 36 + src/antkeeper/renderer/material-property.cpp | 43 + src/antkeeper/renderer/material-property.hpp | 342 + src/antkeeper/renderer/material.cpp | 119 + src/antkeeper/renderer/material.hpp | 179 + src/antkeeper/renderer/model.cpp | 119 + src/antkeeper/renderer/model.hpp | 195 + src/antkeeper/renderer/passes/bloom-pass.cpp | 167 + src/antkeeper/renderer/passes/bloom-pass.hpp | 74 + src/antkeeper/renderer/passes/clear-pass.cpp | 78 + src/antkeeper/renderer/passes/clear-pass.hpp | 54 + src/antkeeper/renderer/passes/final-pass.cpp | 112 + src/antkeeper/renderer/passes/final-pass.hpp | 61 + .../renderer/passes/material-pass.cpp | 545 + .../renderer/passes/material-pass.hpp | 127 + .../renderer/passes/shadow-map-pass.cpp | 304 + .../renderer/passes/shadow-map-pass.hpp | 89 + src/antkeeper/renderer/passes/sky-pass.cpp | 149 + src/antkeeper/renderer/passes/sky-pass.hpp | 57 + src/antkeeper/renderer/passes/ui-pass.cpp | 99 + src/antkeeper/renderer/passes/ui-pass.hpp | 61 + src/antkeeper/renderer/render-context.hpp | 50 + src/antkeeper/renderer/render-operation.hpp | 49 + src/antkeeper/renderer/render-pass.cpp | 35 + src/antkeeper/renderer/render-pass.hpp | 55 + src/antkeeper/renderer/renderer.cpp | 217 + src/antkeeper/renderer/renderer.hpp | 69 + src/antkeeper/renderer/vertex-attributes.hpp | 51 + .../resources/behavior-tree-loader.cpp | 156 + .../resources/entity-archetype-loader.cpp | 201 + .../resources/image-loader.cpp | 22 +- src/{ => antkeeper}/resources/image.cpp | 32 +- src/{ => antkeeper}/resources/image.hpp | 52 +- src/antkeeper/resources/material-loader.cpp | 423 + .../resources/mesh-loader.cpp} | 51 +- src/antkeeper/resources/model-loader.cpp | 289 + .../resources/resource-handle.cpp} | 16 +- .../resources/resource-handle.hpp | 34 +- .../resources/resource-loader.hpp | 18 +- .../resources/resource-manager.cpp | 32 +- .../resources/resource-manager.hpp | 67 +- .../resources/shader-program-loader.cpp | 225 + .../resources/string-table-loader.cpp | 22 +- .../resources/string-table.cpp | 16 +- .../resources/string-table.hpp | 24 +- .../resources/text-file-loader.cpp | 18 +- src/{ => antkeeper}/resources/text-file.hpp | 12 +- src/antkeeper/resources/texture-2d-loader.cpp | 70 + src/antkeeper/scene/ambient-light.hpp | 38 + src/antkeeper/scene/billboard.cpp | 65 + src/antkeeper/scene/billboard.hpp | 97 + src/antkeeper/scene/camera.cpp | 178 + src/antkeeper/scene/camera.hpp | 283 + src/antkeeper/scene/directional-light.cpp | 46 + src/antkeeper/scene/directional-light.hpp | 65 + src/antkeeper/scene/light.cpp | 54 + src/antkeeper/scene/light.hpp | 102 + src/antkeeper/scene/lod-group.cpp | 75 + src/antkeeper/scene/lod-group.hpp | 118 + src/antkeeper/scene/model-instance.cpp | 101 + src/antkeeper/scene/model-instance.hpp | 130 + src/antkeeper/scene/point-light.cpp | 38 + src/antkeeper/scene/point-light.hpp | 69 + src/antkeeper/scene/scene-object.cpp | 63 + src/antkeeper/scene/scene-object.hpp | 239 + src/antkeeper/scene/scene.cpp | 50 + src/antkeeper/scene/scene.hpp | 80 + src/antkeeper/scene/spotlight.cpp | 64 + src/antkeeper/scene/spotlight.hpp | 132 + src/antkeeper/sdf.hpp | 72 + src/antkeeper/state/application-states.hpp | 39 + src/antkeeper/state/fsm.cpp | 46 + src/antkeeper/state/fsm.hpp | 67 + .../state/language-select-state.cpp} | 24 +- src/antkeeper/state/loading-state.cpp | 41 + .../state/pause-state.cpp} | 22 +- src/antkeeper/state/play-state.cpp | 240 + src/antkeeper/state/splash-state.cpp | 50 + src/antkeeper/state/title-state.cpp | 53 + src/antkeeper/systems/behavior-system.cpp | 44 + src/antkeeper/systems/behavior-system.hpp | 34 + src/antkeeper/systems/camera-system.cpp | 124 + src/antkeeper/systems/camera-system.hpp | 52 + src/antkeeper/systems/collision-system.cpp | 44 + src/antkeeper/systems/collision-system.hpp | 42 + src/antkeeper/systems/control-system.cpp | 265 + src/antkeeper/systems/control-system.hpp | 186 + .../systems/entity-system.cpp} | 52 +- src/antkeeper/systems/entity-system.hpp | 39 + src/antkeeper/systems/locomotion-system.cpp | 38 + src/antkeeper/systems/locomotion-system.hpp | 34 + src/antkeeper/systems/model-system.cpp | 89 + src/antkeeper/systems/model-system.hpp | 47 + src/antkeeper/systems/nest-system.cpp | 68 + src/antkeeper/systems/nest-system.hpp | 44 + src/antkeeper/systems/placement-system.cpp | 77 + src/antkeeper/systems/placement-system.hpp | 34 + src/antkeeper/systems/samara-system.cpp | 57 + src/antkeeper/systems/samara-system.hpp | 33 + src/antkeeper/systems/subterrain-system.cpp | 486 + src/antkeeper/systems/subterrain-system.hpp | 130 + src/antkeeper/systems/terrain-system.cpp | 349 + src/antkeeper/systems/terrain-system.hpp | 61 + src/antkeeper/systems/tool-system.cpp | 158 + src/antkeeper/systems/tool-system.hpp | 56 + src/antkeeper/systems/ui-system.cpp | 160 + src/antkeeper/systems/ui-system.hpp | 102 + .../systems/updatable-system.cpp} | 15 +- src/antkeeper/systems/updatable-system.hpp | 40 + src/antkeeper/systems/vegetation-system.cpp | 179 + src/antkeeper/systems/vegetation-system.hpp | 73 + src/{ => antkeeper}/timestamp.cpp | 11 +- src/{ => antkeeper}/timestamp.hpp | 16 +- src/configuration.hpp.in | 28 - src/debug/ansi-escape-codes.cpp | 66 - src/debug/ansi-escape-codes.hpp | 72 - src/debug/command-interpreter.cpp | 134 - src/debug/command-interpreter.hpp | 185 - src/debug/logger.cpp | 107 - src/debug/logger.hpp | 69 - src/entity/component-manager.cpp | 81 - src/entity/component-manager.hpp | 141 - src/entity/component-observer.cpp | 33 - src/entity/component-observer.hpp | 69 - src/entity/component.hpp | 77 - src/entity/components/animation-component.hpp | 42 - src/entity/components/ant-hill-component.hpp | 36 - src/entity/components/behavior-component.cpp | 29 - src/entity/components/behavior-component.hpp | 41 - src/entity/components/camera-component.hpp | 38 - src/entity/components/collision-component.cpp | 30 - src/entity/components/collision-component.hpp | 42 - src/entity/components/component-type.hpp | 41 - .../legged-locomotion-component.cpp | 34 - .../legged-locomotion-component.hpp | 45 - src/entity/components/model-component.cpp | 29 - src/entity/components/model-component.hpp | 37 - .../components/orbit-constraint-component.cpp | 32 - .../components/orbit-constraint-component.hpp | 42 - .../components/sound-source-component.cpp | 28 - .../components/sound-source-component.hpp | 39 - src/entity/components/steering-component.hpp | 56 - .../components/terrain-patch-component.cpp | 30 - .../components/terrain-patch-component.hpp | 40 - src/entity/components/tool-component.cpp | 30 - src/entity/components/tool-component.hpp | 36 - src/entity/components/transform-component.hpp | 38 - src/entity/entity-group-member.hpp | 43 - src/entity/entity-group-observer.hpp | 52 - src/entity/entity-group.cpp | 59 - src/entity/entity-group.hpp | 268 - src/entity/entity-id-pool.cpp | 68 - src/entity/entity-id-pool.hpp | 87 - src/entity/entity-manager.cpp | 69 - src/entity/entity-manager.hpp | 97 - src/entity/entity-template.cpp | 54 - src/entity/entity-template.hpp | 60 - src/entity/system-manager.cpp | 45 - src/entity/system-manager.hpp | 68 - src/entity/system.hpp | 56 - src/entity/systems/animation-system.cpp | 48 - src/entity/systems/animation-system.hpp | 46 - src/entity/systems/behavior-system.cpp | 181 - src/entity/systems/behavior-system.hpp | 61 - src/entity/systems/collision-system.cpp | 65 - src/entity/systems/collision-system.hpp | 46 - src/entity/systems/constraint-system.cpp | 58 - src/entity/systems/constraint-system.hpp | 47 - src/entity/systems/locomotion-system.cpp | 107 - src/entity/systems/locomotion-system.hpp | 52 - src/entity/systems/particle-system.cpp | 161 - src/entity/systems/particle-system.hpp | 86 - src/entity/systems/render-system.cpp | 84 - src/entity/systems/render-system.hpp | 61 - src/entity/systems/sound-system.cpp | 121 - src/entity/systems/sound-system.hpp | 56 - src/entity/systems/steering-system.cpp | 96 - src/entity/systems/steering-system.hpp | 53 - src/entity/systems/terrain-system.cpp | 321 - src/entity/systems/terrain-system.hpp | 105 - src/entity/systems/tool-system.cpp | 95 - src/entity/systems/tool-system.hpp | 63 - src/game.cpp | 3636 --- src/game.hpp | 585 - src/game/brush.cpp | 168 - src/game/brush.hpp | 67 - src/game/camera-rig.cpp | 251 - src/game/camera-rig.hpp | 274 - src/game/curl-noise.cpp | 40 - src/game/forceps.cpp | 393 - src/game/forceps.hpp | 95 - src/game/lens.cpp | 176 - src/game/lens.hpp | 96 - src/game/tool.cpp | 47 - src/game/tool.hpp | 112 - src/glad/glad.c | 1138 + src/glad/glad.h | 2135 ++ src/glad/khrplatform.h | 290 + src/graphics/clear-render-pass.cpp | 86 - src/graphics/clear-render-pass.hpp | 51 - src/graphics/final-render-pass.cpp | 142 - src/graphics/final-render-pass.hpp | 52 - src/graphics/lighting-render-pass.cpp | 401 - src/graphics/lighting-render-pass.hpp | 93 - src/graphics/shadow-map-render-pass.cpp | 278 - src/graphics/shadow-map-render-pass.hpp | 74 - src/graphics/silhouette-render-pass.cpp | 154 - src/graphics/silhouette-render-pass.hpp | 57 - src/graphics/sky-render-pass.cpp | 163 - src/graphics/sky-render-pass.hpp | 54 - src/graphics/ui-render-pass.cpp | 140 - src/graphics/ui-render-pass.hpp | 52 - src/graphics/vertex-format.hpp | 33 - src/menu.cpp | 131 - src/menu.hpp | 127 - src/nlohmann/json.hpp | 22684 ++++++++++++++++ src/resources/entity-template-loader.cpp | 214 - src/resources/material-loader.cpp | 464 - src/resources/model-loader.cpp | 586 - src/resources/parameter-dict-loader.cpp | 75 - src/resources/shader-loader.cpp | 103 - src/resources/texture-2d-loader.cpp | 108 - src/resources/texture-cube-loader.cpp | 377 - src/resources/typeface-loader.cpp | 30 - src/scheduled-function-event.cpp | 29 - src/scheduled-function-event.hpp | 41 - src/state-machine.cpp | 45 - src/state-machine.hpp | 73 - src/states/game-state.hpp | 49 - src/states/language-select-state.cpp | 88 - src/states/play-state.cpp | 42 - src/states/sandbox-state.cpp | 402 - src/states/sandbox-state.hpp | 58 - src/states/splash-state.cpp | 53 - src/states/title-state.cpp | 84 - src/stb/stb_image.cpp | 6 +- src/stb/stb_image.h | 13510 ++++----- src/stb/stb_image_write.cpp | 6 +- src/triangle-mesh-operations.cpp | 148 - src/triangle-mesh-operations.hpp | 69 - src/ui/ui.cpp | 459 - src/ui/ui.hpp | 553 - 376 files changed, 59576 insertions(+), 25068 deletions(-) delete mode 100644 .gitignore delete mode 100644 COPYING create mode 100644 LICENSE.md rename src/{states/game-state.cpp => antkeeper/animation/animation.cpp} (56%) create mode 100644 src/antkeeper/animation/animation.hpp create mode 100644 src/antkeeper/animation/easings.hpp create mode 100644 src/antkeeper/animation/timeline.cpp create mode 100644 src/antkeeper/animation/timeline.hpp create mode 100644 src/antkeeper/animation/tween.hpp create mode 100644 src/antkeeper/application.cpp create mode 100644 src/antkeeper/application.hpp create mode 100644 src/antkeeper/behavior/behavior-tree.hpp create mode 100644 src/antkeeper/behavior/ebt.cpp create mode 100644 src/antkeeper/behavior/ebt.hpp create mode 100644 src/antkeeper/camera-rig.cpp create mode 100644 src/antkeeper/camera-rig.hpp create mode 100644 src/antkeeper/configuration.hpp.in create mode 100644 src/antkeeper/debug/ansi-codes.hpp create mode 100644 src/antkeeper/debug/cli.hpp create mode 100644 src/antkeeper/debug/logger.cpp create mode 100644 src/antkeeper/debug/logger.hpp create mode 100644 src/antkeeper/debug/performance-sampler.cpp create mode 100644 src/antkeeper/debug/performance-sampler.hpp rename src/{game/curl-noise.hpp => antkeeper/entity/archetype.hpp} (50%) create mode 100644 src/antkeeper/entity/components/behavior-component.hpp create mode 100644 src/antkeeper/entity/components/cavity-component.hpp create mode 100644 src/antkeeper/entity/components/collision-component.hpp create mode 100644 src/antkeeper/entity/components/locomotion-component.hpp create mode 100644 src/antkeeper/entity/components/model-component.hpp create mode 100644 src/antkeeper/entity/components/nest-component.hpp create mode 100644 src/antkeeper/entity/components/placement-component.hpp create mode 100644 src/antkeeper/entity/components/samara-component.hpp create mode 100644 src/antkeeper/entity/components/terrain-component.hpp create mode 100644 src/antkeeper/entity/components/tool-component.hpp create mode 100644 src/antkeeper/entity/components/transform-component.hpp create mode 100644 src/antkeeper/event/event-dispatcher.cpp create mode 100644 src/antkeeper/event/event-dispatcher.hpp create mode 100644 src/antkeeper/event/event-handler.hpp create mode 100644 src/antkeeper/event/event.hpp rename src/{ => antkeeper}/filesystem.cpp (66%) rename src/{ => antkeeper}/filesystem.hpp (50%) create mode 100644 src/antkeeper/frame-scheduler.cpp create mode 100644 src/antkeeper/frame-scheduler.hpp create mode 100644 src/antkeeper/geometry/aabb.hpp create mode 100644 src/antkeeper/geometry/bounding-volume.hpp create mode 100644 src/antkeeper/geometry/convex-hull.hpp create mode 100644 src/antkeeper/geometry/csg.cpp create mode 100644 src/antkeeper/geometry/csg.hpp create mode 100644 src/antkeeper/geometry/intersection.cpp create mode 100644 src/antkeeper/geometry/intersection.hpp create mode 100644 src/antkeeper/geometry/mesh-accelerator.cpp create mode 100644 src/antkeeper/geometry/mesh-accelerator.hpp create mode 100644 src/antkeeper/geometry/mesh-functions.cpp create mode 100644 src/antkeeper/geometry/mesh-functions.hpp create mode 100644 src/antkeeper/geometry/mesh.cpp create mode 100644 src/antkeeper/geometry/mesh.hpp create mode 100644 src/antkeeper/geometry/plane.hpp create mode 100644 src/antkeeper/geometry/ray.hpp create mode 100644 src/antkeeper/geometry/sphere.hpp create mode 100644 src/antkeeper/geometry/view-frustum.hpp create mode 100644 src/antkeeper/input/control-set.cpp create mode 100644 src/antkeeper/input/control-set.hpp create mode 100644 src/antkeeper/input/control.cpp create mode 100644 src/antkeeper/input/control.hpp create mode 100644 src/antkeeper/input/game-controller.cpp create mode 100644 src/antkeeper/input/game-controller.hpp create mode 100644 src/antkeeper/input/input-device.cpp create mode 100644 src/antkeeper/input/input-device.hpp create mode 100644 src/antkeeper/input/input-event-router.cpp create mode 100644 src/antkeeper/input/input-event-router.hpp create mode 100644 src/antkeeper/input/input-events.cpp create mode 100644 src/antkeeper/input/input-events.hpp create mode 100644 src/antkeeper/input/input-mapper.cpp create mode 100644 src/antkeeper/input/input-mapper.hpp create mode 100644 src/antkeeper/input/input-mapping.cpp create mode 100644 src/antkeeper/input/input-mapping.hpp create mode 100644 src/antkeeper/input/keyboard.cpp create mode 100644 src/antkeeper/input/keyboard.hpp create mode 100644 src/antkeeper/input/mouse.cpp create mode 100644 src/antkeeper/input/mouse.hpp create mode 100644 src/antkeeper/input/scancode.hpp create mode 100644 src/antkeeper/input/sdl-game-controller-tables.cpp create mode 100644 src/antkeeper/input/sdl-game-controller-tables.hpp create mode 100644 src/antkeeper/input/sdl-scancode-table.cpp create mode 100644 src/antkeeper/input/sdl-scancode-table.hpp rename src/{ => antkeeper}/main.cpp (63%) create mode 100644 src/antkeeper/marching-cubes.cpp create mode 100644 src/antkeeper/marching-cubes.hpp create mode 100644 src/antkeeper/math.hpp create mode 100644 src/antkeeper/morton.cpp create mode 100644 src/antkeeper/morton.hpp create mode 100644 src/antkeeper/nest.cpp create mode 100644 src/antkeeper/nest.hpp create mode 100644 src/antkeeper/octree.hpp create mode 100644 src/antkeeper/orbit-cam.cpp create mode 100644 src/antkeeper/orbit-cam.hpp create mode 100644 src/antkeeper/pheromone-matrix.cpp create mode 100644 src/antkeeper/pheromone-matrix.hpp rename src/{ => antkeeper/platform/windows}/dpi-aware.manifest (95%) create mode 100644 src/antkeeper/platform/windows/icon.rc.in create mode 100644 src/antkeeper/rasterizer/buffer-usage.cpp create mode 100644 src/antkeeper/rasterizer/buffer-usage.hpp create mode 100644 src/antkeeper/rasterizer/drawing-mode.hpp create mode 100644 src/antkeeper/rasterizer/element-array-type.hpp create mode 100644 src/antkeeper/rasterizer/framebuffer.cpp create mode 100644 src/antkeeper/rasterizer/framebuffer.hpp create mode 100644 src/antkeeper/rasterizer/pixel-format.hpp rename src/{entity/components/transform-component.cpp => antkeeper/rasterizer/pixel-type.hpp} (50%) create mode 100644 src/antkeeper/rasterizer/rasterizer.cpp create mode 100644 src/antkeeper/rasterizer/rasterizer.hpp create mode 100644 src/antkeeper/rasterizer/shader-input.cpp create mode 100644 src/antkeeper/rasterizer/shader-input.hpp create mode 100644 src/antkeeper/rasterizer/shader-program.cpp create mode 100644 src/antkeeper/rasterizer/shader-program.hpp rename src/{entity/components/ant-hill-component.cpp => antkeeper/rasterizer/shader-type.hpp} (52%) create mode 100644 src/antkeeper/rasterizer/shader-variable-type.hpp create mode 100644 src/antkeeper/rasterizer/shader.cpp create mode 100644 src/antkeeper/rasterizer/shader.hpp create mode 100644 src/antkeeper/rasterizer/texture-2d.cpp create mode 100644 src/antkeeper/rasterizer/texture-2d.hpp create mode 100644 src/antkeeper/rasterizer/texture-cube.cpp create mode 100644 src/antkeeper/rasterizer/texture-cube.hpp create mode 100644 src/antkeeper/rasterizer/texture-filter.hpp rename src/{entity/entity-id.hpp => antkeeper/rasterizer/texture-wrapping.hpp} (51%) create mode 100644 src/antkeeper/rasterizer/vertex-array.cpp create mode 100644 src/antkeeper/rasterizer/vertex-array.hpp create mode 100644 src/antkeeper/rasterizer/vertex-attribute-type.hpp create mode 100644 src/antkeeper/rasterizer/vertex-buffer.cpp create mode 100644 src/antkeeper/rasterizer/vertex-buffer.hpp create mode 100644 src/antkeeper/renderer/compositor.cpp create mode 100644 src/antkeeper/renderer/compositor.hpp create mode 100644 src/antkeeper/renderer/material-flags.hpp create mode 100644 src/antkeeper/renderer/material-property.cpp create mode 100644 src/antkeeper/renderer/material-property.hpp create mode 100644 src/antkeeper/renderer/material.cpp create mode 100644 src/antkeeper/renderer/material.hpp create mode 100644 src/antkeeper/renderer/model.cpp create mode 100644 src/antkeeper/renderer/model.hpp create mode 100644 src/antkeeper/renderer/passes/bloom-pass.cpp create mode 100644 src/antkeeper/renderer/passes/bloom-pass.hpp create mode 100644 src/antkeeper/renderer/passes/clear-pass.cpp create mode 100644 src/antkeeper/renderer/passes/clear-pass.hpp create mode 100644 src/antkeeper/renderer/passes/final-pass.cpp create mode 100644 src/antkeeper/renderer/passes/final-pass.hpp create mode 100644 src/antkeeper/renderer/passes/material-pass.cpp create mode 100644 src/antkeeper/renderer/passes/material-pass.hpp create mode 100644 src/antkeeper/renderer/passes/shadow-map-pass.cpp create mode 100644 src/antkeeper/renderer/passes/shadow-map-pass.hpp create mode 100644 src/antkeeper/renderer/passes/sky-pass.cpp create mode 100644 src/antkeeper/renderer/passes/sky-pass.hpp create mode 100644 src/antkeeper/renderer/passes/ui-pass.cpp create mode 100644 src/antkeeper/renderer/passes/ui-pass.hpp create mode 100644 src/antkeeper/renderer/render-context.hpp create mode 100644 src/antkeeper/renderer/render-operation.hpp create mode 100644 src/antkeeper/renderer/render-pass.cpp create mode 100644 src/antkeeper/renderer/render-pass.hpp create mode 100644 src/antkeeper/renderer/renderer.cpp create mode 100644 src/antkeeper/renderer/renderer.hpp create mode 100644 src/antkeeper/renderer/vertex-attributes.hpp create mode 100644 src/antkeeper/resources/behavior-tree-loader.cpp create mode 100644 src/antkeeper/resources/entity-archetype-loader.cpp rename src/{ => antkeeper}/resources/image-loader.cpp (73%) rename src/{ => antkeeper}/resources/image.cpp (67%) rename src/{ => antkeeper}/resources/image.hpp (64%) create mode 100644 src/antkeeper/resources/material-loader.cpp rename src/{resources/triangle-mesh-loader.cpp => antkeeper/resources/mesh-loader.cpp} (55%) create mode 100644 src/antkeeper/resources/model-loader.cpp rename src/{entity/system.cpp => antkeeper/resources/resource-handle.cpp} (55%) rename src/{ => antkeeper}/resources/resource-handle.hpp (58%) rename src/{ => antkeeper}/resources/resource-loader.hpp (70%) rename src/{ => antkeeper}/resources/resource-manager.cpp (51%) rename src/{ => antkeeper}/resources/resource-manager.hpp (63%) create mode 100644 src/antkeeper/resources/shader-program-loader.cpp rename src/{ => antkeeper}/resources/string-table-loader.cpp (72%) rename src/{ => antkeeper}/resources/string-table.cpp (59%) rename src/{ => antkeeper}/resources/string-table.hpp (57%) rename src/{ => antkeeper}/resources/text-file-loader.cpp (57%) rename src/{ => antkeeper}/resources/text-file.hpp (64%) create mode 100644 src/antkeeper/resources/texture-2d-loader.cpp create mode 100644 src/antkeeper/scene/ambient-light.hpp create mode 100644 src/antkeeper/scene/billboard.cpp create mode 100644 src/antkeeper/scene/billboard.hpp create mode 100644 src/antkeeper/scene/camera.cpp create mode 100644 src/antkeeper/scene/camera.hpp create mode 100644 src/antkeeper/scene/directional-light.cpp create mode 100644 src/antkeeper/scene/directional-light.hpp create mode 100644 src/antkeeper/scene/light.cpp create mode 100644 src/antkeeper/scene/light.hpp create mode 100644 src/antkeeper/scene/lod-group.cpp create mode 100644 src/antkeeper/scene/lod-group.hpp create mode 100644 src/antkeeper/scene/model-instance.cpp create mode 100644 src/antkeeper/scene/model-instance.hpp create mode 100644 src/antkeeper/scene/point-light.cpp create mode 100644 src/antkeeper/scene/point-light.hpp create mode 100644 src/antkeeper/scene/scene-object.cpp create mode 100644 src/antkeeper/scene/scene-object.hpp create mode 100644 src/antkeeper/scene/scene.cpp create mode 100644 src/antkeeper/scene/scene.hpp create mode 100644 src/antkeeper/scene/spotlight.cpp create mode 100644 src/antkeeper/scene/spotlight.hpp create mode 100644 src/antkeeper/sdf.hpp create mode 100644 src/antkeeper/state/application-states.hpp create mode 100644 src/antkeeper/state/fsm.cpp create mode 100644 src/antkeeper/state/fsm.hpp rename src/{entity/components/camera-component.cpp => antkeeper/state/language-select-state.cpp} (51%) create mode 100644 src/antkeeper/state/loading-state.cpp rename src/{entity/components/steering-component.cpp => antkeeper/state/pause-state.cpp} (52%) create mode 100644 src/antkeeper/state/play-state.cpp create mode 100644 src/antkeeper/state/splash-state.cpp create mode 100644 src/antkeeper/state/title-state.cpp create mode 100644 src/antkeeper/systems/behavior-system.cpp create mode 100644 src/antkeeper/systems/behavior-system.hpp create mode 100644 src/antkeeper/systems/camera-system.cpp create mode 100644 src/antkeeper/systems/camera-system.hpp create mode 100644 src/antkeeper/systems/collision-system.cpp create mode 100644 src/antkeeper/systems/collision-system.hpp create mode 100644 src/antkeeper/systems/control-system.cpp create mode 100644 src/antkeeper/systems/control-system.hpp rename src/{states/loading-state.cpp => antkeeper/systems/entity-system.cpp} (55%) create mode 100644 src/antkeeper/systems/entity-system.hpp create mode 100644 src/antkeeper/systems/locomotion-system.cpp create mode 100644 src/antkeeper/systems/locomotion-system.hpp create mode 100644 src/antkeeper/systems/model-system.cpp create mode 100644 src/antkeeper/systems/model-system.hpp create mode 100644 src/antkeeper/systems/nest-system.cpp create mode 100644 src/antkeeper/systems/nest-system.hpp create mode 100644 src/antkeeper/systems/placement-system.cpp create mode 100644 src/antkeeper/systems/placement-system.hpp create mode 100644 src/antkeeper/systems/samara-system.cpp create mode 100644 src/antkeeper/systems/samara-system.hpp create mode 100644 src/antkeeper/systems/subterrain-system.cpp create mode 100644 src/antkeeper/systems/subterrain-system.hpp create mode 100644 src/antkeeper/systems/terrain-system.cpp create mode 100644 src/antkeeper/systems/terrain-system.hpp create mode 100644 src/antkeeper/systems/tool-system.cpp create mode 100644 src/antkeeper/systems/tool-system.hpp create mode 100644 src/antkeeper/systems/ui-system.cpp create mode 100644 src/antkeeper/systems/ui-system.hpp rename src/{resources/resource-handle.cpp => antkeeper/systems/updatable-system.cpp} (56%) create mode 100644 src/antkeeper/systems/updatable-system.hpp create mode 100644 src/antkeeper/systems/vegetation-system.cpp create mode 100644 src/antkeeper/systems/vegetation-system.hpp rename src/{ => antkeeper}/timestamp.cpp (81%) rename src/{ => antkeeper}/timestamp.hpp (62%) delete mode 100644 src/configuration.hpp.in delete mode 100644 src/debug/ansi-escape-codes.cpp delete mode 100644 src/debug/ansi-escape-codes.hpp delete mode 100644 src/debug/command-interpreter.cpp delete mode 100644 src/debug/command-interpreter.hpp delete mode 100644 src/debug/logger.cpp delete mode 100644 src/debug/logger.hpp delete mode 100644 src/entity/component-manager.cpp delete mode 100644 src/entity/component-manager.hpp delete mode 100644 src/entity/component-observer.cpp delete mode 100644 src/entity/component-observer.hpp delete mode 100644 src/entity/component.hpp delete mode 100644 src/entity/components/animation-component.hpp delete mode 100644 src/entity/components/ant-hill-component.hpp delete mode 100644 src/entity/components/behavior-component.cpp delete mode 100644 src/entity/components/behavior-component.hpp delete mode 100644 src/entity/components/camera-component.hpp delete mode 100644 src/entity/components/collision-component.cpp delete mode 100644 src/entity/components/collision-component.hpp delete mode 100644 src/entity/components/component-type.hpp delete mode 100644 src/entity/components/legged-locomotion-component.cpp delete mode 100644 src/entity/components/legged-locomotion-component.hpp delete mode 100644 src/entity/components/model-component.cpp delete mode 100644 src/entity/components/model-component.hpp delete mode 100644 src/entity/components/orbit-constraint-component.cpp delete mode 100644 src/entity/components/orbit-constraint-component.hpp delete mode 100644 src/entity/components/sound-source-component.cpp delete mode 100644 src/entity/components/sound-source-component.hpp delete mode 100644 src/entity/components/steering-component.hpp delete mode 100644 src/entity/components/terrain-patch-component.cpp delete mode 100644 src/entity/components/terrain-patch-component.hpp delete mode 100644 src/entity/components/tool-component.cpp delete mode 100644 src/entity/components/tool-component.hpp delete mode 100644 src/entity/components/transform-component.hpp delete mode 100644 src/entity/entity-group-member.hpp delete mode 100644 src/entity/entity-group-observer.hpp delete mode 100644 src/entity/entity-group.cpp delete mode 100644 src/entity/entity-group.hpp delete mode 100644 src/entity/entity-id-pool.cpp delete mode 100644 src/entity/entity-id-pool.hpp delete mode 100644 src/entity/entity-manager.cpp delete mode 100644 src/entity/entity-manager.hpp delete mode 100644 src/entity/entity-template.cpp delete mode 100644 src/entity/entity-template.hpp delete mode 100644 src/entity/system-manager.cpp delete mode 100644 src/entity/system-manager.hpp delete mode 100644 src/entity/system.hpp delete mode 100644 src/entity/systems/animation-system.cpp delete mode 100644 src/entity/systems/animation-system.hpp delete mode 100644 src/entity/systems/behavior-system.cpp delete mode 100644 src/entity/systems/behavior-system.hpp delete mode 100644 src/entity/systems/collision-system.cpp delete mode 100644 src/entity/systems/collision-system.hpp delete mode 100644 src/entity/systems/constraint-system.cpp delete mode 100644 src/entity/systems/constraint-system.hpp delete mode 100644 src/entity/systems/locomotion-system.cpp delete mode 100644 src/entity/systems/locomotion-system.hpp delete mode 100644 src/entity/systems/particle-system.cpp delete mode 100644 src/entity/systems/particle-system.hpp delete mode 100644 src/entity/systems/render-system.cpp delete mode 100644 src/entity/systems/render-system.hpp delete mode 100644 src/entity/systems/sound-system.cpp delete mode 100644 src/entity/systems/sound-system.hpp delete mode 100644 src/entity/systems/steering-system.cpp delete mode 100644 src/entity/systems/steering-system.hpp delete mode 100644 src/entity/systems/terrain-system.cpp delete mode 100644 src/entity/systems/terrain-system.hpp delete mode 100644 src/entity/systems/tool-system.cpp delete mode 100644 src/entity/systems/tool-system.hpp delete mode 100644 src/game.cpp delete mode 100644 src/game.hpp delete mode 100644 src/game/brush.cpp delete mode 100644 src/game/brush.hpp delete mode 100644 src/game/camera-rig.cpp delete mode 100644 src/game/camera-rig.hpp delete mode 100644 src/game/curl-noise.cpp delete mode 100644 src/game/forceps.cpp delete mode 100644 src/game/forceps.hpp delete mode 100644 src/game/lens.cpp delete mode 100644 src/game/lens.hpp delete mode 100644 src/game/tool.cpp delete mode 100644 src/game/tool.hpp create mode 100644 src/glad/glad.c create mode 100644 src/glad/glad.h create mode 100644 src/glad/khrplatform.h delete mode 100644 src/graphics/clear-render-pass.cpp delete mode 100644 src/graphics/clear-render-pass.hpp delete mode 100644 src/graphics/final-render-pass.cpp delete mode 100644 src/graphics/final-render-pass.hpp delete mode 100644 src/graphics/lighting-render-pass.cpp delete mode 100644 src/graphics/lighting-render-pass.hpp delete mode 100644 src/graphics/shadow-map-render-pass.cpp delete mode 100644 src/graphics/shadow-map-render-pass.hpp delete mode 100644 src/graphics/silhouette-render-pass.cpp delete mode 100644 src/graphics/silhouette-render-pass.hpp delete mode 100644 src/graphics/sky-render-pass.cpp delete mode 100644 src/graphics/sky-render-pass.hpp delete mode 100644 src/graphics/ui-render-pass.cpp delete mode 100644 src/graphics/ui-render-pass.hpp delete mode 100644 src/graphics/vertex-format.hpp delete mode 100644 src/menu.cpp delete mode 100644 src/menu.hpp create mode 100644 src/nlohmann/json.hpp delete mode 100644 src/resources/entity-template-loader.cpp delete mode 100644 src/resources/material-loader.cpp delete mode 100644 src/resources/model-loader.cpp delete mode 100644 src/resources/parameter-dict-loader.cpp delete mode 100644 src/resources/shader-loader.cpp delete mode 100644 src/resources/texture-2d-loader.cpp delete mode 100644 src/resources/texture-cube-loader.cpp delete mode 100644 src/resources/typeface-loader.cpp delete mode 100644 src/scheduled-function-event.cpp delete mode 100644 src/scheduled-function-event.hpp delete mode 100644 src/state-machine.cpp delete mode 100644 src/state-machine.hpp delete mode 100644 src/states/game-state.hpp delete mode 100644 src/states/language-select-state.cpp delete mode 100644 src/states/play-state.cpp delete mode 100644 src/states/sandbox-state.cpp delete mode 100644 src/states/sandbox-state.hpp delete mode 100644 src/states/splash-state.cpp delete mode 100644 src/states/title-state.cpp delete mode 100644 src/triangle-mesh-operations.cpp delete mode 100644 src/triangle-mesh-operations.hpp delete mode 100644 src/ui/ui.cpp delete mode 100644 src/ui/ui.hpp diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 52987dd..0000000 --- a/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -CMakeFiles -cmake_install.cmake -CMakeCache.txt -Makefile -tags diff --git a/CMakeLists.txt b/CMakeLists.txt index b28aaa2..19fa214 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,97 +1,122 @@ -# Prevent in-source builds -if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) - message(FATAL_ERROR "In-source builds prohibited. Call cmake from the build directory.") -endif() - -cmake_minimum_required(VERSION 3.7) - -# Set compiler flags -if(CMAKE_COMPILER_IS_GNUCC) - set(CMAKE_CXX_FLAGS "-Wall -Wextra") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -g") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -O3") -elseif(MSVC) - set(CMAKE_CXX_FLAGS "/W3 /MP /MD") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS}") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} /Ox") -endif() - -# Include project macro -include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/project.cmake) - -# Find dependency packages -find_package(emergent REQUIRED CONFIG) -find_package(OpenAL REQUIRED CONFIG) - -# Determine dependencies -set(STATIC_LIBS - emergent) -set(SHARED_LIBS - OpenAL::OpenAL) - -# Generate configuration header file -configure_file(${PROJECT_SOURCE_DIR}/src/configuration.hpp.in - ${PROJECT_BINARY_DIR}/src/configuration.hpp) - -# Collect source files -file(GLOB_RECURSE SOURCE_FILES - ${PROJECT_SOURCE_DIR}/src/*.cpp) - -# Make DPI-aware on Windows -if(MSVC) - list(APPEND SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/dpi-aware.manifest") -endif() - -# Add executable target -set(EXECUTABLE_TARGET ${PROJECT_NAME}-executable) -add_executable(${EXECUTABLE_TARGET} ${SOURCE_FILES}) -set_target_properties(${EXECUTABLE_TARGET} PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) - -# Add compile definitions -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - target_compile_definitions(${EXECUTABLE_TARGET} PRIVATE DEBUG) -else() - target_compile_definitions(${EXECUTABLE_TARGET} PRIVATE NDEBUG) -endif() - -# Set C++17 standard -set_target_properties(${EXECUTABLE_TARGET} PROPERTIES - CXX_STANDARD 17 - CXX_EXTENSIONS OFF) -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set_target_properties(${EXECUTABLE_TARGET} PROPERTIES COMPILE_FLAGS "-std=c++17") -elseif(MSVC) - set_target_properties(${EXECUTABLE_TARGET} PROPERTIES COMPILE_FLAGS "/std:c++17") -endif() - -# Set link flags to show console window on debug builds and hide it on release builds -if(MSVC) - set_target_properties(${EXECUTABLE_TARGET} PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") - set_target_properties(${EXECUTABLE_TARGET} PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:\"mainCRTStartup\"") -endif(MSVC) - -# Set include directories -target_include_directories(${EXECUTABLE_TARGET} - PUBLIC - ${PROJECT_SOURCE_DIR}/src - ${PROJECT_BINARY_DIR}/src) - -# Link to dependencies -target_link_libraries(${EXECUTABLE_TARGET} ${STATIC_LIBS} ${SHARED_LIBS}) - -# Install executable -if(PACKAGE_PLATFORM MATCHES "linux") - install(TARGETS ${EXECUTABLE_TARGET} DESTINATION bin) -elseif(PACKAGE_PLATFORM MATCHES "win") - # Install executable - install(TARGETS ${EXECUTABLE_TARGET} DESTINATION .) - - # Install OpenAL DLL - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - get_target_property(OPENAL_DLL OpenAL::OpenAL IMPORTED_LOCATION_DEBUG) - else() - get_target_property(OPENAL_DLL OpenAL::OpenAL IMPORTED_LOCATION_RELEASE) - endif() - - install(FILES ${OPENAL_DLL} DESTINATION .) -endif() +# Prevent in-source builds +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + message(FATAL_ERROR "In-source builds prohibited. Call cmake from the build directory.") +endif() + +cmake_minimum_required(VERSION 3.7) + +# Include project macro +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/project.cmake) + +# Set compiler flags +if(CMAKE_COMPILER_IS_GNUCC) + set(CMAKE_CXX_FLAGS "-Wall -Wextra") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -g") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -O3") +elseif(MSVC) + set(CMAKE_CXX_FLAGS "/W3 /MP /MD") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} /Ox") +endif() + + +# Find dependency packages +find_package(vmq REQUIRED CONFIG) +find_package(EnTT REQUIRED CONFIG) +find_package(OpenGL REQUIRED) +find_package(SDL2 REQUIRED COMPONENTS SDL2::SDL2-static SDL2::SDL2main CONFIG) +find_package(OpenAL REQUIRED CONFIG) + +# Determine dependencies +set(STATIC_LIBS + vmq + EnTT + SDL2::SDL2main) +set(SHARED_LIBS + ${OPENGL_gl_LIBRARY} + SDL2::SDL2 + OpenAL::OpenAL) + +# Generate configuration header file +configure_file(${PROJECT_SOURCE_DIR}/src/antkeeper/configuration.hpp.in + ${PROJECT_BINARY_DIR}/src/antkeeper/configuration.hpp) + +# Collect source files +file(GLOB_RECURSE SOURCE_FILES + ${PROJECT_SOURCE_DIR}/src/*.cpp + ${PROJECT_SOURCE_DIR}/src/*.c) + +if(MSVC) + # Generate Windows icon resource file + set(ICON_FILE "${PROJECT_SOURCE_DIR}/../antkeeper-data/src/icons/antkeeper.ico") + configure_file(${PROJECT_SOURCE_DIR}/src/antkeeper/platform/windows/icon.rc.in + ${PROJECT_BINARY_DIR}/src/antkeeper/platform/windows/icon.rc) + + # Add executable icon + list(APPEND SOURCE_FILES "${PROJECT_BINARY_DIR}/src/antkeeper/platform/windows/icon.rc") + + # Make DPI-aware on Windows + list(APPEND SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/antkeeper/platform/windows/dpi-aware.manifest") +endif() + +# Add executable target +set(EXECUTABLE_TARGET ${PROJECT_NAME}-executable) +add_executable(${EXECUTABLE_TARGET} ${SOURCE_FILES}) +set_target_properties(${EXECUTABLE_TARGET} PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) + +# Add compile definitions +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_definitions(${EXECUTABLE_TARGET} PRIVATE DEBUG) +else() + target_compile_definitions(${EXECUTABLE_TARGET} PRIVATE NDEBUG) +endif() + +# Set C++17 standard +set_target_properties(${EXECUTABLE_TARGET} PROPERTIES + CXX_STANDARD 17 + CXX_EXTENSIONS OFF) +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set_target_properties(${EXECUTABLE_TARGET} PROPERTIES COMPILE_FLAGS "-std=c++17") +elseif(MSVC) + set_target_properties(${EXECUTABLE_TARGET} PROPERTIES COMPILE_FLAGS "/std:c++17") +endif() + +# Set link flags to show console window on debug builds and hide it on release builds +if(MSVC) + set_target_properties(${EXECUTABLE_TARGET} PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE") + set_target_properties(${EXECUTABLE_TARGET} PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /ENTRY:\"mainCRTStartup\"") +endif(MSVC) + +# Set include directories +target_include_directories(${EXECUTABLE_TARGET} + PUBLIC + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_SOURCE_DIR}/src/antkeeper + ${PROJECT_BINARY_DIR}/src/antkeeper) + +# Link to dependencies +target_link_libraries(${EXECUTABLE_TARGET} ${STATIC_LIBS} ${SHARED_LIBS}) + +# Install executable +if(PACKAGE_PLATFORM MATCHES "linux") + install(TARGETS ${EXECUTABLE_TARGET} DESTINATION bin) +elseif(PACKAGE_PLATFORM MATCHES "win") + # Install executable + install(TARGETS ${EXECUTABLE_TARGET} DESTINATION .) + + # Install SDL2 DLLs + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + get_target_property(SDL2_DLL SDL2::SDL2 IMPORTED_LOCATION_DEBUG) + else() + get_target_property(SDL2_DLL SDL2::SDL2 IMPORTED_LOCATION_RELEASE) + endif() + install(FILES ${SDL2_DLL} DESTINATION .) + + # Install OpenAL DLL + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + get_target_property(OPENAL_DLL OpenAL::OpenAL IMPORTED_LOCATION_DEBUG) + else() + get_target_property(OPENAL_DLL OpenAL::OpenAL IMPORTED_LOCATION_RELEASE) + endif() + install(FILES ${OPENAL_DLL} DESTINATION .) +endif() diff --git a/COPYING b/COPYING deleted file mode 100644 index 818433e..0000000 --- a/COPYING +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program 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. - - This program 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 this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..2fb2e74 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,675 @@ +### GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +### Preamble + +The GNU General Public License is a free, copyleft license for +software and other kinds of works. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom +to share and change all versions of a program--to make sure it remains +free software for all its users. We, the Free Software Foundation, use +the GNU General Public License for most of our software; it applies +also to any other work released this way by its authors. You can apply +it to your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you +have certain responsibilities if you distribute copies of the +software, or if you modify it: responsibilities to respect the freedom +of others. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + +Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + +Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the +manufacturer can do so. This is fundamentally incompatible with the +aim of protecting users' freedom to change the software. The +systematic pattern of such abuse occurs in the area of products for +individuals to use, which is precisely where it is most unacceptable. +Therefore, we have designed this version of the GPL to prohibit the +practice for those products. If such problems arise substantially in +other domains, we stand ready to extend this provision to those +domains in future versions of the GPL, as needed to protect the +freedom of users. + +Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish +to avoid the special danger that patents applied to a free program +could make it effectively proprietary. To prevent this, the GPL +assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and +modification follow. + +### TERMS AND CONDITIONS + +#### 0. Definitions. + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +#### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +#### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +#### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +#### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +#### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +#### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +#### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +#### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +#### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +#### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +#### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +#### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +#### 13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + +#### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in +detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU General Public +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that numbered version or +of any later version published by the Free Software Foundation. If the +Program does not specify a version number of the GNU General Public +License, you may choose any version ever published by the Free +Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU General Public License can be used, that proxy's public +statement of acceptance of a version permanently authorizes you to +choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +#### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +#### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +#### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively state +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program 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. + + This program 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 this program. If not, see . + +Also add information on how to contact you by electronic and paper +mail. + +If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands \`show w' and \`show c' should show the +appropriate parts of the General Public License. Of course, your +program's commands might be different; for a GUI interface, you would +use an "about box". + +You should also get your employer (if you work as a programmer) or +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU GPL, see . + +The GNU General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Lesser General Public License instead of this License. But first, +please read . diff --git a/README.md b/README.md index c9d3a65..ec0b864 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,54 @@ -# Antkeeper Source · [![GitHub license](https://img.shields.io/github/license/cjhoward/antkeeper.svg)](https://github.com/cjhoward/antkeeper/blob/master/COPYING) [![GitHub release](https://img.shields.io/github/release/cjhoward/antkeeper.svg)](https://github.com/cjhoward/antkeeper/releases/) - -Antkeeper is an ant colony simulation game for Windows, Mac OS X, and GNU/Linux. This repository contains all of the source code to Antkeeper. The game data, however, is proprietary. You can access the game data by purchasing a copy of Antkeeper at [antkeeper.com](https://antkeeper.com/). - -## License - -The source code for Antkeeper is licensed under the GNU General Public License, version 3. See [`COPYING`](./COPYING) for details. - -### 3rd-Party Software - -| Name | Author(s) | License | Files | -| :--------------- | :----------- | :-------------------------- | :---- | -| dr_wav | David Reid | Public Domain | [`dr_wav.h`](./src/dr_libs/dr_wav.h) | -| Emergent | C. J. Howard | GNU GPL v3.0 | | -| OpenAL soft | | GNU GPL v2.0 | | -| stb_image | Sean Barrett | Public Domain / MIT License | [`stb_image.h`](./src/stb/stb_image.h) | -| stb_image_writer | Sean Barrett | Public Domain / MIT License | [`stb_image_writer.h`](./src/stb/stb_image_writer.h) | +# Antkeeper Source + +Antkeeper is an ant colony simulation game currently in development for Windows, Mac OS X, and GNU/Linux. This repository contains all of the source code to Antkeeper. + +Head over to [antkeeper.com](https://antkeeper.com/) if you're interested in following the development of the game or purchasing a copy when it's released. Antkeeper is an indie game with a single developer, so feel free to reach out to me personally with any questions, comments, or feedback you may have. + +## Configuration & Building + +CMake is required to configure and build the application. Depending on the target build platform, CMake should be invoked from one of the following directories: + + build/linux32 // 32-bit GNU/Linux application + build/linux64 // 64-bit GNU/Linux application + build/win32 // 32-bit Windows application + build/win64 // 64-bit Windows application + +The following arguments may be passed to CMake during configuration: + + -DCMAKE_BUILD_TYPE // [Debug, Release] + +### GNU/Linux + +Building on GNU/Linux requires CMake, GCC, G++, and GNU Make. Open a terminal in the project root directory and run the following commands: + + cd build/linux64 + cmake ../.. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=... + cmake --build . + +### Windows + +Building on Windows requires CMake and Visual Studio. Additionally, [NSIS](http://nsis.sourceforge.net/) is required if you want to build a distributable installer program. In order to correctly build for your target architecture, you must use the `x86 Native Tools Command Prompt` or the `x64 Native Tools Command Prompt` for 32-bit and 64-bit applications, respectively. Then navigate to the project root directory and run the following commands: + + cd build\win64 + cmake ..\.. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=... + cmake --build . + +## License + +Antkeeper source code is licensed under the GNU General Public License, version 3. For more information, see [LICENSE.md](./LICENSE.md). + +### 3rd-Party Software + +| Name | Author(s) | License | File(s) | +| :------------------------------ | :--------------------- | :-------------------------- | :------------------------------------------------------- | +| dr_wav | David Reid | Public Domain (Unlicense) | [dr_wav.h](./src/dr_libs/dr_wav.h) | +| Easing Functions (Equations) | Robert Penner | MIT License | [easings.hpp](./src/antkeeper/animation/easings.hpp) | +| EnTT | Michele Caini | MIT License | *Not included in repository* | +| khrplatform.h | The Khronos Group Inc. | MIT License | [khrplatform.h](./src/glad/khrplatform.h) | +| OpenGL loader generated by glad | David Herberth | Public Domain / WTFPL / CC0 | [glad.h](./src/glad/glad.h), [glad.c](./src/glad/glad.c) | +| JSON for Modern C++ | Niels Lohmann | MIT License | [json.hpp](./src/nlohmann/json.hpp) | +| OpenAL Soft | | GNU GPL v2.0 | *Not included in repository* | +| Simple DirectMedia Layer | Sam Lantinga | zlib License | *Not included in repository* | +| stb_image | Sean Barrett | Public Domain / MIT License | [stb_image.h](./src/stb/stb_image.h) | +| stb_image_write | Sean Barrett | Public Domain / MIT License | [stb_image_write.h](./src/stb/stb_image_writer.h) | + diff --git a/build/.gitignore b/build/.gitignore index d6b7ef3..c96a04f 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -1,2 +1,2 @@ * -!.gitignore +!.gitignore \ No newline at end of file diff --git a/src/states/game-state.cpp b/src/antkeeper/animation/animation.cpp similarity index 56% rename from src/states/game-state.cpp rename to src/antkeeper/animation/animation.cpp index db0cea8..1fcb933 100644 --- a/src/states/game-state.cpp +++ b/src/antkeeper/animation/animation.cpp @@ -1,27 +1,29 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#include "game-state.hpp" +#include "animation.hpp" -GameState::GameState(Game* game): - game(game) +void animation_base::animate(float dt) {} -GameState::~GameState() -{} +void animator::animate(float dt) +{ + +} + diff --git a/src/antkeeper/animation/animation.hpp b/src/antkeeper/animation/animation.hpp new file mode 100644 index 0000000..17b7c06 --- /dev/null +++ b/src/antkeeper/animation/animation.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 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_ANIMATION_HPP +#define ANTKEEPER_ANIMATION_HPP + +#include + +class animation_base +{ +public: + void animate(float dt); +}; + +template +class animation: public animation_base +{ +public: + void set_start_callback(std::function callback); + void set_end_callback(std::function callback); + void set_loop_callback(std::function callback); + void set_frame_callback(std::function callback); + +private: + std::function start_callback; + std::function end_callback; + std::function loop_callback; + std::function motion_callback; +}; + +class skeletal_animation: public animation +{ + +}; + +class animator +{ +public: + /** + * Progresses all active animations by @p dt. + * + * @param dt Delta time by which the animations will be progressed. + */ + void animate(float dt); +}; + +#endif // ANTKEEPER_ANIMATION_HPP + diff --git a/src/antkeeper/animation/easings.hpp b/src/antkeeper/animation/easings.hpp new file mode 100644 index 0000000..5c34d78 --- /dev/null +++ b/src/antkeeper/animation/easings.hpp @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2020 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 . + */ + +/* + * Easing Functions (Equations) + * + * Copyright (C) 2001 Robert Penner + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the author nor the names of contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ANTKEEPER_EASINGS_HPP +#define ANTKEEPER_EASINGS_HPP + +#include +#include + +template +inline T ease_linear(const T& x, const T& y, float a) +{ + return (y - x) * a + x; +} + +template +T ease_in_sine(const T& x, const T& y, float a) +{ + return -(y - x) * std::cos(a * vmq::half_pi) + (y - x) + x; +} + +template +T ease_out_sine(const T& x, const T& y, float a) +{ + return (y - x) * std::sin(a * vmq::half_pi) + x; +} + +template +T ease_in_out_sine(const T& x, const T& y, float a) +{ + return -(y - x) * 0.5f * (std::cos(vmq::pi * a) - 1.0f) + x; +} + +template +T ease_in_quad(const T& x, const T& y, float a) +{ + return (y - x) * a * a + x; +} + +template +T ease_out_quad(const T& x, const T& y, float a) +{ + return -(y - x) * a * (a - 2.0f) + x; +} + +template +T ease_in_out_quad(const T& x, const T& y, float a) +{ + a *= 2.0f; + if (a < 1.0f) + { + return (y - x) * 0.5f * a * a + x; + } + + a -= 1.0f; + return -(y - x) * 0.5f * (a * (a - 2.0f) - 1.0f) + x; +} + +template +T ease_in_cubic(const T& x, const T& y, float a) +{ + return (y - x) * a * a * a + x; +} + +template +T ease_out_cubic(const T& x, const T& y, float a) +{ + a -= 1.0f; + return (y - x) * (a * a * a + 1.0f) + x; +} + +template +T ease_in_out_cubic(const T& x, const T& y, float a) +{ + a *= 2.0f; + if (a < 1.0f) + { + return (y - x) * 0.5f * a * a * a + x; + } + + a -= 2.0f; + return (y - x) * 0.5f * (a * a * a + 2.0f) + x; +} + +template +T ease_in_quart(const T& x, const T& y, float a) +{ + return (y - x) * a * a * a * a + x; +} + +template +T ease_out_quart(const T& x, const T& y, float a) +{ + a -= 1.0f; + return -(y - x) * (a * a * a * a - 1.0f) + x; +} + +template +T ease_in_out_quart(const T& x, const T& y, float a) +{ + a *= 2.0f; + if (a < 1.0f) + { + return (y - x) * 0.5f * a * a * a * a + x; + } + + a -= 2.0f; + return -(y - x) * 0.5f * (a * a * a * a - 2.0f) + x; +} + +template +T ease_in_quint(const T& x, const T& y, float a) +{ + return (y - x) * a * a * a * a * a + x; +} + +template +T ease_out_quint(const T& x, const T& y, float a) +{ + a -= 1.0f; + return (y - x) * (a * a * a * a * a + 1.0f) + x; +} + +template +T ease_in_out_quint(const T& x, const T& y, float a) +{ + a *= 2.0f; + if (a < 1.0f) + { + return (y - x) * 0.5f * a * a * a * a * a + x; + } + + a -= 2.0f; + return (y - x) * 0.5f * (a * a * a * a * a + 2.0f) + x; +} + +template +T ease_in_expo(const T& x, const T& y, float a) +{ + if (a == 0.0f) + { + return x; + } + + return (y - x) * std::pow(2.0f, 10.0f * (a - 1.0f)) + x; +} + +template +T ease_out_expo(const T& x, const T& y, float a) +{ + if (a == 1.0f) + { + return y; + } + + return (y - x) * (-std::pow(2.0f, -10.0f * a) + 1.0f) + x; +} + +template +T ease_in_out_expo(const T& x, const T& y, float a) +{ + if (a == 0.0f) + { + return x; + } + else if (a == 1.0f) + { + return y; + } + + a *= 2.0f; + if (a < 1.0f) + { + return (y - x) * 0.5f * std::pow(2.0f, 10.0f * (a - 1.0f)) + x; + } + + a -= 1.0f; + return (y - x) * 0.5f * (-std::pow(2.0f, -10.0f * a) + 2.0f) + x; +} + +template +T ease_in_circ(const T& x, const T& y, float a) +{ + return -(y - x) * (std::sqrt(1.0f - a * a) - 1.0f) + x; +} + +template +T ease_out_circ(const T& x, const T& y, float a) +{ + a -= 1.0f; + return (y - x) * std::sqrt(1.0f - a * a) + x; +} + +template +T ease_in_out_circ(const T& x, const T& y, float a) +{ + a *= 2.0f; + if (a < 1.0f) + { + return -(y - x) * 0.5f * (std::sqrt(1.0f - a * a) - 1.0f) + x; + } + + a -= 2.0f; + return (y - x) * 0.5f * (std::sqrt(1.0f - a * a) + 1.0f) + x; +} + +template +T ease_in_back(const T& x, const T& y, float a) +{ + const float s = 1.70158f; + return (y - x) * a * a * ((s + 1.0f) * a - s) + x; +} + +template +T ease_out_back(const T& x, const T& y, float a) +{ + const float s = 1.70158f; + a -= 1.0f; + return (y - x) * (a * a * ((s + 1.0f) * a + s) + 1.0f) + x; +} + +template +T ease_in_out_back(const T& x, const T& y, float a) +{ + const float s = 1.70158f * 1.525f; + + a *= 2.0f; + if (a < 1.0f) + { + return (y - x) * 0.5f * (a * a * ((s + 1.0f) * a - s)) + x; + } + + a -= 2.0f; + return (y - x) * 0.5f * (a * a * ((s + 1.0f) * a + s) + 2.0f) + x; +} + +#endif // ANTKEEPER_EASINGS_HPP diff --git a/src/antkeeper/animation/timeline.cpp b/src/antkeeper/animation/timeline.cpp new file mode 100644 index 0000000..4628f0f --- /dev/null +++ b/src/antkeeper/animation/timeline.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 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 "timeline.hpp" + +auto cue_compare = [](const cue& a, const cue& b) +{ + return std::get<0>(a) < std::get<0>(b); +}; + +timeline::timeline(): + cues(cue_compare), + position(0.0f), + autoremove(false) +{} + +void timeline::advance(float dt) +{ + auto lower_bound = cues.lower_bound({position, nullptr}); + auto upper_bound = cues.upper_bound({position + dt, nullptr}); + + for (auto iterator = lower_bound; iterator != upper_bound; ++iterator) + { + std::get<1>(*iterator)(); + } + + if (autoremove && lower_bound != upper_bound) + { + cues.erase(lower_bound, upper_bound); + } + + position += dt; +} + +void timeline::seek(float t) +{ + position = t; +} + +void timeline::add_cue(const cue& c) +{ + cues.emplace(c); +} + +void timeline::remove_cue(const cue& c) +{ + cues.erase(c); +} + +void timeline::remove_cues(float start, float end) +{ + auto lower_bound = cues.lower_bound({start, nullptr}); + auto upper_bound = cues.upper_bound({end, nullptr}); + cues.erase(lower_bound, upper_bound); +} + +void timeline::add_sequence(const sequence& s) +{ + for (const cue& c: s) + { + add_cue(c); + } +} + +void timeline::remove_sequence(const sequence& s) +{ + for (const cue& c: s) + { + remove_cue(c); + } +} + +void timeline::clear() +{ + cues.clear(); +} + +void timeline::set_autoremove(bool enabled) +{ + autoremove = enabled; +} + +sequence timeline::get_cues(float start, float end) +{ + sequence s; + + auto lower_bound = cues.lower_bound({start, nullptr}); + auto upper_bound = cues.upper_bound({end, nullptr}); + for (auto iterator = lower_bound; iterator != upper_bound; ++iterator) + { + s.push_back(*iterator); + } + + return s; +} + diff --git a/src/antkeeper/animation/timeline.hpp b/src/antkeeper/animation/timeline.hpp new file mode 100644 index 0000000..d48e3b4 --- /dev/null +++ b/src/antkeeper/animation/timeline.hpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 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_TIMELINE_HPP +#define ANTKEEPER_TIMELINE_HPP + +#include +#include +#include + +/** + * Scheduled function consisting of a time and function object. + */ +typedef std::tuple> cue; + +/** + * List of cues. + */ +typedef std::list sequence; + +/** + * Timeline which executes cues (scheduled functions) when advanced over their respective positions in time. + */ +class timeline +{ +public: + /** + * Creates a timeline. + */ + timeline(); + + /** + * Advances the timeline position (t) by @p dt, triggering any cues scheduled between `t` and `dt`. If autoremove is enabled, triggered cues will be removed. + * + * @param dt Delta time by which the timeline position will be advanced. + */ + void advance(float dt); + + /** + * Sets the timeline position to @p t. + * + * @param t Position in time to which the timeline position will be set. + */ + void seek(float t); + + /** + * Adds a cue to the timeline. + * + * @param c Cue to add. + */ + void add_cue(const cue& c); + + /** + * Removes a cue from the timeline. If there are multiple identical cues (same time and function), they will all be removed. + * + * @param c Cue to remove. + */ + void remove_cue(const cue& c); + + /** + * Removes all cues on `[start, end)`. + * + * @param start Starting position in time (inclusive). + * @param end Ending position in time (non-inclusive). + */ + void remove_cues(float start, float end); + + /** + * Adds a sequence of cues to the timeline. + * + * @param s Sequence of cues to add. + */ + void add_sequence(const sequence& s); + + /** + * Removes a sequence of cues from the timeline. + * + * @param s Sequence of cues to remove. + */ + void remove_sequence(const sequence& s); + + /** + * Removes all cues from the timeline. + */ + void clear(); + + /** + * If enabled, cues will be automatically removed from the timeline when they are triggered. + * + * @param enabled Whether to enable autoremove. + */ + void set_autoremove(bool enabled); + + /** + * Returns the current position in time on the timeline. + */ + float get_position() const; + + /** + * Returns all the cues on `[start, end)`. + * + * @param start Starting position in time (inclusive). + * @param end Ending position in time (non-inclusive). + * @return All cues on `[start, end)`. + */ + sequence get_cues(float start, float end); + +private: + std::multiset> cues; + float position; + bool autoremove; +}; + +inline float timeline::get_position() const +{ + return position; +} + +#endif // ANTKEEPER_TIMELINE_HPP + diff --git a/src/antkeeper/animation/tween.hpp b/src/antkeeper/animation/tween.hpp new file mode 100644 index 0000000..319f558 --- /dev/null +++ b/src/antkeeper/animation/tween.hpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2020 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_TWEEN_HPP +#define ANTKEEPER_TWEEN_HPP + +#include +#include +#include + +/** + * Linearly interpolates between two values. + * + * @param x Start of the range in which to interpolate. + * @param y End of the range in which to interpolate. + * @param a Value used to interpolate between @p x and @p y. + * @return Interpolated value. + */ +template +T tween_default_lerp(const T& x, const T& y, float a); + +/** + * Container which stores two states along with an interpolator, for quick and easy tweening. + * + * @tparam T Value type. + * @tparam Interpolator Interpolator function or function object type. + */ +template ::type(const T&, const T&, float)>> +class tween +{ +public: + typedef typename std::remove_pointer::type value_type; + typedef typename std::decay::type interpolator_type; + + /** + * Creates a tween. + * + * @param state0 Initial value of state 0. + * @param state1 Initial value of state 1. + * @param interpolator Function or function object that will be used to interpolate between states 0 and 1. + */ + explicit tween(const T& state0, const T& state1, const interpolator_type& interpolator = tween_default_lerp); + + /** + * Creates a tween. + * + * @param value Initial value of states 0 and 1. + * @param interpolator Function or function object that will be used to interpolate between states 0 and 1. + */ + explicit tween(const T& value, const interpolator_type& interpolator = tween_default_lerp); + + /** + * Creates a tween. + * + * @param interpolator Function or function object that will be used to interpolate between states 0 and 1. + */ + explicit tween(const interpolator_type& interpolator = tween_default_lerp); + + /** + * Returns a reference to the specified tween state. + * + * @param state Index of a tween state. Should be either `0` or `1`. + * @return Reference to the specified tween state. + */ + const T& operator[](int state) const; + + /// @copydoc tween::operator[](int) const + T& operator[](int state); + + /** + * Returns an interpolated state between state 0 and state 1. + * + * @param a Interpolation factor. Should be on `[0.0, 1.0]`. + * @return Interpolated state. + */ + value_type interpolate(float a) const; + + /** + * Returns the function or function object that is used to interpolate between states 0 and 1. + */ + const interpolator_type& get_interpolator() const; + + /** + * Sets state 0 = state 1. + */ + void update(); + + /** + * Swaps state 0 and state 1. + */ + void swap(); + +private: + interpolator_type interpolator; + T state0; + T state1; +}; + +template +inline T tween_default_lerp(const T& x, const T& y, float a) +{ + return x * (1.0f - a) + y * a; +} + +template +tween::tween(const T& value, const interpolator_type& interpolator): + interpolator(interpolator), + state0(value), + state1(value) +{} + +template +tween::tween(const T& state0, const T& state1, const interpolator_type& interpolator): + interpolator(interpolator), + state0(state0), + state1(state1) +{} + +template +tween::tween(const interpolator_type& interpolator): + interpolator(interpolator) +{} + +template +inline const T& tween::operator[](int state) const +{ + return (state <= 0) ? state0 : state1; +} + +template +inline T& tween::operator[](int state) +{ + return (state <= 0) ? state0 : state1; +} + +template +inline typename tween::value_type tween::interpolate(float a) const +{ + return interpolator(state0, state1, a); +} + +template +inline const typename tween::interpolator_type& tween::get_interpolator() const +{ + return interpolator; +} + +template +inline void tween::update() +{ + state0 = state1; +} + +template +inline void tween::swap() +{ + std::swap(state0, state1); +} + +#endif // ANTKEEPER_TWEEN_HPP + diff --git a/src/antkeeper/application.cpp b/src/antkeeper/application.cpp new file mode 100644 index 0000000..22951e8 --- /dev/null +++ b/src/antkeeper/application.cpp @@ -0,0 +1,1055 @@ +/* + * Copyright (C) 2020 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 "application.hpp" +#include "configuration.hpp" +#include "state/application-states.hpp" +#include "filesystem.hpp" +#include "math.hpp" +#include "timestamp.hpp" + +// STL +#include +#include +#include +#include +#include +#include + +// External +#include +#include +#include "stb/stb_image_write.h" + +// Debug +#include "debug/ansi-codes.hpp" + +// Resources +#include "resources/resource-manager.hpp" + +// Input +#include "input/sdl-scancode-table.hpp" +#include "input/sdl-game-controller-tables.hpp" +#include "input/scancode.hpp" +#include "input/input-mapping.hpp" + +// Rasterizer +#include "rasterizer/rasterizer.hpp" +#include "rasterizer/framebuffer.hpp" +#include "rasterizer/texture-2d.hpp" +#include "rasterizer/pixel-type.hpp" +#include "rasterizer/pixel-format.hpp" +#include "rasterizer/vertex-buffer.hpp" +#include "rasterizer/vertex-array.hpp" +#include "rasterizer/vertex-attribute-type.hpp" +#include "rasterizer/texture-wrapping.hpp" +#include "rasterizer/texture-filter.hpp" + +// Renderer +#include "renderer/passes/shadow-map-pass.hpp" +#include "renderer/passes/sky-pass.hpp" +#include "renderer/passes/clear-pass.hpp" +#include "renderer/passes/material-pass.hpp" +#include "renderer/passes/bloom-pass.hpp" +#include "renderer/passes/final-pass.hpp" +#include "renderer/vertex-attributes.hpp" +#include "renderer/material-flags.hpp" + +// Scene +#include "scene/billboard.hpp" +#include "scene/model-instance.hpp" + +// Systems +#include "systems/behavior-system.hpp" +#include "systems/camera-system.hpp" +#include "systems/collision-system.hpp" +#include "systems/locomotion-system.hpp" +#include "systems/model-system.hpp" +#include "systems/nest-system.hpp" +#include "systems/placement-system.hpp" +#include "systems/samara-system.hpp" +#include "systems/subterrain-system.hpp" +#include "systems/terrain-system.hpp" +#include "systems/vegetation-system.hpp" +#include "systems/tool-system.hpp" +#include "systems/control-system.hpp" +#include "systems/ui-system.hpp" + +// Entity components +#include "entity/components/cavity-component.hpp" + +using namespace vmq::operators; + +application::application(int argc, char** argv): + closed(false), + exit_status(EXIT_SUCCESS) +{ + // Format log messages + logger.set_warning_prefix("Warning: "); + logger.set_error_prefix("Error: "); + logger.set_success_prefix(std::string()); + #if defined(DEBUG) + #if !defined(_WIN32) + logger.set_warning_prefix(std::string(ansi::bold) + std::string(ansi::yellow) + std::string("Warning: ") + std::string(ansi::reset) + std::string(ansi::yellow)); + logger.set_warning_postfix(ansi::reset); + logger.set_error_prefix(std::string(ansi::bold) + std::string(ansi::red) + std::string("Error: ") + std::string(ansi::reset) + std::string(ansi::red)); + logger.set_error_postfix(ansi::reset); + logger.set_success_prefix(ansi::green); + logger.set_success_postfix(ansi::reset); + #endif + #endif + + // Redirect logger output + #if defined(DEBUG) + logger.redirect(&std::cout); + #else + std::string log_filename = "log.txt"; + log_filestream.open(log_filename.c_str()); + logger.redirect(&log_filestream); + #endif + + // Determine application name + std::string application_name; + #if defined(_WIN32) + application_name = "Antkeeper"; + #else + application_name = "antkeeper"; + #endif + + // Form resource paths + data_path = get_data_path(application_name) + "data/"; + config_path = get_config_path(application_name); + logger.log("Detected data path as \"" + data_path + "\"\n"); + logger.log("Detected config path as \"" + config_path + "\"\n"); + + screenshots_path = config_path + "screenshots/"; + + // Create nonexistent config directories + std::vector config_paths; + config_paths.push_back(config_path); + config_paths.push_back(screenshots_path); + for (const std::string& path: config_paths) + { + if (!path_exists(path)) + { + logger.log("Creating directory \"" + path + "\"... "); + if (create_directory(path)) + { + logger.success("success\n"); + } + else + { + logger.error("failed\n"); + } + } + } + + // Setup resource manager + resource_manager = new ::resource_manager(); + + // Include resource search paths in order of priority + resource_manager->include(config_path); + resource_manager->include(data_path); + resource_manager->include(data_path + "/shaders/include/"); + resource_manager->include(data_path + "/shaders/src/"); + resource_manager->include(data_path + "/models/"); + resource_manager->include(data_path + "/textures/"); + resource_manager->include(data_path + "/materials/"); + resource_manager->include(data_path + "/entities/"); + resource_manager->include(data_path + "/behaviors/"); + resource_manager->include(data_path + "/controls/"); + + // Get SDL compiled version + SDL_version sdl_compiled_version; + SDL_VERSION(&sdl_compiled_version); + std::string sdl_compiled_version_string = std::to_string(sdl_compiled_version.major) + "." + std::to_string(sdl_compiled_version.minor) + "." + std::to_string(sdl_compiled_version.patch); + logger.log("Compiled against SDL " + sdl_compiled_version_string + "\n"); + + // Get SDL linked version + SDL_version sdl_linked_version; + SDL_GetVersion(&sdl_linked_version); + std::string sdl_linked_version_string = std::to_string(sdl_linked_version.major) + "." + std::to_string(sdl_linked_version.minor) + "." + std::to_string(sdl_linked_version.patch); + logger.log("Linking against SDL " + sdl_linked_version_string + "\n"); + + // Init SDL + logger.log("Initializing SDL... "); + if (SDL_Init(SDL_INIT_VIDEO) != 0) + { + logger.log("failed\n"); + throw std::runtime_error("Failed to initialize SDL"); + } + else + { + logger.log("success\n"); + } + + // Load default OpenGL library + logger.log("Loading OpenGL library... "); + if (SDL_GL_LoadLibrary(nullptr) != 0) + { + logger.log("failed\n"); + } + else + { + logger.log("success\n"); + } + + // Set window creation hints + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + + SDL_DisplayMode sdl_display_mode; + if (SDL_GetDesktopDisplayMode(0, &sdl_display_mode) != 0) + { + logger.error("Failed to get desktop display mode: \"" + std::string(SDL_GetError()) + "\"\n"); + } + else + { + logger.log("Detected " + std::to_string(sdl_display_mode.w) + "x" + std::to_string(sdl_display_mode.h) + " display\n"); + display_dimensions = {sdl_display_mode.w, sdl_display_mode.h}; + } + + int window_width = 1920; + int window_height = 1080; + fullscreen = true; + viewport = {0.0f, 0.0f, static_cast(window_width), static_cast(window_height)}; + + // Create window + logger.log("Creating " + std::to_string(window_width) + "x" + std::to_string(window_height) + " window... "); + window = SDL_CreateWindow + ( + "Antkeeper", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + window_width, window_height, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_FULLSCREEN_DESKTOP + ); + if (!window) + { + logger.error("failed\n"); + throw std::runtime_error("Failed to create SDL window"); + } + else + { + logger.success("success\n"); + } + + // Create OpenGL context + logger.log("Creating OpenGL 3.3 context... "); + context = SDL_GL_CreateContext(window); + if (!context) + { + logger.error("failed\n"); + throw std::runtime_error("Failed to create OpenGL context"); + } + else + { + logger.success("success\n"); + } + + // Load OpenGL functions via GLAD + logger.log("Loading OpenGL functions... "); + if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) + { + logger.error("failed\n"); + } + else + { + logger.success("success\n"); + } + + // Set v-sync mode + int swap_interval = 1; + logger.log((swap_interval) ? "Enabling v-sync... " : "Disabling v-sync... "); + if (SDL_GL_SetSwapInterval(swap_interval) != 0) + { + logger.error("failed\n"); + } + else + { + logger.success("success\n"); + } + + // Setup rasterizer + rasterizer = new ::rasterizer(); + + // Show window + SDL_ShowWindow(window); + + // Clear window to black + rasterizer->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f); + rasterizer->clear_framebuffer(true, false, false); + SDL_GL_SwapWindow(window); + + // Hide cursor + SDL_ShowCursor(SDL_DISABLE); + + // Init SDL joystick and game controller subsystems + logger.log("Initializing SDL Joystick and Game Controller subsystems... "); + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0) + { + logger.log("failed\n"); + throw std::runtime_error("Failed to initialize SDL Joystick or Game Controller subsystems"); + } + else + { + logger.success("success\n"); + } + + // Load SDL game controller mappings + logger.log("Loading SDL game controller mappings from database... "); + std::string gamecontrollerdb_path = data_path + "controls/gamecontrollerdb.txt"; + if (SDL_GameControllerAddMappingsFromFile(gamecontrollerdb_path.c_str()) == -1) + { + logger.error("failed\n"); + } + else + { + logger.success("success\n"); + } + + // Setup billboard VAO + { + const float billboard_vertex_data[] = + { + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f + }; + + std::size_t billboard_vertex_size = 8; + std::size_t billboard_vertex_stride = sizeof(float) * billboard_vertex_size; + std::size_t billboard_vertex_count = 6; + + billboard_vbo = new vertex_buffer(sizeof(float) * billboard_vertex_size * billboard_vertex_count, billboard_vertex_data); + billboard_vao = new vertex_array(); + billboard_vao->bind_attribute(VERTEX_POSITION_LOCATION, *billboard_vbo, 3, vertex_attribute_type::float_32, billboard_vertex_stride, 0); + billboard_vao->bind_attribute(VERTEX_TEXCOORD_LOCATION, *billboard_vbo, 2, vertex_attribute_type::float_32, billboard_vertex_stride, sizeof(float) * 3); + billboard_vao->bind_attribute(VERTEX_BARYCENTRIC_LOCATION, *billboard_vbo, 3, vertex_attribute_type::float_32, billboard_vertex_stride, sizeof(float) * 5); + } + + // Setup renderer + renderer.set_billboard_vao(billboard_vao); + + // Load fallback material + fallback_material = resource_manager->load("fallback.mtl"); + + + // Create shadow map depth texture and framebuffer + shadow_map_resolution = 4096; + shadow_map_depth_texture = new texture_2d(shadow_map_resolution, shadow_map_resolution, pixel_type::float_32, pixel_format::d); + shadow_map_depth_texture->set_wrapping(texture_wrapping::clamp, texture_wrapping::clamp); + shadow_map_depth_texture->set_filters(texture_min_filter::linear, texture_mag_filter::linear); + shadow_map_depth_texture->set_max_anisotropy(0.0f); + shadow_map_framebuffer = new framebuffer(shadow_map_resolution, shadow_map_resolution); + shadow_map_framebuffer->attach(framebuffer_attachment_type::depth, shadow_map_depth_texture); + + // Create HDR framebuffer (32F color, 32F depth) + framebuffer_hdr_color = new texture_2d(window_width, window_height, pixel_type::float_32, pixel_format::rgb); + framebuffer_hdr_color->set_wrapping(texture_wrapping::clamp, texture_wrapping::clamp); + framebuffer_hdr_color->set_filters(texture_min_filter::linear, texture_mag_filter::linear); + framebuffer_hdr_color->set_max_anisotropy(0.0f); + framebuffer_hdr_depth = new texture_2d(window_width, window_height, pixel_type::float_32, pixel_format::d); + framebuffer_hdr_depth->set_wrapping(texture_wrapping::clamp, texture_wrapping::clamp); + framebuffer_hdr_depth->set_filters(texture_min_filter::linear, texture_mag_filter::linear); + framebuffer_hdr_depth->set_max_anisotropy(0.0f); + framebuffer_hdr = new framebuffer(window_width, window_height); + framebuffer_hdr->attach(framebuffer_attachment_type::color, framebuffer_hdr_color); + framebuffer_hdr->attach(framebuffer_attachment_type::depth, framebuffer_hdr_depth); + + // Create pingpong framebuffers (32F color, no depth) + int bloom_width = window_width / 2; + int bloom_height = window_height / 2; + bloom_texture = new texture_2d(bloom_width, bloom_height, pixel_type::float_32, pixel_format::rgb); + bloom_texture->set_wrapping(texture_wrapping::clamp, texture_wrapping::clamp); + bloom_texture->set_filters(texture_min_filter::linear, texture_mag_filter::linear); + bloom_texture->set_max_anisotropy(0.0f); + framebuffer_bloom = new framebuffer(bloom_width, bloom_height); + framebuffer_bloom->attach(framebuffer_attachment_type::color, bloom_texture); + + // Setup default compositor + shadow_map_clear_pass = new ::clear_pass(rasterizer, shadow_map_framebuffer); + shadow_map_clear_pass->set_cleared_buffers(false, true, false); + shadow_map_pass = new ::shadow_map_pass(rasterizer, shadow_map_framebuffer, resource_manager); + shadow_map_pass->set_split_scheme_weight(0.75f); + clear_pass = new ::clear_pass(rasterizer, framebuffer_hdr); + clear_pass->set_cleared_buffers(true, true, false); + sky_pass = new ::sky_pass(rasterizer, framebuffer_hdr, resource_manager); + material_pass = new ::material_pass(rasterizer, framebuffer_hdr, resource_manager); + material_pass->set_fallback_material(fallback_material); + material_pass->set_time_tween(&time); + material_pass->set_focal_point_tween(&focal_point_tween); + material_pass->shadow_map_pass = shadow_map_pass; + material_pass->shadow_map = shadow_map_depth_texture; + + bloom_pass = new ::bloom_pass(rasterizer, framebuffer_bloom, resource_manager); + bloom_pass->set_source_texture(framebuffer_hdr_color); + bloom_pass->set_brightness_threshold(1.0f); + bloom_pass->set_blur_iterations(4); + bloom_pass->set_enabled(false); + + final_pass = new ::final_pass(rasterizer, &rasterizer->get_default_framebuffer(), resource_manager); + final_pass->set_color_texture(framebuffer_hdr_color); + final_pass->set_bloom_texture(bloom_texture); + + default_compositor.add_pass(shadow_map_clear_pass); + default_compositor.add_pass(shadow_map_pass); + default_compositor.add_pass(clear_pass); + default_compositor.add_pass(sky_pass); + default_compositor.add_pass(material_pass); + default_compositor.add_pass(bloom_pass); + default_compositor.add_pass(final_pass); + + // Setup default camera + default_camera.set_perspective(45.0f * vmq::pi / 180.0f, (float)window_width / (float)window_height, 0.1f, 1000.0f); + default_camera.set_compositor(&default_compositor); + default_camera.set_composite_index(1); + + // Setup timeline system + timeline.set_autoremove(true); + + // Setup animation system + // ... + + // ECS + terrain_system = new ::terrain_system(ecs_registry, resource_manager); + terrain_system->set_patch_size(TERRAIN_PATCH_SIZE); + vegetation_system = new ::vegetation_system(ecs_registry); + vegetation_system->set_terrain_patch_size(TERRAIN_PATCH_SIZE); + vegetation_system->set_vegetation_patch_resolution(VEGETATION_PATCH_RESOLUTION); + vegetation_system->set_vegetation_density(1.0f); + vegetation_system->set_vegetation_model(resource_manager->load("grass-tuft.obj")); + vegetation_system->set_scene(&overworld_scene); + + + tool_system = new ::tool_system(ecs_registry); + tool_system->set_camera(&default_camera); + tool_system->set_orbit_cam(&orbit_cam); + tool_system->set_viewport(viewport); + camera_system = new ::camera_system(ecs_registry); + camera_system->set_orbit_cam(&orbit_cam); + camera_system->set_viewport(viewport); + subterrain_system = new ::subterrain_system(ecs_registry, resource_manager); + subterrain_system->set_scene(&underworld_scene); + nest_system = new ::nest_system(ecs_registry, resource_manager); + collision_system = new ::collision_system(ecs_registry); + samara_system = new ::samara_system(ecs_registry); + placement_system = new ::placement_system(ecs_registry); + behavior_system = new ::behavior_system(ecs_registry); + locomotion_system = new ::locomotion_system(ecs_registry); + model_system = new ::model_system(ecs_registry, overworld_scene); + + // Setup systems + systems.push_back([this](double t, double dt){ this->overworld_scene.update_tweens(); this->underworld_scene.update_tweens(); this->ui_system->get_scene()->update_tweens(); focal_point_tween.update(); }); + systems.push_back([this](double t, double dt){ this->translate_sdl_events(); }); + systems.push_back([this](double t, double dt){ this->event_dispatcher.update(t); }); + systems.push_back([this](double t, double dt){ this->timeline.advance(dt); }); + systems.push_back(std::bind(&terrain_system::update, terrain_system, std::placeholders::_1, std::placeholders::_2)); + systems.push_back(std::bind(&vegetation_system::update, vegetation_system, std::placeholders::_1, std::placeholders::_2)); + systems.push_back(std::bind(&placement_system::update, placement_system, std::placeholders::_1, std::placeholders::_2)); + systems.push_back(std::bind(&nest_system::update, nest_system, std::placeholders::_1, std::placeholders::_2)); + systems.push_back(std::bind(&subterrain_system::update, subterrain_system, std::placeholders::_1, std::placeholders::_2)); + systems.push_back(std::bind(&collision_system::update, collision_system, std::placeholders::_1, std::placeholders::_2)); + systems.push_back(std::bind(&samara_system::update, samara_system, std::placeholders::_1, std::placeholders::_2)); + systems.push_back(std::bind(&camera_system::update, camera_system, std::placeholders::_1, std::placeholders::_2)); + systems.push_back(std::bind(&behavior_system::update, behavior_system, std::placeholders::_1, std::placeholders::_2)); + systems.push_back(std::bind(&locomotion_system::update, locomotion_system, std::placeholders::_1, std::placeholders::_2)); + systems.push_back([this](double t, double dt){ this->control_system->update(dt); }); + systems.push_back([this](double t, double dt){ + this->subterrain_light.set_translation(orbit_cam.get_focal_point()); + this->lantern.set_translation(orbit_cam.get_focal_point()); + this->spotlight.set_transform(default_camera.get_transform()); + this->focal_point_tween[1] = orbit_cam.get_focal_point(); + }); + + systems.push_back([this](double t, double dt){ this->ui_system->update(dt); }); + systems.push_back(std::bind(&tool_system::update, tool_system, std::placeholders::_1, std::placeholders::_2)); + systems.push_back(std::bind(&model_system::update, model_system, std::placeholders::_1, std::placeholders::_2)); + systems.push_back([this](double t, double dt){ this->animator.animate(dt); }); + systems.push_back([this](double t, double dt){ this->application_controls.update(); this->menu_controls.update(); this->camera_controls->update(); }); + + // Setup FSM states + loading_state = + { + std::function(std::bind(enter_loading_state, this)), + std::function(std::bind(exit_loading_state, this)) + }; + language_select_state = + { + std::function(std::bind(enter_language_select_state, this)), + std::function(std::bind(exit_language_select_state, this)) + }; + splash_state = + { + std::function(std::bind(enter_splash_state, this)), + std::function(std::bind(exit_splash_state, this)) + }; + title_state = + { + std::function(std::bind(enter_title_state, this)), + std::function(std::bind(exit_title_state, this)) + }; + play_state = + { + std::function(std::bind(enter_play_state, this)), + std::function(std::bind(exit_play_state, this)) + }; + pause_state = + { + std::function(std::bind(enter_pause_state, this)), + std::function(std::bind(exit_pause_state, this)) + }; + + // Setup frame timing + frame_scheduler.set_update_callback(std::bind(&application::update, this, std::placeholders::_1, std::placeholders::_2)); + frame_scheduler.set_render_callback(std::bind(&application::render, this, std::placeholders::_1)); + frame_scheduler.set_update_rate(60.0); + frame_scheduler.set_max_frame_duration(0.25); + + // Setup performance sampling + performance_sampler.set_sample_size(15); + + // Setup input event routing + input_event_router.set_event_dispatcher(&event_dispatcher); + input_mapper.set_event_dispatcher(&event_dispatcher); + + // Setup input devices + keyboard.set_event_dispatcher(&event_dispatcher); + mouse.set_event_dispatcher(&event_dispatcher); + game_controller.set_event_dispatcher(&event_dispatcher); + + // Setup controls + application_controls.add_control(&toggle_fullscreen_control); + application_controls.add_control(&dig_control); + application_controls.add_control(&screenshot_control); + toggle_fullscreen_control.set_activated_callback(std::bind(&application::toggle_fullscreen, this)); + screenshot_control.set_activated_callback([this]() + { + take_screenshot(); + }); + + menu_back_control.set_activated_callback(std::bind(&application::close, this, 0)); + menu_controls.add_control(&menu_back_control); + menu_controls.add_control(&menu_select_control); + + orbit_cam.attach(&default_camera); + control_system = new ::control_system(); + control_system->set_orbit_cam(&orbit_cam); + control_system->set_viewport(viewport); + event_dispatcher.subscribe(control_system); + event_dispatcher.subscribe(camera_system); + event_dispatcher.subscribe(tool_system); + + camera_controls = control_system->get_control_set(); + + // Application control mappings + input_event_router.add_mapping(key_mapping(&toggle_fullscreen_control, nullptr, scancode::f11)); + input_event_router.add_mapping(key_mapping(&screenshot_control, nullptr, scancode::f12)); + + // Add menu control mappings + input_event_router.add_mapping(key_mapping(&menu_back_control, nullptr, scancode::escape)); + input_event_router.add_mapping(key_mapping(&menu_back_control, nullptr, scancode::backspace)); + input_event_router.add_mapping(game_controller_button_mapping(&menu_back_control, nullptr, game_controller_button::b)); + + input_event_router.add_mapping(key_mapping(control_system->get_tool_menu_control(), nullptr, scancode::left_shift)); + input_event_router.add_mapping(game_controller_button_mapping(control_system->get_tool_menu_control(), nullptr, game_controller_button::x)); + + + + input_event_router.add_mapping(key_mapping(&menu_select_control, nullptr, scancode::enter)); + input_event_router.add_mapping(key_mapping(&menu_select_control, nullptr, scancode::space)); + input_event_router.add_mapping(key_mapping(control_system->get_move_forward_control(), nullptr, scancode::w)); + + input_event_router.add_mapping(key_mapping(control_system->get_toggle_view_control(), nullptr, scancode::tab)); + control_system->get_toggle_view_control()->set_activated_callback( + [this]() + { + this->active_scene->remove_object(&this->default_camera); + this->active_scene = (this->active_scene == &this->overworld_scene) ? &this->underworld_scene : &this->overworld_scene; + this->active_scene->add_object(&this->default_camera); + + if (this->active_scene == &this->overworld_scene) + this->sky_pass->set_enabled(true); + else + this->sky_pass->set_enabled(false); + }); + + input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_move_forward_control(), nullptr, game_controller_axis::left_y, true)); + input_event_router.add_mapping(key_mapping(control_system->get_move_back_control(), nullptr, scancode::s)); + input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_move_back_control(), nullptr, game_controller_axis::left_y, false)); + input_event_router.add_mapping(key_mapping(control_system->get_move_left_control(), nullptr, scancode::a)); + input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_move_left_control(), nullptr, game_controller_axis::left_x, true)); + input_event_router.add_mapping(key_mapping(control_system->get_move_right_control(), nullptr, scancode::d)); + input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_move_right_control(), nullptr, game_controller_axis::left_x, false)); + input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_rotate_ccw_control(), nullptr, game_controller_axis::right_x, false)); + input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_rotate_cw_control(), nullptr, game_controller_axis::right_x, true)); + input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_tilt_up_control(), nullptr, game_controller_axis::right_y, false)); + input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_tilt_down_control(), nullptr, game_controller_axis::right_y, true)); + input_event_router.add_mapping(mouse_wheel_mapping(control_system->get_zoom_in_control(), nullptr, mouse_wheel_axis::positive_y)); + input_event_router.add_mapping(mouse_wheel_mapping(control_system->get_zoom_out_control(), nullptr, mouse_wheel_axis::negative_y)); + input_event_router.add_mapping(mouse_button_mapping(control_system->get_adjust_camera_control(), nullptr, 3)); + input_event_router.add_mapping(game_controller_button_mapping(control_system->get_ascend_control(), nullptr, game_controller_button::y)); + input_event_router.add_mapping(game_controller_button_mapping(control_system->get_descend_control(), nullptr, game_controller_button::a)); + input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_zoom_out_control(), nullptr, game_controller_axis::trigger_left, false)); + input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_zoom_in_control(), nullptr, game_controller_axis::trigger_right, false)); + + control_system->get_adjust_camera_control()->set_activated_callback([this](){ this->set_relative_mouse_mode(true); this->tool_system->set_pick(false); }); + control_system->get_adjust_camera_control()->set_deactivated_callback([this](){ this->set_relative_mouse_mode(false); this->tool_system->set_pick(true); }); + + + + input_event_router.add_mapping(key_mapping(&dig_control, nullptr, scancode::one)); + dig_control.set_activated_callback( + [this]() + { + const float r = 25.0f; + + ecs::cavity_component cavity; + cavity.position = + { + frand(-r, r), + frand(-r * 2, r), + frand(-r, r) + }; + cavity.radius = frand(0.75f, 1.25f); + + ecs_registry.assign(ecs_registry.create(), cavity); + }); + + pheromones.rows = 256; + pheromones.columns = 256; + pheromones.buffers = new float*[2]; + pheromones.buffers[0] = new float[pheromones.rows * pheromones.columns]; + pheromones.buffers[1] = new float[pheromones.rows * pheromones.columns]; + pheromones.current = 0; + //diffuse(&pheromones); + + + control_system->set_tool(nullptr); + + // Setup UI + ui_system = new ::ui_system(resource_manager); + ui_system->set_viewport(viewport); + ui_system->set_tool_menu_control(control_system->get_tool_menu_control()); + event_dispatcher.subscribe(ui_system); + + // Setup UI camera compositor + ui_clear_pass = new ::clear_pass(rasterizer, &rasterizer->get_default_framebuffer()); + ui_clear_pass->set_cleared_buffers(false, true, false); + ui_material_pass = new ::material_pass(rasterizer, &rasterizer->get_default_framebuffer(), resource_manager); + ui_material_pass->set_fallback_material(fallback_material); + ui_material_pass->set_time_tween(&time); + ui_compositor.add_pass(ui_clear_pass); + ui_compositor.add_pass(ui_material_pass); + ui_system->get_camera()->set_compositor(&ui_compositor); + + // Setup lights + sun_indirect.set_intensity(0.25f); + sun_indirect.update_tweens(); + sun_direct.look_at({-1.0f, 5.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}); + sun_direct.set_intensity(1.0f); + sun_direct.update_tweens(); + subterrain_light.set_color({1, 1, 1}); + subterrain_light.set_intensity(1.0f); + subterrain_light.set_attenuation({1.0f, 0.09f, 0.032f}); + subterrain_light.update_tweens(); + spotlight.set_color({1, 1, 1}); + spotlight.set_intensity(1.0f); + spotlight.set_attenuation({1.0f, 0.09f, 0.032f}); + spotlight.set_cutoff({vmq::radians(15.0f), vmq::radians(30.0f)}); + spotlight.update_tweens(); + spotlight.set_active(false); + + underworld_ambient_light.set_color({1, 1, 1}); + underworld_ambient_light.set_intensity(0.15f); + underworld_ambient_light.update_tweens(); + + // Darkness + darkness_volume.set_model(resource_manager->load("darkness-volume.obj")); + lantern.set_model(resource_manager->load("lantern.obj")); + + // Cloud + cloud.set_model(resource_manager->load("cloud.obj")); + cloud.set_translation({0, 0, 4500}); + cloud.set_scale(float3{1, 1, 1} * 100.0f); + + + // Create depth debug billboard + /* + material* depth_debug_material = new material(); + depth_debug_material->set_shader_program(resource_manager->load("ui-element-textured.glsl")); + depth_debug_material->add_property("background")->set_value(shadow_map_depth_texture); + depth_debug_material->add_property("tint")->set_value(float4{1, 1, 1, 1}); + billboard* depth_debug_billboard = new billboard(); + depth_debug_billboard->set_material(depth_debug_material); + depth_debug_billboard->set_scale({128, 128, 1}); + depth_debug_billboard->set_translation({-960 + 128, 1080 * 0.5f - 128, 0}); + depth_debug_billboard->update_tweens(); + ui_system->get_scene()->add_object(depth_debug_billboard); + */ + + material* billboard_material = new material(); + billboard_material->set_shader_program(resource_manager->load("ui-element-textured.glsl")); + billboard_material->add_property("background")->set_value(resource_manager->load("arrow.png")); + billboard_material->add_property("tint")->set_value(float4{1, 1, 1, 1}); + billboard_material->set_flags(MATERIAL_FLAG_TRANSLUCENT | MATERIAL_FLAG_NOT_SHADOW_CASTER); + billboard* arrow_billboard = new billboard(); + arrow_billboard->set_material(billboard_material); + arrow_billboard->set_scale(float3{1, 1, 1} * 2.0f); + arrow_billboard->set_translation({0, 10, 0}); + arrow_billboard->set_billboard_type(billboard_type::cylindrical); + arrow_billboard->set_alignment_axis({0, 1, 0}); + arrow_billboard->update_tweens(); + + + billboard_material = new material(); + billboard_material->set_shader_program(resource_manager->load("portal-card.glsl")); + billboard_material->add_property("color")->set_value(float4{1, 1, 1, 1}); + billboard_material->add_property("range")->set_value(float2{50.0f, 500.0f}); + billboard_material->set_flags(MATERIAL_FLAG_TRANSLUCENT | MATERIAL_FLAG_NOT_SHADOW_CASTER); + billboard* portal_billboard = new billboard(); + portal_billboard->set_material(billboard_material); + portal_billboard->set_scale(float3{1, 1, 1} * 10.0f); + portal_billboard->set_translation({0.0f, 0, 0}); + portal_billboard->set_billboard_type(billboard_type::spherical); + portal_billboard->set_alignment_axis({0, 1, 0}); + portal_billboard->update_tweens(); + + + // Setup overworld scene + overworld_scene.add_object(&default_camera); + overworld_scene.add_object(&sun_indirect); + overworld_scene.add_object(&sun_direct); + overworld_scene.add_object(&spotlight); + overworld_scene.add_object(&cloud); + overworld_scene.add_object(arrow_billboard); + + underworld_scene.add_object(portal_billboard); + + model_instance* larva = new model_instance(resource_manager->load("larva.obj")); + underworld_scene.add_object(larva); + + model_instance* samara = new model_instance(resource_manager->load("samara.obj")); + samara->set_translation({2, -1, 0}); + underworld_scene.add_object(samara); + + + // Setup underworld scene + underworld_scene.add_object(&underworld_ambient_light); + //underworld_scene.add_object(&darkness_volume); + underworld_scene.add_object(&lantern); + underworld_scene.add_object(&subterrain_light); + + + active_scene = &overworld_scene; + +} + +application::~application() +{ + // Destroy the SDL window + SDL_DestroyWindow(window); + + // Shutdown SDL + SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); + SDL_Quit(); +} + +void application::close(int status) +{ + closed = true; + exit_status = status; +} + +int application::execute() +{ + // Enter inital state + state_machine.change_state(play_state); + + // Perform initial update + update(0.0, 0.0); + + // Reset frame scheduler + frame_scheduler.reset(); + + // Reset time tween + time[0] = time[1] = 0.0; + + // Schedule frames until closed + while (!closed) + { + // Tick frame scheduler + frame_scheduler.tick(); + + // Sample frame duration + performance_sampler.sample(frame_scheduler.get_frame_duration()); + } + + // Exit current state + state_machine.change_state({nullptr, nullptr}); + + return exit_status; +} + +void application::update(double t, double dt) +{ + // Update time tween + time.update(); + time[1] = t; + + // Sequentially process systems + for (const auto& system: systems) + { + system(t, dt); + } +} + +void application::render(double alpha) +{ + /* + std::cout << std::fixed; + std::cout << std::setprecision(2); + std::cout << performance_sampler.mean_frame_duration() * 1000.0 << std::endl; + */ + + renderer.render(alpha, *active_scene); + renderer.render(alpha, *ui_system->get_scene()); + + SDL_GL_SwapWindow(window); +} + +void application::translate_sdl_events() +{ + SDL_Event sdl_event; + while (SDL_PollEvent(&sdl_event)) + { + switch (sdl_event.type) + { + case SDL_KEYDOWN: + case SDL_KEYUP: + { + if (sdl_event.key.repeat == 0) + { + scancode scancode = scancode::unknown; + if (sdl_event.key.keysym.scancode <= SDL_SCANCODE_APP2) + { + scancode = sdl_scancode_table[sdl_event.key.keysym.scancode]; + } + + if (sdl_event.type == SDL_KEYDOWN) + keyboard.press(scancode); + else + keyboard.release(scancode); + } + break; + } + + case SDL_MOUSEMOTION: + { + mouse.move(sdl_event.motion.x, sdl_event.motion.y, sdl_event.motion.xrel, sdl_event.motion.yrel); + break; + } + + case SDL_MOUSEBUTTONDOWN: + { + mouse.press(sdl_event.button.button, sdl_event.button.x, sdl_event.button.y); + break; + } + + case SDL_MOUSEBUTTONUP: + { + mouse.release(sdl_event.button.button, sdl_event.button.x, sdl_event.button.y); + break; + } + + case SDL_MOUSEWHEEL: + { + int direction = (sdl_event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) ? -1 : 1; + mouse.scroll(sdl_event.wheel.x * direction, sdl_event.wheel.y * direction); + break; + } + + case SDL_CONTROLLERBUTTONDOWN: + { + if (sdl_event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID) + { + game_controller_button button = sdl_button_table[sdl_event.cbutton.button]; + game_controller.press(button); + } + break; + } + + case SDL_CONTROLLERBUTTONUP: + { + if (sdl_event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID) + { + game_controller_button button = sdl_button_table[sdl_event.cbutton.button]; + game_controller.release(button); + } + break; + } + + case SDL_CONTROLLERAXISMOTION: + { + if (sdl_event.caxis.axis != SDL_CONTROLLER_AXIS_INVALID) + { + game_controller_axis axis = sdl_axis_table[sdl_event.caxis.axis]; + float value = sdl_event.caxis.value; + value /= (value < 0.0f) ? 32768.0f : 32767.0f; + game_controller.move(axis, value); + } + break; + } + + case SDL_CONTROLLERDEVICEADDED: + { + if (SDL_IsGameController(sdl_event.cdevice.which)) + { + SDL_GameController* sdl_controller = SDL_GameControllerOpen(sdl_event.cdevice.which); + + std::string controller_name = SDL_GameControllerNameForIndex(sdl_event.cdevice.which); + if (sdl_controller) + { + logger.log("Connected game controller \"" + controller_name + "\"\n"); + } + else + { + logger.error("Failed to connected game controller \"" + controller_name + "\"\n"); + } + } + + break; + } + + case SDL_CONTROLLERDEVICEREMOVED: + { + SDL_GameController* sdl_controller = SDL_GameControllerFromInstanceID(sdl_event.cdevice.which); + + if (sdl_controller) + { + SDL_GameControllerClose(sdl_controller); + logger.log("Disconnected game controller\n"); + } + + break; + } + + case SDL_WINDOWEVENT: + { + if (sdl_event.window.event == SDL_WINDOWEVENT_RESIZED) + { + window_resized(); + } + + break; + } + + case SDL_QUIT: + { + close(EXIT_SUCCESS); + break; + } + } + } +} + +void application::set_relative_mouse_mode(bool enabled) +{ + if (enabled) + { + SDL_GetMouseState(&std::get<0>(saved_mouse_position), &std::get<1>(saved_mouse_position)); + } + + SDL_SetRelativeMouseMode((enabled) ? SDL_TRUE : SDL_FALSE); + + if (!enabled) + { + SDL_WarpMouseInWindow(window, std::get<0>(saved_mouse_position), std::get<1>(saved_mouse_position)); + } +} + +void application::toggle_fullscreen() +{ + fullscreen = !fullscreen; + + if (fullscreen) + { + SDL_GetWindowSize(window, &std::get<0>(window_dimensions), &std::get<1>(window_dimensions)); + SDL_GetWindowPosition(window, &std::get<0>(window_position), &std::get<1>(window_position)); + + SDL_SetWindowBordered(window, SDL_FALSE); + SDL_SetWindowResizable(window, SDL_FALSE); + SDL_SetWindowPosition(window, 0, 0); + SDL_SetWindowSize(window, std::get<0>(display_dimensions), std::get<1>(display_dimensions)); + } + else + { + SDL_SetWindowBordered(window, SDL_TRUE); + SDL_SetWindowResizable(window, SDL_TRUE); + SDL_SetWindowSize(window, std::get<0>(window_dimensions), std::get<1>(window_dimensions)); + SDL_SetWindowPosition(window, std::get<0>(window_position), std::get<1>(window_position)); + } + + window_resized(); +} + +void application::window_resized() +{ + int width, height; + SDL_GetWindowSize(window, &width, &height); + float aspect_ratio = static_cast(width) / static_cast(height); + viewport = {0.0f, 0.0f, static_cast(width), static_cast(height)}; + + rasterizer->window_resized(width, height); + default_camera.set_perspective(default_camera.get_fov(), aspect_ratio, default_camera.get_clip_near(), default_camera.get_clip_far()); + control_system->set_viewport(viewport); + camera_system->set_viewport(viewport); + tool_system->set_viewport(viewport); + ui_system->set_viewport(viewport); +} + +void application::take_screenshot() const +{ + int x = viewport[0]; + int y = viewport[1]; + int w = viewport[2]; + int h = viewport[3]; + + // Read pixel data from framebuffer + unsigned char* pixels = new unsigned char[w * h * 3]; + glReadBuffer(GL_BACK); + glReadPixels(x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels); + + std::string filename = screenshots_path + "antkeeper-" + timestamp() + ".png"; + std::thread screenshot_thread(application::save_image, filename, w, h, pixels); + screenshot_thread.detach(); +} + +void application::save_image(const std::string& filename, int w, int h, const unsigned char* pixels) +{ + stbi_flip_vertically_on_write(1); + stbi_write_png(filename.c_str(), w, h, 3, pixels, w * 3); + delete[] pixels; +} + diff --git a/src/antkeeper/application.hpp b/src/antkeeper/application.hpp new file mode 100644 index 0000000..e1a00af --- /dev/null +++ b/src/antkeeper/application.hpp @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2020 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_APPLICATION_HPP +#define ANTKEEPER_APPLICATION_HPP + +// STL +#include +#include +#include +#include +#include + +// External +#include + +// Debug +#include "debug/logger.hpp" +#include "debug/performance-sampler.hpp" + +// Input +#include "input/control.hpp" +#include "input/control-set.hpp" +#include "input/keyboard.hpp" +#include "input/mouse.hpp" +#include "input/game-controller.hpp" +#include "input/input-event-router.hpp" +#include "input/input-mapper.hpp" + +// Event +#include "event/event-dispatcher.hpp" + +// Renderer +#include "renderer/compositor.hpp" +#include "renderer/renderer.hpp" + +// Scene +#include "scene/scene.hpp" +#include "scene/camera.hpp" +#include "scene/ambient-light.hpp" +#include "scene/directional-light.hpp" +#include "scene/point-light.hpp" +#include "scene/spotlight.hpp" +#include "scene/model-instance.hpp" + +// Animation +#include "animation/timeline.hpp" +#include "animation/animation.hpp" +#include "animation/tween.hpp" + +// Misc +#include "state/fsm.hpp" +#include "frame-scheduler.hpp" +#include "pheromone-matrix.hpp" +#include "orbit-cam.hpp" + +// Forward declarations +//{ + // SDL + typedef struct SDL_Window SDL_Window; + typedef void* SDL_GLContext; + + // Resources + class resource_manager; + + // Rasterizer + class rasterizer; + class framebuffer; + class vertex_buffer; + class vertex_array; + class texture_2d; + + // Renderer + class clear_pass; + class shadow_map_pass; + class material_pass; + class sky_pass; + class bloom_pass; + class final_pass; + + // Systems + class behavior_system; + class camera_system; + class collision_system; + class locomotion_system; + class model_system; + class nest_system; + class placement_system; + class samara_system; + class subterrain_system; + class terrain_system; + class vegetation_system; + class tool_system; + class control_system; + class ui_system; +//} + +class application +{ +public: + /** + * Creates and initializes an application. + */ + application(int argc, char** argv); + + /** + * Destroys an application. + */ + ~application(); + + /** + * Executes the application, causing it to enter the execution loop until closed. + * + * @return Exit status code. + */ + int execute(); + + /** + * Requests the application's execution loop to cleanly terminate, and specifies its exit status code. + * + * @param status Status to be returned by application::execute() upon execution loop termination. + */ + void close(int status); + + logger* get_logger(); + resource_manager* get_resource_manager(); + fsm::machine* get_state_machine(); + const fsm::state& get_loading_state() const; + const fsm::state& get_language_select_state() const; + const fsm::state& get_splash_state() const; + const fsm::state& get_title_state() const; + const fsm::state& get_play_state() const; + const fsm::state& get_pause_state() const; + timeline* get_timeline(); + animator* get_animator(); + camera* get_camera(); + orbit_cam* get_orbit_cam(); + control_system* get_control_system(); + + entt::registry& get_ecs_registry(); + scene& get_scene(); + + void take_screenshot() const; + +private: + void update(double t, double dt); + void render(double alpha); + void translate_sdl_events(); + void set_relative_mouse_mode(bool enabled); + void toggle_fullscreen(); + void window_resized(); + static void save_image(const std::string& filename, int w, int h, const unsigned char* pixels); + + bool fullscreen; + std::tuple saved_mouse_position; + std::tuple window_dimensions; + std::tuple window_position; + std::tuple display_dimensions; + float4 viewport; + + // Debugging + std::ofstream log_filestream; + logger logger; + + // Paths + std::string data_path; + std::string config_path; + std::string screenshots_path; + + // Resources + resource_manager* resource_manager; + + SDL_Window* window; + SDL_GLContext context; + bool closed; + int exit_status; + + // Updatable systems + timeline timeline; + animator animator; + std::list> systems; + + int shadow_map_resolution; + framebuffer* shadow_map_framebuffer; + texture_2d* shadow_map_depth_texture; + + framebuffer* framebuffer_hdr; + texture_2d* framebuffer_hdr_color; + texture_2d* framebuffer_hdr_depth; + + framebuffer* framebuffer_bloom; // General purpose framebuffer A + texture_2d* bloom_texture; + + // Rendering + rasterizer* rasterizer; + material* fallback_material; + ::clear_pass* clear_pass; + ::sky_pass* sky_pass; + ::material_pass* material_pass; + compositor default_compositor; + ::clear_pass* shadow_map_clear_pass; + ::shadow_map_pass* shadow_map_pass; + ::bloom_pass* bloom_pass; + ::final_pass* final_pass; + + camera default_camera; + ambient_light sun_indirect; + directional_light sun_direct; + point_light subterrain_light; + ambient_light underworld_ambient_light; + model_instance darkness_volume; + model_instance lantern; + model_instance cloud; + model_instance* grass_patches; + ::spotlight spotlight; + vertex_buffer* billboard_vbo; + vertex_array* billboard_vao; + ::renderer renderer; + scene overworld_scene; + scene underworld_scene; + scene* active_scene; + + // FSM + fsm::machine state_machine; + fsm::state loading_state; + fsm::state language_select_state; + fsm::state splash_state; + fsm::state title_state; + fsm::state play_state; + fsm::state pause_state; + + // Frame timing + frame_scheduler frame_scheduler; + performance_sampler performance_sampler; + tween time; + + // Events + event_dispatcher event_dispatcher; + input_event_router input_event_router; + input_mapper input_mapper; + + // Input devices + keyboard keyboard; + mouse mouse; + game_controller game_controller; + + // Controls + control_set menu_controls; + control menu_back_control; + control menu_select_control; + control_set* camera_controls; + + // System controls + control_set application_controls; + control toggle_fullscreen_control; + control screenshot_control; + control dig_control; + + // Game + orbit_cam orbit_cam; + pheromone_matrix pheromones; + control_system* control_system; + + // ECS + entt::registry ecs_registry; + behavior_system* behavior_system; + camera_system* camera_system; + collision_system* collision_system; + locomotion_system* locomotion_system; + model_system* model_system; + nest_system* nest_system; + placement_system* placement_system; + samara_system* samara_system; + subterrain_system* subterrain_system; + terrain_system* terrain_system; + vegetation_system* vegetation_system; + tool_system* tool_system; + + // UI + ui_system* ui_system; + compositor ui_compositor; + ::clear_pass* ui_clear_pass; + ::material_pass* ui_material_pass; + + // Animation + tween focal_point_tween; +}; + +inline logger* application::get_logger() +{ + return &logger; +} + +inline resource_manager* application::get_resource_manager() +{ + return resource_manager; +} + +inline fsm::machine* application::get_state_machine() +{ + return &state_machine; +} + +inline const fsm::state& application::get_loading_state() const +{ + return loading_state; +} + +inline const fsm::state& application::get_language_select_state() const +{ + return language_select_state; +} + +inline const fsm::state& application::get_splash_state() const +{ + return splash_state; +} + +inline const fsm::state& application::get_title_state() const +{ + return title_state; +} + +inline const fsm::state& application::get_play_state() const +{ + return play_state; +} + +inline const fsm::state& application::get_pause_state() const +{ + return pause_state; +} + +inline timeline* application::get_timeline() +{ + return &timeline; +} + +inline animator* application::get_animator() +{ + return &animator; +} + +inline camera* application::get_camera() +{ + return &default_camera; +} + +inline orbit_cam* application::get_orbit_cam() +{ + return &orbit_cam; +} + +inline control_system* application::get_control_system() +{ + return control_system; +} + +inline entt::registry& application::get_ecs_registry() +{ + return ecs_registry; +} + +inline scene& application::get_scene() +{ + return overworld_scene; +} + +#endif // ANTKEEPER_APPLICATION_HPP + diff --git a/src/antkeeper/behavior/behavior-tree.hpp b/src/antkeeper/behavior/behavior-tree.hpp new file mode 100644 index 0000000..5990ebf --- /dev/null +++ b/src/antkeeper/behavior/behavior-tree.hpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2020 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_BEHAVIOR_TREE_HPP +#define ANTKEEPER_BEHAVIOR_TREE_HPP + +#include +#include + +namespace behavior_tree { + +/// Behavior tree node return status enumerations. +enum class status +{ + failure, ///< Indicates a node's execution failed. + success, ///< Indicates a node's execution succeed. + running ///< Indicates a node's execution has not finished. +}; + +/** + * Abstract base class for behavior tree nodes. + * + * @tparam T Data type on which nodes operate. + */ +template +struct node +{ + /// Data type on which nodes operate. + typedef T context_type; + + /** + * Executes a node's functionality and returns its status. + * + * @param context Context data on which the node will operate. + */ + virtual status execute(context_type& context) const = 0; +}; + +/// A node with no children. +template +using leaf_node = node; + +/// A node with exactly one child. +template +struct decorator_node: node +{ + node* child; +}; + +/// A node that can have one or more children. +template +struct composite_node: node +{ + std::list children; +}; + +/// Executes a function on a context and returns the status. +template +struct action: leaf_node +{ + virtual status execute(context_type& context) const final; + typedef std::function function_type; + function_type function; +}; + +/// Evaluates a boolean condition (predicate) and returns either `status::success` or `status::failure`. +template +struct condition: leaf_node +{ + virtual status execute(context_type& context) const final; + typedef std::function predicate_type; + predicate_type predicate; +}; + +/// Executes a child node and returns its inverted status. If the child returns `status::success`, then `status::failure` will be returned. Otherwise if the child returns `status::failure`, then `status::success` will be returned. +template +struct inverter: decorator_node +{ + virtual status execute(context_type& context) const final; +}; + +/// Attempts to execute a child node `n` times or until the child fails. +template +struct repeater: decorator_node +{ + virtual status execute(context_type& context) const final; + int n; +}; + +/// Executes a child node and returns `status::success` regardless of the child node status. +template +struct succeeder: decorator_node +{ + virtual status execute(context_type& context) const final; +}; + +/// Attempts to execute each child node sequentially until one fails. If all children are executed successfully, `status::success` will be returned. Otherwise if any children fail, `status::failure` will be returned. +template +struct sequence: composite_node +{ + virtual status execute(context_type& context) const final; +}; + +/// Attempts to execute each child node sequentially until one succeeds. If a child succeeds, `status::success` will be returned. Otherwise if all children fail, `status::failure` will be returned. +template +struct selector: composite_node +{ + virtual status execute(context_type& context) const final; +}; + +template +status action::execute(context_type& context) const +{ + return function(context); +} + +template +status condition::execute(context_type& context) const +{ + return (predicate(context)) ? status::success : status::failure; +} + +template +status inverter::execute(context_type& context) const +{ + status child_status = child->execute(context); + return (child_status == status::success) ? status::failure : (child_status == status::failure) ? status::success : child_status; +} + +template +status repeater::execute(context_type& context) const +{ + status child_status; + for (int i = 0; i < n; ++i) + { + child_status = child->execute(context); + if (child_status == status::failure) + break; + } + return child_status; +} + +template +status succeeder::execute(context_type& context) const +{ + child->execute(context); + return status::success; +} + +template +status sequence::execute(context_type& context) const +{ + for (const node* child: children) + { + status child_status = child->execute(context); + if (child_status != status::success) + return child_status; + } + return status::success; +} + +template +status selector::execute(context_type& context) const +{ + for (const node* child: children) + { + status child_status = child->execute(context); + if (child_status != status::failure) + return child_status; + } + return status::failure; +} + +} // namespace behavior_tree + +#endif // ANTKEEPER_BEHAVIOR_TREE_HPP + diff --git a/src/antkeeper/behavior/ebt.cpp b/src/antkeeper/behavior/ebt.cpp new file mode 100644 index 0000000..0232454 --- /dev/null +++ b/src/antkeeper/behavior/ebt.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 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 "behavior/ebt.hpp" +#include "entity/components/transform-component.hpp" +#include + +using namespace ecs; + +namespace ebt { + +status print(context& context, const std::string& text) +{ + std::cout << text; + return status::success; +} + +status print_eid(context& context) +{ + std::cout << context.entity << std::endl; + return status::success; +} + +status warp_to(context& context, float x, float y, float z) +{ + auto& transform = context.registry->get(context.entity); + transform.transform.translation = {x, y, z}; + transform.warp = true; + return status::success; +} + +bool is_carrying_food(const context& context) +{ + return false; +} + +} // namespace ebt + diff --git a/src/antkeeper/behavior/ebt.hpp b/src/antkeeper/behavior/ebt.hpp new file mode 100644 index 0000000..8e5cfee --- /dev/null +++ b/src/antkeeper/behavior/ebt.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 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_EBT_HPP +#define ANTKEEPER_EBT_HPP + +#include "behavior/behavior-tree.hpp" +#include + +/// Entity Behavior Tree + +/** + * The `ebt` namespace defines Entity Behavior Tree (EBT) nodes and an EBT context, on which EBT nodes operate. + */ +namespace ebt { + +/** + * EBT context which references an entity and its registry. + */ +struct context +{ + entt::registry* registry; + entt::entity entity; +}; + +typedef behavior_tree::status status; +typedef behavior_tree::node node; +typedef behavior_tree::leaf_node leaf_node; +typedef behavior_tree::decorator_node decorator_node; +typedef behavior_tree::composite_node composite_node; +typedef behavior_tree::action action; +typedef behavior_tree::condition condition; +typedef behavior_tree::inverter inverter; +typedef behavior_tree::repeater repeater; +typedef behavior_tree::succeeder succeeder; +typedef behavior_tree::sequence sequence; +typedef behavior_tree::selector selector; + +// Actions +status print(context& context, const std::string& text); +status print_eid(context& context); +status warp_to(context& context, float x, float y, float z); + +// Conditions +bool is_carrying_food(const context& context); + +} // namespace ebt + +#endif // ANTKEEPER_EBT_HPP + diff --git a/src/antkeeper/camera-rig.cpp b/src/antkeeper/camera-rig.cpp new file mode 100644 index 0000000..791a284 --- /dev/null +++ b/src/antkeeper/camera-rig.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 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 "orbit-cam.hpp" +#include "scene/camera.hpp" +#include "configuration.hpp" +#include +#include + +using namespace vmq::operators; + +camera_rig::camera_rig(): + camera(nullptr), + translation{0.0f, 0.0f, 0.0f}, + rotation(vmq::identity_quaternion), + forward(global_forward), + right(global_right), + up(global_up) +{} + +void camera_rig::attach(::camera* camera) +{ + this->camera = camera; + if (camera != nullptr) + { + camera->look_at(translation, translation + forward, up); + } +} + +void camera_rig::detach() +{ + camera = nullptr; +} + +void camera_rig::set_translation(const float3& translation) +{ + this->translation = translation; +} + +void camera_rig::set_rotation(const vmq::quaternion& rotation) +{ + this->rotation = rotation; + + // Calculate orthonormal basis + forward = rotation * global_forward; + up = rotation * global_up; + right = rotation * global_right; +} + diff --git a/src/antkeeper/camera-rig.hpp b/src/antkeeper/camera-rig.hpp new file mode 100644 index 0000000..5c355d2 --- /dev/null +++ b/src/antkeeper/camera-rig.hpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2020 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_CAMERA_RIG_HPP +#define ANTKEEPER_CAMERA_RIG_HPP + +#include + +using namespace vmq::types; + +class camera; + +/** + * Abstract base class for camera rigs which control the movement of cameras. + */ +class camera_rig +{ +public: + camera_rig(); + + /** + * Updates the rig. + * + * @param dt Delta time. + */ + virtual void update(float dt) = 0; + + /** + * Attaches a camera to the rig. + * + * @param camera Camera to attach. + */ + void attach(::camera* camera); + + /** + * Detaches a camera from the rig. + */ + void detach(); + + /** + * Sets the translation of the camera rig. + */ + void set_translation(const float3& translation); + + /** + * Sets the rotation of the camera rig. + */ + void set_rotation(const vmq::quaternion& rotation); + + /** + * Returns the attached camera. + */ + const ::camera* get_camera() const; + + /// @copydoc camera_rig::get_camera() const + ::camera* get_camera(); + + const float3& get_translation() const; + const vmq::quaternion& get_rotation() const; + const float3& get_forward() const; + const float3& get_right() const; + const float3& get_up() const; + +private: + camera* camera; + float3 translation; + vmq::quaternion rotation; + float3 forward; + float3 right; + float3 up; +}; + +inline const camera* camera_rig::get_camera() const +{ + return camera; +} + +inline camera* camera_rig::get_camera() +{ + return camera; +} + +inline const float3& camera_rig::get_translation() const +{ + return translation; +} + +inline const vmq::quaternion& camera_rig::get_rotation() const +{ + return rotation; +} + +inline const float3& camera_rig::get_forward() const +{ + return forward; +} + +inline const float3& camera_rig::get_right() const +{ + return right; +} + +inline const float3& camera_rig::get_up() const +{ + return up; +} + +#endif // ANTKEEPER_CAMERA_RIG_HPP + diff --git a/src/antkeeper/configuration.hpp.in b/src/antkeeper/configuration.hpp.in new file mode 100644 index 0000000..1b314b1 --- /dev/null +++ b/src/antkeeper/configuration.hpp.in @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 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_CONFIGURATION_HPP +#define ANTKEEPER_CONFIGURATION_HPP + +#include + +using namespace vmq::types; + +#define ANTKEEPER_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ +#define ANTKEEPER_VERSION_MINOR @PROJECT_VERSION_MINOR@ +#define ANTKEEPER_VERSION_PATCH @PROJECT_VERSION_PATCH@ +#define ANTKEEPER_VERSION_STRING "@PROJECT_VERSION@" + +constexpr float3 global_forward = {0.0f, 0.0f, -1.0f}; +constexpr float3 global_up = {0.0f, 1.0f, 0.0f}; +constexpr float3 global_right = {1.0f, 0.0f, 0.0f}; + +#define MATERIAL_PASS_MAX_AMBIENT_LIGHT_COUNT 1 +#define MATERIAL_PASS_MAX_POINT_LIGHT_COUNT 1 +#define MATERIAL_PASS_MAX_DIRECTIONAL_LIGHT_COUNT 1 +#define MATERIAL_PASS_MAX_SPOTLIGHT_COUNT 1 +#define TERRAIN_PATCH_SIZE 200.0f +#define TERRAIN_PATCH_RESOLUTION 4 +#define VEGETATION_PATCH_RESOLUTION 1 + +#endif // ANTKEEPER_CONFIGURATION_HPP diff --git a/src/antkeeper/debug/ansi-codes.hpp b/src/antkeeper/debug/ansi-codes.hpp new file mode 100644 index 0000000..c3eba85 --- /dev/null +++ b/src/antkeeper/debug/ansi-codes.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 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_ANSI_CODES_HPP +#define ANTKEEPER_ANSI_CODES_HPP + +namespace ansi { + +constexpr char* reset = "\u004b[0m"; + +constexpr char* black = "\u003b[30m"; +constexpr char* red = "\u003b[31m"; +constexpr char* green = "\u003b[32m"; +constexpr char* yellow = "\u003b[33m"; +constexpr char* blue = "\u003b[34m"; +constexpr char* magenta = "\u003b[35m"; +constexpr char* cyan = "\u003b[36m"; +constexpr char* white = "\u003b[37m"; + +constexpr char* bright_black = "\u003b[30;1m"; +constexpr char* bright_red = "\u003b[31;1m"; +constexpr char* bright_green = "\u003b[32;1m"; +constexpr char* bright_yellow = "\u003b[33;1m"; +constexpr char* bright_blue = "\u003b[34;1m"; +constexpr char* bright_magenta = "\u003b[35;1m"; +constexpr char* bright_cyan = "\u003b[36;1m"; +constexpr char* bright_white = "\u003b[37;1m"; + +constexpr char* background_black = "\u003b[40m"; +constexpr char* background_red = "\u003b[41m"; +constexpr char* background_green = "\u003b[42m"; +constexpr char* background_yellow = "\u003b[43m"; +constexpr char* background_blue = "\u003b[44m"; +constexpr char* background_magenta = "\u003b[45m"; +constexpr char* background_cyan = "\u003b[46m"; +constexpr char* background_white = "\u003b[47m"; + +constexpr char* background_bright_black = "\u003b[40;1m"; +constexpr char* background_bright_red = "\u003b[41;1m"; +constexpr char* background_bright_green = "\u003b[42;1m"; +constexpr char* background_bright_yellow = "\u003b[43;1m"; +constexpr char* background_bright_blue = "\u003b[44;1m"; +constexpr char* background_bright_magenta = "\u003b[45;1m"; +constexpr char* background_bright_cyan = "\u003b[46;1m"; +constexpr char* background_bright_white = "\u003b[47;1m"; + +constexpr char* bold = "\u003b[1m"; +constexpr char* underline = "\u003b[4m"; +constexpr char* reversed = "\u001b[7m"; + +} // namespace ansi + +#endif // ANTKEEPER_ANSI_CODES_HPP + diff --git a/src/antkeeper/debug/cli.hpp b/src/antkeeper/debug/cli.hpp new file mode 100644 index 0000000..8df45dd --- /dev/null +++ b/src/antkeeper/debug/cli.hpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2020 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_CLI_HPP +#define ANTKEEPER_CLI_HPP + +#include +#include +#include +#include +#include + +/** + * Minimal command-line interpreter. + */ +class cli +{ +public: + /// String-wrapped function object + typedef std::function command_type; + + /** + * Interprets a command line as a function invocation. + * + * @param line Command line. + * @return Stringified return value of the command function. + */ + std::string interpret(const std::string& line) const; + + /** + * Registers a command with the CLI. + * + * @tparam T Command function return type. + * @tparam Args Command function argument types. + * @param name Command name. + * @param function Command function. + */ + template + void register_command(const std::string& name, const std::function& function); + + /// @copydoc register_command(const std::string, const std::function&) + template + void register_command(const std::string& name, T (*function)(Args...)); + + /** + * Unregisters a command from the CLI. + * + * @param name Command name. + */ + void unregister_command(const std::string& name); + +private: + /** + * Parses a single argument from a string stream. + * + * @param stream String stream from which an argument should be parsed. + */ + template + static T parse(std::istringstream& stream); + + /** + * Wraps a function in an interpreter function that parses strings as arguments then executes the wrapped function with the parsed arguments. + * + * @param function Function to wrap. + * @return Wrapped function. + */ + template + static command_type wrap(const std::function& function); + + std::map commands; +}; + +std::string cli::interpret(const std::string& line) const +{ + std::istringstream stream(line); + std::string command_name; + stream >> command_name; + + if (auto it = commands.find(command_name); it != commands.end()) + { + return it->second(line.substr(command_name.length() + 1)); + } + + return std::string(); +} + +template +void cli::register_command(const std::string& name, const std::function& function) +{ + commands[name] = wrap(function); +} + +template +void cli::register_command(const std::string& name, T (*function)(Args...)) +{ + commands[name] = wrap(std::function(function)); +} + +void cli::unregister_command(const std::string& name) +{ + if (auto it = commands.find(name); it != commands.end()) + commands.erase(it); +} + +template +T cli::parse(std::istringstream& stream) +{ + T value; + stream >> value; + return value; +} + +template +typename cli::command_type cli::wrap(const std::function& function) +{ + return std::bind( + [function](const std::string& line) -> std::string + { + //std::size_t argument_count = sizeof...(Args); + + // Parse string into tuple of arguments + std::istringstream istream(line); + std::tuple arguments{parse(istream)...}; + + // Invoke function with arguments and save the result + T result = std::apply(function, arguments); + + // Return function result as string + std::ostringstream ostream; + ostream << result; + return ostream.str(); + }, + std::placeholders::_1); +} + +#endif // ANTKEEPER_CLI_HPP + diff --git a/src/antkeeper/debug/logger.cpp b/src/antkeeper/debug/logger.cpp new file mode 100644 index 0000000..daceef5 --- /dev/null +++ b/src/antkeeper/debug/logger.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2020 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 "logger.hpp" +#include + +logger::logger(): + os(&std::cout), + log_prefix(std::string()), + log_postfix(std::string()), + warning_prefix(std::string("Warning: ")), + warning_postfix(std::string()), + error_prefix(std::string("Error: ")), + error_postfix(std::string()), + success_prefix(std::string("Success: ")), + success_postfix(std::string()) +{} + +logger::~logger() +{} + +void logger::redirect(std::ostream* stream) +{ + os = stream; +} + +void logger::log(const std::string& text) +{ + if (os) + { + for (const std::string& prefix: prefix_stack) + { + (*os) << prefix; + } + + (*os) << (log_prefix + text + log_postfix); + + os->flush(); + } +} + +void logger::warning(const std::string& text) +{ + log(warning_prefix + text + warning_postfix); +} + +void logger::error(const std::string& text) +{ + log(error_prefix + text + error_postfix); +} + +void logger::success(const std::string& text) +{ + log(success_prefix + text + success_postfix); +} + +void logger::set_log_prefix(const std::string& prefix) +{ + log_prefix = prefix; +} + +void logger::set_log_postfix(const std::string& postfix) +{ + log_postfix = postfix; +} + +void logger::set_warning_prefix(const std::string& prefix) +{ + warning_prefix = prefix; +} + +void logger::set_warning_postfix(const std::string& postfix) +{ + warning_postfix = postfix; +} + +void logger::set_error_prefix(const std::string& prefix) +{ + error_prefix = prefix; +} + +void logger::set_error_postfix(const std::string& postfix) +{ + error_postfix = postfix; +} + +void logger::set_success_prefix(const std::string& prefix) +{ + success_prefix = prefix; +} + +void logger::set_success_postfix(const std::string& postfix) +{ + success_postfix = postfix; +} + +void logger::push_prefix(const std::string& prefix) +{ + prefix_stack.push_back(prefix); +} + +void logger::pop_prefix() +{ + prefix_stack.pop_back(); +} + diff --git a/src/antkeeper/debug/logger.hpp b/src/antkeeper/debug/logger.hpp new file mode 100644 index 0000000..86e73ff --- /dev/null +++ b/src/antkeeper/debug/logger.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 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_LOGGER_HPP +#define ANTKEEPER_LOGGER_HPP + +#include +#include +#include + +class logger +{ +public: + logger(); + ~logger(); + + /** + * Redirects log output to the specified output stream. + * + * @param stream Output stream to which log text will be written. + */ + void redirect(std::ostream* stream); + /** + * Outputs text to the log. + */ + void log(const std::string& text); + + void warning(const std::string& text); + void error(const std::string& text); + void success(const std::string& text); + + void set_log_prefix(const std::string& prefix); + void set_log_postfix(const std::string& postfix); + void set_warning_prefix(const std::string& prefix); + void set_warning_postfix(const std::string& postfix); + void set_error_prefix(const std::string& prefix); + void set_error_postfix(const std::string& postfix); + void set_success_prefix(const std::string& prefix); + void set_success_postfix(const std::string& postfix); + + void push_prefix(const std::string& prefix); + void pop_prefix(); + +private: + std::ostream* os; + std::string log_prefix; + std::string log_postfix; + std::string warning_prefix; + std::string warning_postfix; + std::string error_prefix; + std::string error_postfix; + std::string success_prefix; + std::string success_postfix; + std::list prefix_stack; +}; + +#endif // ANTKEEPER_LOGGER_HPP + diff --git a/src/antkeeper/debug/performance-sampler.cpp b/src/antkeeper/debug/performance-sampler.cpp new file mode 100644 index 0000000..a139c93 --- /dev/null +++ b/src/antkeeper/debug/performance-sampler.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2020 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 "performance-sampler.hpp" +#include +#include + +performance_sampler::performance_sampler(): + sample_size(1), + sample_index(0) +{} + +void performance_sampler::sample(double duration) +{ + if (samples.size() < sample_size) + { + samples.push_back(duration); + } + else + { + samples[sample_index] = duration; + sample_index = (sample_index + 1) % samples.size(); + } +} + +void performance_sampler::reset() +{ + samples.clear(); + sample_index = 0; +} + +void performance_sampler::set_sample_size(std::size_t size) +{ + sample_size = size; + if (samples.size() > size) + { + samples.resize(size); + sample_index = 0; + } +} + +double performance_sampler::mean_frame_duration() const +{ + return std::accumulate(samples.begin(), samples.end(), 0.0) / static_cast(samples.size()); +} + diff --git a/src/antkeeper/debug/performance-sampler.hpp b/src/antkeeper/debug/performance-sampler.hpp new file mode 100644 index 0000000..89efa8e --- /dev/null +++ b/src/antkeeper/debug/performance-sampler.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 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_PERFORMANCE_SAMPLER_HPP +#define ANTKEEPER_PERFORMANCE_SAMPLER_HPP + +#include +#include + +/** + * Measures a rolling mean frame duration. + */ +class performance_sampler +{ +public: + /// Creates a performance sampler. + performance_sampler(); + + /** + * Adds a frame duration to the sample. + * + * @param duration Duration of the frame. + */ + void sample(double duration); + + /** + * Resets the sampling process. + */ + void reset(); + + /** + * Sets the number of frames in a sample. + * + * @param size Number of frames in a sample. + */ + void set_sample_size(std::size_t size); + + /** + * Returns the mean frame duration. + */ + double mean_frame_duration() const; + +private: + std::vector samples; + std::size_t sample_size; + std::size_t sample_index; +}; + +#endif // ANTKEEPER_PERFORMANCE_SAMPLER_HPP + diff --git a/src/game/curl-noise.hpp b/src/antkeeper/entity/archetype.hpp similarity index 50% rename from src/game/curl-noise.hpp rename to src/antkeeper/entity/archetype.hpp index cba105f..dd25dff 100644 --- a/src/game/curl-noise.hpp +++ b/src/antkeeper/entity/archetype.hpp @@ -1,28 +1,32 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#ifndef CURL_NOISE_HPP -#define CURL_NOISE_HPP +#ifndef ANTKEEPER_ECS_ARCHETYPE_HPP +#define ANTKEEPER_ECS_ARCHETYPE_HPP -#include -using namespace Emergent; +#include -Vector3 curl(const Vector3& p, const Vector3& offset, float frequency); +namespace ecs { + +typedef entt::prototype archetype; + +} // namespace ecs + +#endif // ANTKEEPER_ECS_ARCHETYPE_HPP -#endif // CURL_NOISE_HPP diff --git a/src/antkeeper/entity/components/behavior-component.hpp b/src/antkeeper/entity/components/behavior-component.hpp new file mode 100644 index 0000000..3ec8952 --- /dev/null +++ b/src/antkeeper/entity/components/behavior-component.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 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_ECS_BEHAVIOR_COMPONENT_HPP +#define ANTKEEPER_ECS_BEHAVIOR_COMPONENT_HPP + +#include "behavior/ebt.hpp" + +namespace ecs { + +struct behavior_component +{ + const ebt::node* behavior_tree; +}; + +} // namespace ecs + +#endif // ANTKEEPER_ECS_BEHAVIOR_COMPONENT_HPP + diff --git a/src/antkeeper/entity/components/cavity-component.hpp b/src/antkeeper/entity/components/cavity-component.hpp new file mode 100644 index 0000000..bb6f655 --- /dev/null +++ b/src/antkeeper/entity/components/cavity-component.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 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_ECS_CAVITY_COMPONENT_HPP +#define ANTKEEPER_ECS_CAVITY_COMPONENT_HPP + +#include +using namespace vmq::types; + +namespace ecs { + +struct cavity_component +{ + float3 position; + float radius; +}; + +} // namespace ecs + +#endif // ANTKEEPER_ECS_CAVITY_COMPONENT_HPP + diff --git a/src/antkeeper/entity/components/collision-component.hpp b/src/antkeeper/entity/components/collision-component.hpp new file mode 100644 index 0000000..092602c --- /dev/null +++ b/src/antkeeper/entity/components/collision-component.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 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_ECS_COLLISION_COMPONENT_HPP +#define ANTKEEPER_ECS_COLLISION_COMPONENT_HPP + +#include "geometry/aabb.hpp" +#include "geometry/mesh-accelerator.hpp" + +class mesh; + +namespace ecs { + +struct collision_component +{ + mesh* mesh; + aabb bounds; + mesh_accelerator mesh_accelerator; +}; + +} // namespace ecs + +#endif // ANTKEEPER_ECS_COLLISION_COMPONENT_HPP + diff --git a/src/antkeeper/entity/components/locomotion-component.hpp b/src/antkeeper/entity/components/locomotion-component.hpp new file mode 100644 index 0000000..ed2ec3d --- /dev/null +++ b/src/antkeeper/entity/components/locomotion-component.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 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_ECS_PLACEMENT_COMPONENT_HPP +#define ANTKEEPER_ECS_PLACEMENT_COMPONENT_HPP + +#include "geometry/mesh.hpp" +#include +using namespace vmq::types; + +namespace ecs { + +struct locomotion_component +{ + const mesh::face* triangle; + float3 barycentric_position; +}; + +} // namespace ecs + +#endif // ANTKEEPER_ECS_PLACEMENT_COMPONENT_HPP + diff --git a/src/antkeeper/entity/components/model-component.hpp b/src/antkeeper/entity/components/model-component.hpp new file mode 100644 index 0000000..fb141cc --- /dev/null +++ b/src/antkeeper/entity/components/model-component.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 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_ECS_MODEL_COMPONENT_HPP +#define ANTKEEPER_ECS_MODEL_COMPONENT_HPP + +#include "renderer/model.hpp" +#include + +namespace ecs { + +struct model_component +{ + model* model; + std::unordered_map materials; + int instance_count; +}; + +} // namespace ecs + +#endif // ANTKEEPER_ECS_MODEL_COMPONENT_HPP + diff --git a/src/antkeeper/entity/components/nest-component.hpp b/src/antkeeper/entity/components/nest-component.hpp new file mode 100644 index 0000000..183d5dd --- /dev/null +++ b/src/antkeeper/entity/components/nest-component.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 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_ECS_NEST_COMPONENT_HPP +#define ANTKEEPER_ECS_NEST_COMPONENT_HPP + +namespace ecs { + +struct nest_component +{ + int bla; +}; + +} // namespace ecs + +#endif // ANTKEEPER_ECS_NEST_COMPONENT_HPP + diff --git a/src/antkeeper/entity/components/placement-component.hpp b/src/antkeeper/entity/components/placement-component.hpp new file mode 100644 index 0000000..e694c79 --- /dev/null +++ b/src/antkeeper/entity/components/placement-component.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 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_ECS_PLACEMENT_COMPONENT_HPP +#define ANTKEEPER_ECS_PLACEMENT_COMPONENT_HPP + +#include "geometry/ray.hpp" + +namespace ecs { + +struct placement_component +{ + ::ray ray; +}; + +} // namespace ecs + +#endif // ANTKEEPER_ECS_PLACEMENT_COMPONENT_HPP + diff --git a/src/antkeeper/entity/components/samara-component.hpp b/src/antkeeper/entity/components/samara-component.hpp new file mode 100644 index 0000000..3c7dc31 --- /dev/null +++ b/src/antkeeper/entity/components/samara-component.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 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_ECS_SAMARA_COMPONENT_HPP +#define ANTKEEPER_ECS_SAMARA_COMPONENT_HPP + +#include +using namespace vmq::types; + +namespace ecs { + +struct samara_component +{ + float3 direction; + float angle; + float chirality; +}; + +} // namespace ecs + +#endif // ANTKEEPER_ECS_SAMARA_COMPONENT_HPP + diff --git a/src/antkeeper/entity/components/terrain-component.hpp b/src/antkeeper/entity/components/terrain-component.hpp new file mode 100644 index 0000000..aef93ab --- /dev/null +++ b/src/antkeeper/entity/components/terrain-component.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 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_ECS_TERRAIN_COMPONENT_HPP +#define ANTKEEPER_ECS_TERRAIN_COMPONENT_HPP + +namespace ecs { + +struct terrain_component +{ + int subdivisions; + int x; + int z; +}; + +} // namespace ecs + +#endif // ANTKEEPER_ECS_TERRAIN_COMPONENT_HPP + diff --git a/src/antkeeper/entity/components/tool-component.hpp b/src/antkeeper/entity/components/tool-component.hpp new file mode 100644 index 0000000..5484be5 --- /dev/null +++ b/src/antkeeper/entity/components/tool-component.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 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_ECS_TOOL_COMPONENT_HPP +#define ANTKEEPER_ECS_TOOL_COMPONENT_HPP + +namespace ecs { + +struct tool_component +{ + bool active; +}; + +} // namespace ecs + +#endif // ANTKEEPER_ECS_TOOL_COMPONENT_HPP + diff --git a/src/antkeeper/entity/components/transform-component.hpp b/src/antkeeper/entity/components/transform-component.hpp new file mode 100644 index 0000000..2b89e97 --- /dev/null +++ b/src/antkeeper/entity/components/transform-component.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 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_ECS_TRANSFORM_COMPONENT_HPP +#define ANTKEEPER_ECS_TRANSFORM_COMPONENT_HPP + +#include + +namespace ecs { + +struct transform_component +{ + vmq::transform transform; + bool warp; +}; + +} // namespace ecs + +#endif // ANTKEEPER_ECS_TRANSFORM_COMPONENT_HPP + diff --git a/src/antkeeper/event/event-dispatcher.cpp b/src/antkeeper/event/event-dispatcher.cpp new file mode 100644 index 0000000..b2b230f --- /dev/null +++ b/src/antkeeper/event/event-dispatcher.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2020 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 "event-dispatcher.hpp" + +event_dispatcher::event_dispatcher() +{} + +event_dispatcher::~event_dispatcher() +{ + clear(); +} + +void event_dispatcher::update(double time) +{ + // Process pending subscriptions + for (auto it = to_subscribe.begin(); it != to_subscribe.end(); ++it) + { + handler_map[std::get<0>(*it)].push_back(std::get<1>(*it)); + } + to_subscribe.clear(); + + // Process pending unsubscriptions + for (auto it = to_unsubscribe.begin(); it != to_unsubscribe.end(); ++it) + { + handler_map[std::get<0>(*it)].remove(std::get<1>(*it)); + } + to_unsubscribe.clear(); + + // Dispatch queued events + flush(); + + // For each scheduled event + for (auto event = scheduled_events.begin(); event != scheduled_events.end();) + { + // If the event is due + if (time >= event->first) + { + // Dispatch event + dispatch(*(event->second)); + + // Delete event + delete event->second; + event = scheduled_events.erase(event); + } + else + { + break; + } + } +} + +void event_dispatcher::flush() +{ + // For each event in the queue + for (auto event = queued_events.begin(); event != queued_events.end(); ++event) + { + // Dispatch event + dispatch(**event); + + // Delete event + delete (*event); + } + + // Clear event queue + queued_events.clear(); +} + +void event_dispatcher::clear() +{ + // For each event in the queue + for (auto event = queued_events.begin(); event != queued_events.end(); ++event) + { + // Delete event + delete (*event); + } + + // Clear event queue + queued_events.clear(); + + // For each scheduled event + for (auto event = scheduled_events.begin(); event != scheduled_events.end(); ++event) + { + // Delete event + delete event->second; + } + + // Clear scheduled events + scheduled_events.clear(); +} + diff --git a/src/antkeeper/event/event-dispatcher.hpp b/src/antkeeper/event/event-dispatcher.hpp new file mode 100644 index 0000000..e4f1c07 --- /dev/null +++ b/src/antkeeper/event/event-dispatcher.hpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2020 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_EVENT_DISPATCHER_HPP +#define ANTKEEPER_EVENT_DISPATCHER_HPP + +#include "event.hpp" +#include "event-handler.hpp" +#include +#include +#include + +/** + * Queues events and dispatches them to event handlers. + */ +class event_dispatcher +{ +public: + /// Creates an event dispatcher + event_dispatcher(); + + /// Destroys an event dispatcher + ~event_dispatcher(); + + /** + * Processes all pending subscriptions and unsubscriptions, dispatches queued events, then dispatches due scheduled events. + * + * @param time The current time. + */ + void update(double time); + + /** + * Subscribes an event handler to event dispatches. + * + * @param handler Handler to subscribe. + */ + template + void subscribe(event_handler* handler); + + /** + * Unsubscribes an event handler from event dispatches. + * + * @param handler Handler to unsubscribe. + */ + template + void unsubscribe(event_handler* handler); + + /** + * Adds an event to the queue. + * + * @param event Event to queue. + */ + void queue(const event_base& event); + + /** + * Schedules an event to be dispatched at a specific time. + * + * @param event Event to schedule. + * @param time Time that the event should be dispatched. + */ + void schedule(const event_base& event, double time); + + /** + * Dispatches a single event. + * + * @param event Event to dispatch. + */ + void dispatch(const event_base& event); + + /** + * Dispatches all events in the queue. + */ + void flush(); + + /// Removes all queued and scheduled events from the queue without notifying handlers. + void clear(); + +private: + std::list> to_subscribe; + std::list> to_unsubscribe; + std::map> handler_map; + std::list queued_events; + std::multimap scheduled_events; +}; + +template +void event_dispatcher::subscribe(event_handler* handler) +{ + to_subscribe.push_back(std::make_tuple(handler->get_handled_event_type_id(), handler)); +} + +template +void event_dispatcher::unsubscribe(event_handler* handler) +{ + to_unsubscribe.push_back(std::make_tuple(handler->get_handled_event_type_id(), handler)); +} + +inline void event_dispatcher::queue(const event_base& event) +{ + queued_events.push_back(event.clone()); +} + +inline void event_dispatcher::schedule(const event_base& event, double time) +{ + scheduled_events.insert(std::pair(time, event.clone())); +} + +inline void event_dispatcher::dispatch(const event_base& event) +{ + // Get list of handlers for this type of event + const std::list& handlers = handler_map[event.get_event_type_id()]; + + // For each handler + for (auto handler = handlers.begin(); handler != handlers.end(); ++handler) + { + // Pass event to the handler + (*handler)->route_event(event); + } +} + +#endif // ANTKEEPER_EVENT_DISPATCHER_HPP + diff --git a/src/antkeeper/event/event-handler.hpp b/src/antkeeper/event/event-handler.hpp new file mode 100644 index 0000000..4f27bd7 --- /dev/null +++ b/src/antkeeper/event/event-handler.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 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_EVENT_HANDLER_HPP +#define ANTKEEPER_EVENT_HANDLER_HPP + +#include "event.hpp" +#include + +class event_dispatcher; + +/** + * Abstract base class for event handlers. + */ +class event_handler_base +{ +private: + friend class event_dispatcher; + + /** + * Receives an event, casts it to its derived event type, then handles it. + * + * @param event Received event. + */ + virtual void route_event(const event_base& event) = 0; +}; + +/** + * Templates abstract base class for event handlers. + * + * @tparam Event type. + */ +template +class event_handler: public event_handler_base +{ +public: + static_assert(std::is_base_of::value, "T must be a descendant of event_base."); + + /// Returns the unique event type identifier for the event type handled by this event handler. + const std::size_t get_handled_event_type_id() const; + + /** + * Handles an event of type T. + * + * @param event Event to handle. + */ + virtual void handle_event(const T& event) = 0; + +private: + /// @copydoc event_handler_base::route_event() + virtual void route_event(const event_base& event) final; +}; + +template +inline const std::size_t event_handler::get_handled_event_type_id() const +{ + return T::event_type_id; +} + +template +void event_handler::route_event(const event_base& event) +{ + handle_event(static_cast(event)); +} + +#endif // ANTKEEPER_EVENT_HANDLER_HPP + diff --git a/src/antkeeper/event/event.hpp b/src/antkeeper/event/event.hpp new file mode 100644 index 0000000..12b1e97 --- /dev/null +++ b/src/antkeeper/event/event.hpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 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_EVENT_HPP +#define ANTKEEPER_EVENT_HPP + +#include +#include + +/** + * Abstract base class for events. + */ +class event_base +{ +public: + /// Destroys an event base. + virtual ~event_base() = default; + + /// Returns the unique event type identifier for this event type. + virtual const std::size_t get_event_type_id() const = 0; + + /** + * Allocates a copy of this event. + * + * @return Newly allocated copy of this event. + */ + virtual event_base* clone() const = 0; + +protected: + /// Returns then increments the next available event type ID. + static std::size_t next_event_type_id(); +}; + +inline std::size_t event_base::next_event_type_id() +{ + static std::atomic next_event_type_id{0}; + return next_event_type_id++; +} + +/** + * Templated abstract base class for events. + * + * @tparam T The derived class. + */ +template +class event: public event_base +{ +public: + /// The unique event type identifier for this event type. + static const std::atomic event_type_id; + + /// Destroys an event + virtual ~event() = default; + + virtual const std::size_t get_event_type_id() const final; + virtual event_base* clone() const = 0; +}; + +template +const std::atomic event::event_type_id{event_base::next_event_type_id()}; + +template +inline const std::size_t event::get_event_type_id() const +{ + return event_type_id; +} + +#endif // ANTKEEPER_EVENT_HPP + diff --git a/src/filesystem.cpp b/src/antkeeper/filesystem.cpp similarity index 66% rename from src/filesystem.cpp rename to src/antkeeper/filesystem.cpp index f2e93ee..5a0536b 100644 --- a/src/filesystem.cpp +++ b/src/antkeeper/filesystem.cpp @@ -1,20 +1,20 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ #include "filesystem.hpp" @@ -49,9 +49,9 @@ } #endif -std::string getExecutablePath() +std::string get_executable_path() { - std::string executablePath; + std::string executable_path; #if defined(_WIN32) // Get executable path on Windows @@ -59,7 +59,7 @@ std::string getExecutablePath() std::wstring wpath(MAX_PATH, L'\0'); GetModuleFileNameW(hModule, &wpath[0], MAX_PATH); wpath.erase(std::find(wpath.begin(), wpath.end(), L'\0'), wpath.end()); - executablePath = narrow(wpath); + executable_path = narrow(wpath); #else // Get executable path on Linux char path[PATH_MAX]; @@ -67,45 +67,45 @@ std::string getExecutablePath() if (length != -1) { path[length] = '\0'; - executablePath = path; + executable_path = path; } #endif - return executablePath; + return executable_path; } -std::string getDataPath(const std::string& applicationName) +std::string get_data_path(const std::string& application_name) { - std::string dataPath; + std::string data_path; #if defined(_WIN32) - std::string executablePath = getExecutablePath(); - std::size_t delimeter = executablePath.find_last_of("\\/") + 1; - dataPath = executablePath.substr(0, delimeter); + std::string executable_path = get_executable_path(); + std::size_t delimeter = executable_path.find_last_of("\\/") + 1; + data_path = executable_path.substr(0, delimeter); #else - std::string executablePath = getExecutablePath(); - std::size_t delimeter = executablePath.find_last_of("\\/") + 1; - dataPath = executablePath.substr(0, delimeter) + std::string("../share/") + applicationName + std::string("/"); + std::string executable_path = get_executable_path(); + std::size_t delimeter = executable_path.find_last_of("\\/") + 1; + data_path = executable_path.substr(0, delimeter) + std::string("../share/") + application_name + std::string("/"); #endif - return dataPath; + return data_path; } -std::string getConfigPath(const std::string& applicationName) +std::string get_config_path(const std::string& application_name) { - std::string configPath; + std::string config_path; #if defined(_WIN32) std::wstring wpath(MAX_PATH, L'\0'); if (SHGetSpecialFolderPathW(nullptr, &wpath[0], CSIDL_LOCAL_APPDATA, FALSE)) { wpath.erase(std::find(wpath.begin(), wpath.end(), L'\0'), wpath.end()); - configPath = narrow(wpath); - configPath += std::string("\\") + applicationName + std::string("\\"); + config_path = narrow(wpath); + config_path += std::string("\\") + application_name + std::string("\\"); } #else // Determine home path - std::string homePath = std::string(getpwuid(getuid())->pw_dir); + std::string home_path = std::string(getpwuid(getuid())->pw_dir); // Determine config path char* xdgConfigHome = std::getenv("XDG_CONFIG_HOME"); @@ -113,18 +113,18 @@ std::string getConfigPath(const std::string& applicationName) { // Default to $HOME/.config/ as per: // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables - configPath = homePath + std::string("/.config/") + applicationName + std::string("/"); + config_path = home_path + std::string("/.config/") + application_name + std::string("/"); } else { - configPath = xdgConfigHome + std::string("/") + applicationName + std::string("/"); + config_path = xdgConfigHome + std::string("/") + application_name + std::string("/"); } #endif - return configPath; + return config_path; } -bool pathExists(const std::string& path) +bool path_exists(const std::string& path) { #if defined(_WIN32) std::wstring wpath = widen(path); @@ -136,7 +136,7 @@ bool pathExists(const std::string& path) #endif } -bool createDirectory(const std::string& path) +bool create_directory(const std::string& path) { #if defined(_WIN32) std::wstring wpath = widen(path); @@ -145,3 +145,4 @@ bool createDirectory(const std::string& path) return (mkdir(path.c_str(), 0777) == 0); #endif } + diff --git a/src/filesystem.hpp b/src/antkeeper/filesystem.hpp similarity index 50% rename from src/filesystem.hpp rename to src/antkeeper/filesystem.hpp index 1d420f1..498c20d 100644 --- a/src/filesystem.hpp +++ b/src/antkeeper/filesystem.hpp @@ -1,24 +1,24 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#ifndef FILESYSTEM_HPP -#define FILESYSTEM_HPP +#ifndef ANTKEEPER_FILESYSTEM_HPP +#define ANTKEEPER_FILESYSTEM_HPP #include @@ -27,35 +27,35 @@ * * @return Path to the application's executable. */ -std::string getExecutablePath(); +std::string get_executable_path(); /** * Returns the absolute path to the directory containing application data. * - * Windows: executableDirectory - * GNU/Linux: executableDirectory/../share/applicationName/ + * Windows: executable_directory + * GNU/Linux: executable_directory/../share/applicationName/ * - * @param applicationName Name of the application. + * @param application_name Name of the application. * @return Path to the application's data directory. */ -std::string getDataPath(const std::string& applicationName); +std::string get_data_path(const std::string& application_name); /** * Returns the absolute path to the directory containing user-specific application data. * - * Windows: %LOCALAPPDATA%\applicationName\ - * GNU/Linux: $XDG_CONFIG_HOME/applicationName/ or ~/.config/applicationName/ if $XDG_CONFIG_HOME is not set. + * Windows: %LOCALAPPDATA%\application_name\ + * GNU/Linux: $XDG_CONFIG_HOME/application_name/ or ~/.config/application_name/ if $XDG_CONFIG_HOME is not set. * - * @param applicationName Name of the application. + * @param application_name Name of the application. * @return Path to the application's config directory. */ -std::string getConfigPath(const std::string& applicationName); +std::string get_config_path(const std::string& application_name); /// Checks if a file or directory exists -bool pathExists(const std::string& path); +bool path_exists(const std::string& path); /// Creates a directory -bool createDirectory(const std::string& path); +bool create_directory(const std::string& path); -#endif // FILESYSTEM_HPP +#endif // ANTKEEPER_FILESYSTEM_HPP diff --git a/src/antkeeper/frame-scheduler.cpp b/src/antkeeper/frame-scheduler.cpp new file mode 100644 index 0000000..49464dc --- /dev/null +++ b/src/antkeeper/frame-scheduler.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 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 "frame-scheduler.hpp" +#include + +frame_scheduler::frame_scheduler(): + update_callback(nullptr), + render_callback(nullptr), + update_rate(60.0), + update_timestep(1.0 / update_rate), + max_frame_duration(update_timestep) +{ + reset(); +} + +void frame_scheduler::set_update_callback(std::function callback) +{ + update_callback = callback; +} + +void frame_scheduler::set_render_callback(std::function callback) +{ + render_callback = callback; +} + +void frame_scheduler::set_update_rate(double frequency) +{ + update_rate = frequency; + update_timestep = 1.0 / frequency; +} + +void frame_scheduler::set_max_frame_duration(double duration) +{ + max_frame_duration = duration; +} + +double frame_scheduler::get_frame_duration() const +{ + return frame_duration; +} + +void frame_scheduler::reset() +{ + elapsed_time = 0.0; + accumulator = 0.0; + frame_start = std::chrono::high_resolution_clock::now(); + frame_end = frame_start; + frame_duration = 0.0; +} + +void frame_scheduler::tick() +{ + accumulator += std::min(max_frame_duration, frame_duration); + + while (accumulator >= update_timestep) + { + update_callback(elapsed_time, update_timestep); + elapsed_time += update_timestep; + accumulator -= update_timestep; + } + + render_callback(accumulator * update_rate); + + frame_end = std::chrono::high_resolution_clock::now(); + frame_duration = static_cast(std::chrono::duration_cast(frame_end - frame_start).count()) / 1000000.0; + frame_start = frame_end; +} + diff --git a/src/antkeeper/frame-scheduler.hpp b/src/antkeeper/frame-scheduler.hpp new file mode 100644 index 0000000..27ddfdf --- /dev/null +++ b/src/antkeeper/frame-scheduler.hpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 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_FRAME_SCHEDULER_HPP +#define ANTKEEPER_FRAME_SCHEDULER_HPP + +#include +#include + +/** + * Schedules fixed-timestep update calls and variable timestep render calls. + */ +class frame_scheduler +{ +public: + frame_scheduler(); + + /** + * Sets the update callback. + * + * @param callback Function which takes two parameters: `t`, the total elapsed time, and `dt`, delta time (timestep) which is calculated as `1.0 / update_rate`. This function will be called at the frequency specified by `frame_scheduler::set_update_rate()`. + */ + void set_update_callback(std::function callback); + + /** + * Sets the render callback. + * + * @param callback Function which takes one parameter: `alpha`, which is a factor that can be used to interpolate between the previous and current update states. + */ + void set_render_callback(std::function callback); + + /** + * Sets the update rate. + * + * @param frequency Frequency, in hertz, at which the update callback should be called. + */ + void set_update_rate(double frequency); + + /** + * Sets the maximum duration of a frame. This limits the number of times the update callback is called per frame, thereby preventing a "spiral of death", in which the update callback is called too many times per frame while trying to catch up to the target update rate. + * + * @param duration Maximum frame duration, in seconds.. + */ + void set_max_frame_duration(double duration); + + /** + * Returns the duration of the last frame, in seconds. + */ + double get_frame_duration() const; + + /** + * Resets the total elapsed time, frame duration, and internal timers. + */ + void reset(); + + /** + * Updates the internal timers and performs the scheduled update and render callbacks. + */ + void tick(); + +private: + std::function update_callback; + std::function render_callback; + double update_rate; + double update_timestep; + double max_frame_duration; + double elapsed_time; + double accumulator; + std::chrono::high_resolution_clock::time_point frame_start; + std::chrono::high_resolution_clock::time_point frame_end; + double frame_duration; +}; + +#endif // ANTKEEPER_FRAME_SCHEDULER_HPP + diff --git a/src/antkeeper/geometry/aabb.hpp b/src/antkeeper/geometry/aabb.hpp new file mode 100644 index 0000000..2ebc305 --- /dev/null +++ b/src/antkeeper/geometry/aabb.hpp @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2020 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_AABB_HPP +#define ANTKEEPER_AABB_HPP + +#include "bounding-volume.hpp" +#include "sphere.hpp" +#include +#include +#include + +using vmq::vector; +using vmq::matrix; +using vmq::transform; +using namespace vmq::operators; + +template +struct aabb: public bounding_volume +{ + vector min_point; + vector max_point; + + /** + * Transforms an AABB. + * + * @param a AABB to be transformed. + * @param t Transform by which the AABB should be transformed. + * @return Transformed AABB. + */ + static aabb transform(const aabb& a, const ::transform& t); + + /** + * Transforms an AABB. + * + * @param a AABB to be transformed. + * @param m Matrix by which the AABB should be transformed. + * @return Transformed AABB. + */ + static aabb transform(const aabb& a, const matrix& m); + + aabb(const vector& min_point, const vector& max_point); + aabb(); + + virtual bounding_volume_type get_bounding_volume_type() const; + virtual bool intersects(const sphere& sphere) const; + virtual bool intersects(const aabb& aabb) const; + virtual bool contains(const sphere& sphere) const; + virtual bool contains(const aabb& aabb) const; + virtual bool contains(const vector& point) const; + + /** + * Returns the position of the specified corner. + * + * @param index Index of a corner. + * @return Position of the specified corner. + */ + vector corner(int index) const noexcept; +}; + +template +aabb aabb::transform(const aabb& a, const ::transform& t) +{ + vector min_point = {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; + vector max_point = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; + + for (std::size_t i = 0; i < 8; ++i) + { + vector transformed_corner = vmq::mul(t, a.corner(i)); + + for (std::size_t j = 0; j < 3; ++j) + { + min_point[j] = std::min(min_point[j], transformed_corner[j]); + max_point[j] = std::max(max_point[j], transformed_corner[j]); + } + } + + return {min_point, max_point}; +} + +template +aabb aabb::transform(const aabb& a, const matrix& m) +{ + vector min_point = {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; + vector max_point = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; + + for (std::size_t i = 0; i < 8; ++i) + { + vector corner = a.corner(i); + vector transformed_corner = vmq::mul(m, vector{corner.x, corner.y, corner.z, T(1)}); + + for (std::size_t j = 0; j < 3; ++j) + { + min_point[j] = std::min(min_point[j], transformed_corner[j]); + max_point[j] = std::max(max_point[j], transformed_corner[j]); + } + } + + return {min_point, max_point}; +} + +template +aabb::aabb(const vector& min_point, const vector& max_point): + min_point(min_point), + max_point(max_point) +{} + +template +aabb::aabb() +{} + +template +inline bounding_volume_type aabb::get_bounding_volume_type() const +{ + return bounding_volume_type::aabb; +} + +template +bool aabb::intersects(const sphere& sphere) const +{ + const vector radius_vector = {sphere.radius, sphere.radius, sphere.radius}; + return aabb(min_point - radius_vector, max_point + radius_vector).contains(sphere.center); +} + +template +bool aabb::intersects(const aabb& aabb) const +{ + if (max_point.x < aabb.min_point.x || min_point.x > aabb.max_point.x) + return false; + if (max_point.y < aabb.min_point.y || min_point.y > aabb.max_point.y) + return false; + if (max_point.z < aabb.min_point.z || min_point.z > aabb.max_point.z) + return false; + return true; +} + +template +bool aabb::contains(const sphere& sphere) const +{ + if (sphere.center.x - sphere.radius < min_point.x || sphere.center.x + sphere.radius > max_point.x) + return false; + if (sphere.center.y - sphere.radius < min_point.y || sphere.center.y + sphere.radius > max_point.y) + return false; + if (sphere.center.z - sphere.radius < min_point.z || sphere.center.z + sphere.radius > max_point.z) + return false; + return true; +} + +template +bool aabb::contains(const aabb& aabb) const +{ + if (aabb.min_point.x < min_point.x || aabb.max_point.x > max_point.x) + return false; + if (aabb.min_point.y < min_point.y || aabb.max_point.y > max_point.y) + return false; + if (aabb.min_point.z < min_point.z || aabb.max_point.z > max_point.z) + return false; + return true; +} + +template +bool aabb::contains(const vector& point) const +{ + if (point.x < min_point.x || point.x > max_point.x) + return false; + if (point.y < min_point.y || point.y > max_point.y) + return false; + if (point.z < min_point.z || point.z > max_point.z) + return false; + return true; +} + +template +vector aabb::corner(int index) const noexcept +{ + return + { + ((index >> 2) & 1) ? max_point[0] : min_point[0], + ((index >> 1) & 1) ? max_point[1] : min_point[1], + ((index >> 0) & 1) ? max_point[2] : min_point[2] + }; +} + +#endif // ANTKEEPER_AABB_HPP + diff --git a/src/antkeeper/geometry/bounding-volume.hpp b/src/antkeeper/geometry/bounding-volume.hpp new file mode 100644 index 0000000..cb7d60d --- /dev/null +++ b/src/antkeeper/geometry/bounding-volume.hpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2020 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_BOUNDING_VOLUME_HPP +#define ANTKEEPER_BOUNDING_VOLUME_HPP + +#include +#include + +using vmq::vector; + +template +struct sphere; +template +struct aabb; + +/// Enumerates bounding volume types. +enum class bounding_volume_type +{ + aabb, ///< Indicates the bounding volume is an axis-aligned bounding box. + sphere, ///< Indicates the bounding volume is a sphere. + convex_hull ///< Indicates the bounding volume is a convex hull. +}; + +/** + * Abstract base class for bounding volumes. + * + * @tparam T Scalar type. + */ +template +class bounding_volume +{ +public: + /// Returns the enumerated type of this bounding volume. + virtual bounding_volume_type get_bounding_volume_type() const = 0; + + /// Tests for intersection between this bounding volume and a bounding sphere. + virtual bool intersects(const sphere& sphere) const = 0; + + /// Tests for intersection between this bounding volume and an axis-aligned bounding box. + virtual bool intersects(const aabb& aabb) const = 0; + + /// Tests whether this bounding volume contains a sphere. + virtual bool contains(const sphere& sphere) const = 0; + + /// Tests whether this bounding volume contains an axis-aligned bounding box. + virtual bool contains(const aabb& aabb) const = 0; + + /// Tests whether this bounding volume contains a point. + virtual bool contains(const vector& point) const = 0; + + /// Tests for intersection between this bounding volume and another bounding volume. + bool intersects(const bounding_volume& volume) const; +}; + +/// Tests for intersection between this bounding volume and another bounding volume. +template +bool bounding_volume::intersects(const bounding_volume& volume) const +{ + const bounding_volume_type type = volume.get_bounding_volume_type(); + switch (type) + { + case bounding_volume_type::sphere: + return intersects(static_cast&>(volume)); + break; + + case bounding_volume_type::aabb: + return intersects(static_cast&>(volume)); + break; + + default: + throw std::runtime_error("unimplemented"); + break; + } +} + +#endif // ANTKEEPER_BOUNDING_VOLUME_HPP + diff --git a/src/antkeeper/geometry/convex-hull.hpp b/src/antkeeper/geometry/convex-hull.hpp new file mode 100644 index 0000000..a9d2fd6 --- /dev/null +++ b/src/antkeeper/geometry/convex-hull.hpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2020 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_CONVEX_HULL_HPP +#define ANTKEEPER_CONVEX_HULL_HPP + +#include +#include +#include "bounding-volume.hpp" +#include "plane.hpp" +#include "sphere.hpp" +#include "aabb.hpp" + +/** + * A plane-bounded convex hull. + */ +template +struct convex_hull: public bounding_volume +{ + /// Vector of planes with descibe the bounds of the convex hull. + std::vector> planes; + + /** + * Creates a convex hull. + * + * @param size Number of planes the convex hull should accommodate. + */ + convex_hull(std::size_t size); + + /// Creates a convex hull. + convex_hull(); + + virtual bounding_volume_type get_bounding_volume_type() const; + virtual bool intersects(const sphere& sphere) const; + virtual bool intersects(const aabb& aabb) const; + virtual bool contains(const sphere& sphere) const; + virtual bool contains(const aabb& aabb) const; + virtual bool contains(const vector& point) const; +}; + +template +convex_hull::convex_hull(std::size_t size): + planes(size, plane()) +{} + +template +convex_hull::convex_hull() +{} + +template +inline bounding_volume_type convex_hull::get_bounding_volume_type() const +{ + return bounding_volume_type::convex_hull; +} + +template +bool convex_hull::intersects(const sphere& sphere) const +{ + for (const plane& plane: planes) + if (signed_distance(plane, sphere.center) < -sphere.radius) + return false; + return true; +} + +template +bool convex_hull::intersects(const aabb& aabb) const +{ + vector pv; + for (const plane& plane: planes) + { + pv.x = (plane.normal.x > T(0)) ? aabb.max_point.x : aabb.min_point.x; + pv.y = (plane.normal.y > T(0)) ? aabb.max_point.y : aabb.min_point.y; + pv.z = (plane.normal.z > T(0)) ? aabb.max_point.z : aabb.min_point.z; + if (signed_distance(plane, pv) < T(0)) + return false; + } + + return true; +} + +template +bool convex_hull::contains(const sphere& sphere) const +{ + for (const plane& plane: planes) + if (signed_distance(plane, sphere.center) < sphere.radius) + return false; + return true; +} + +template +bool convex_hull::contains(const aabb& aabb) const +{ + vector pv; + vector nv; + + for (const plane& plane: planes) + { + pv.x = (plane.normal.x > T(0)) ? aabb.max_point.x : aabb.min_point.x; + pv.y = (plane.normal.y > T(0)) ? aabb.max_point.y : aabb.min_point.y; + pv.z = (plane.normal.z > T(0)) ? aabb.max_point.z : aabb.min_point.z; + nv.x = (plane.normal.x < T(0)) ? aabb.max_point.x : aabb.min_point.x; + nv.y = (plane.normal.y < T(0)) ? aabb.max_point.y : aabb.min_point.y; + nv.z = (plane.normal.z < T(0)) ? aabb.max_point.z : aabb.min_point.z; + + if (signed_distance(plane, pv) < T(0) || signed_distance(plane, nv) < T(0)) + return false; + } + + return true; +} + +template +bool convex_hull::contains(const vector& point) const +{ + for (const plane& plane: planes) + if (signed_distance(plane, point) < T(0)) + return false; + + return true; +} + +#endif // ANTKEEPER_CONVEX_HULL_HPP + diff --git a/src/antkeeper/geometry/csg.cpp b/src/antkeeper/geometry/csg.cpp new file mode 100644 index 0000000..4fef882 --- /dev/null +++ b/src/antkeeper/geometry/csg.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2020 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 "csg.hpp" +#include + +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 + diff --git a/src/antkeeper/geometry/csg.hpp b/src/antkeeper/geometry/csg.hpp new file mode 100644 index 0000000..94ef6af --- /dev/null +++ b/src/antkeeper/geometry/csg.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 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_CSG_HPP +#define ANTKEEPER_CSG_HPP + +#include +#include + + +namespace csg { + +using vmq::float3; +using vmq::float4; + +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 + +#endif // ANTKEEPER_CSG_HPP + diff --git a/src/antkeeper/geometry/intersection.cpp b/src/antkeeper/geometry/intersection.cpp new file mode 100644 index 0000000..84afa55 --- /dev/null +++ b/src/antkeeper/geometry/intersection.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2020 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 "intersection.hpp" +#include + +std::tuple ray_plane_intersection(const ray& ray, const plane& plane) +{ + float denom = vmq::dot(ray.direction, plane.normal); + if (denom != 0.0f) + { + float t = -(vmq::dot(ray.origin, plane.normal) + plane.distance) / denom; + + if (t >= 0.0f) + { + return std::make_tuple(true, t); + } + } + + return std::make_tuple(false, std::numeric_limits::infinity()); +} + +std::tuple ray_triangle_intersection(const ray& ray, const float3& a, const float3& b, const float3& c) +{ + // Find edges + float3 edge10 = b - a; + float3 edge20 = c - a; + + // Calculate determinant + float3 pv = vmq::cross(ray.direction, edge20); + float det = vmq::dot(edge10, pv); + + if (!det) + { + return std::make_tuple(false, std::numeric_limits::infinity(), 0.0f, 0.0f); + } + + float inverse_det = 1.0f / det; + + // Calculate u + float3 tv = ray.origin - a; + float u = vmq::dot(tv, pv) * inverse_det; + + if (u < 0.0f || u > 1.0f) + { + return std::make_tuple(false, std::numeric_limits::infinity(), 0.0f, 0.0f); + } + + // Calculate v + float3 qv = vmq::cross(tv, edge10); + float v = vmq::dot(ray.direction, qv) * inverse_det; + + if (v < 0.0f || u + v > 1.0f) + { + return std::make_tuple(false, std::numeric_limits::infinity(), 0.0f, 0.0f); + } + + // Calculate t + float t = vmq::dot(edge20, qv) * inverse_det; + + if (t > 0.0f) + { + return std::make_tuple(true, t, u, v); + } + + return std::make_tuple(false, std::numeric_limits::infinity(), 0.0f, 0.0f); +} + +std::tuple ray_aabb_intersection(const ray& ray, const aabb& aabb) +{ + float t0 = -std::numeric_limits::infinity(); + float t1 = std::numeric_limits::infinity(); + + for (std::size_t i = 0; i < 3; ++i) + { + if (ray.direction[i] == 0.0f) + { + if (ray.origin[i] < aabb.min_point[i] || ray.origin[i] > aabb.max_point[i]) + { + return std::make_tuple(false, std::numeric_limits::infinity(), std::numeric_limits::infinity()); + } + } + else + { + float tmin = (aabb.min_point[i] - ray.origin[i]) / ray.direction[i]; + float tmax = (aabb.max_point[i] - ray.origin[i]) / ray.direction[i]; + + t0 = std::max(t0, std::min(tmin, tmax)); + t1 = std::min(t1, std::max(tmin, tmax)); + } + } + + if (t0 > t1 || t1 < 0.0f) + { + return std::make_tuple(false, std::numeric_limits::infinity(), std::numeric_limits::infinity()); + } + + return std::make_tuple(true, t0, t1); +} + +std::tuple ray_mesh_intersection(const ray& ray, const mesh& mesh) +{ + const std::vector& triangles = mesh.get_faces(); + + bool intersection = false; + float t0 = std::numeric_limits::infinity(); + float t1 = -std::numeric_limits::infinity(); + std::size_t index0 = triangles.size(); + std::size_t index1 = triangles.size(); + + for (std::size_t i = 0; i < triangles.size(); ++i) + { + const mesh::face* triangle = triangles[i]; + + const float3& a = reinterpret_cast(triangle->edge->vertex->position); + const float3& b = reinterpret_cast(triangle->edge->next->vertex->position); + const float3& c = reinterpret_cast(triangle->edge->previous->vertex->position); + + auto result = ray_triangle_intersection(ray, a, b, c); + + if (std::get<0>(result)) + { + intersection = true; + float t = std::get<1>(result); + + if (t < t0) + { + t0 = t; + index0 = i; + } + + if (t > t1) + { + t1 = t; + index1 = i; + } + } + } + + return std::make_tuple(intersection, t0, t1, index0, index1); +} + +bool aabb_aabb_intersection(const aabb& a, const aabb& b) +{ + if (a.max_point.x < b.min_point.x || a.min_point.x > b.max_point.x) + return false; + if (a.max_point.y < b.min_point.y || a.min_point.y > b.max_point.y) + return false; + if (a.max_point.z < b.min_point.z || a.min_point.z > b.max_point.z) + return false; + return true; +} + +bool aabb_sphere_intersection(const aabb& aabb, const float3& center, float radius) +{ + float distance_squared = 0.0f; + for (int i = 0; i < 3; ++i) + { + float v = center[i]; + if (v < aabb.min_point[i]) + distance_squared += (aabb.min_point[i] - v) * (aabb.min_point[i] - v); + if (v > aabb.max_point[i]) + distance_squared += (v - aabb.max_point[i]) * (v - aabb.max_point[i]); + } + + return (distance_squared <= (radius * radius)); +} + diff --git a/src/antkeeper/geometry/intersection.hpp b/src/antkeeper/geometry/intersection.hpp new file mode 100644 index 0000000..0dfde97 --- /dev/null +++ b/src/antkeeper/geometry/intersection.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 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_INTERSECTION_HPP +#define ANTKEEPER_INTERSECTION_HPP + +#include +#include +#include "aabb.hpp" +#include "mesh.hpp" +#include "plane.hpp" +#include "ray.hpp" + +using namespace vmq::types; +using namespace vmq::operators; + + +/** + * Tests for intersection between a ray and a plane. + * + * @param ray Ray to test for intersection. + * @param plane Plane to test for intersection. + * @return Tuple containing a boolean indicating whether intersection occurred, and a float indicating the signed distance along the ray to the point of intersection. + */ +std::tuple ray_plane_intersection(const ray& ray, const plane& plane); + +std::tuple ray_triangle_intersection(const ray& ray, const float3& a, const float3& b, const float3& c); + +std::tuple ray_aabb_intersection(const ray& ray, const aabb& aabb); + +std::tuple ray_mesh_intersection(const ray& ray, const mesh& mesh); + +bool aabb_sphere_intersection(const aabb& aabb, const float3& center, float radius); + +bool aabb_aabb_intersection(const aabb& a, const aabb& b); + +#endif // ANTKEEPER_INTERSECTION_HPP + diff --git a/src/antkeeper/geometry/mesh-accelerator.cpp b/src/antkeeper/geometry/mesh-accelerator.cpp new file mode 100644 index 0000000..20fff48 --- /dev/null +++ b/src/antkeeper/geometry/mesh-accelerator.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2020 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 "mesh-accelerator.hpp" +#include "mesh-functions.hpp" +#include "morton.hpp" +#include +#include + +mesh_accelerator::mesh_accelerator() +{} + +void mesh_accelerator::build(const mesh& mesh) +{ + // Clear octree and face map + octree.clear(); + face_map.clear(); + + // Calculate mesh dimensions + aabb bounds = calculate_bounds(mesh); + float3 mesh_dimensions = bounds.max_point - bounds.min_point; + center_offset = mesh_dimensions * 0.5f - (bounds.min_point + bounds.max_point) * 0.5f; + + // Calculate node dimensions at each octree depth + for (auto i = 0; i <= octree32::max_depth; ++i) + { + node_dimensions[i] = mesh_dimensions * static_cast((1.0f / std::pow(2, i))); + } + + // Add faces to octree + for (mesh::face* face: mesh.get_faces()) + { + // Calculate face bounds + float3 min_point = reinterpret_cast(face->edge->vertex->position); + float3 max_point = min_point; + mesh::edge* edge = face->edge; + do + { + const auto& position = edge->vertex->position; + for (int i = 0; i < 3; ++i) + { + min_point[i] = std::min(min_point[i], position[i]); + max_point[i] = std::max(max_point[i], position[i]); + } + + edge = edge->next; + } + while (edge != face->edge); + + // 1. Find max depth node of aabb min + // 2. Find max depth node of aabb max + // 3. Find common ancestor of the two nodes--that's the containing node. + octree32::node_type min_node = find_node(min_point); + octree32::node_type max_node = find_node(max_point); + octree32::node_type containing_node = octree32::common_ancestor(min_node, max_node); + + // Insert containing node into octree + octree.insert(containing_node); + + // Add face to face map + face_map[containing_node].push_back(face); + } +} + +std::optional mesh_accelerator::query_nearest(const ray& ray) const +{ + ray_query_result result; + result.t = std::numeric_limits::infinity(); + result.face = nullptr; + + query_nearest_recursive(result.t, result.face, octree.root, ray); + + if (result.face) + return std::optional{result}; + return std::nullopt; +} + +void mesh_accelerator::query_nearest_recursive(float& nearest_t, ::mesh::face*& nearest_face, octree32::node_type node, const ray& ray) const +{ + // Get node bounds + const aabb node_bounds = get_node_bounds(node); + + // Test for intersection with node bounds + auto aabb_intersection = ray_aabb_intersection(ray, node_bounds); + + // If ray passed through this node + if (std::get<0>(aabb_intersection)) + { + // Test all triangles in the node + if (auto it = face_map.find(node); it != face_map.end()) + { + const std::list& faces = it->second; + + //std::cout << std::bitset<32>(node) << " has " << faces.size() << " faces\n"; + + for (mesh::face* face: faces) + { + // Get triangle coordinates + const float3& a = reinterpret_cast(face->edge->vertex->position); + const float3& b = reinterpret_cast(face->edge->next->vertex->position); + const float3& c = reinterpret_cast(face->edge->previous->vertex->position); + + // Test for intersection with triangle + auto triangle_intersection = ray_triangle_intersection(ray, a, b, c); + if (std::get<0>(triangle_intersection)) + { + float t = std::get<1>(triangle_intersection); + if (t < nearest_t) + { + nearest_t = t; + nearest_face = face; + } + } + } + } + + // Test all child nodes + if (!octree.is_leaf(node)) + { + for (int i = 0; i < 8; ++i) + query_nearest_recursive(nearest_t, nearest_face, octree.child(node, i), ray); + } + } +} + +aabb mesh_accelerator::get_node_bounds(octree32::node_type node) const +{ + // Decode Morton location of node + auto [x, y, z] = morton::decode_3d(octree32::location(node)); + float3 node_location = float3{static_cast(x), static_cast(y), static_cast(z)}; + + // Get node dimensions at node depth + const float3& dimensions = node_dimensions[octree32::depth(node)]; + + // Calculate AABB + float3 min_point = (node_location * dimensions) - center_offset; + return aabb{min_point, min_point + dimensions}; +} + +octree32::node_type mesh_accelerator::find_node(const float3& point) const +{ + // Transform point to octree space + float3 transformed_point = (point + center_offset); + + // Account for floating-point tolerance + const float epsilon = 0.00001f; + transformed_point.x = std::max(0.0f, std::min(node_dimensions[0].x - epsilon, transformed_point.x)); + transformed_point.y = std::max(0.0f, std::min(node_dimensions[0].y - epsilon, transformed_point.y)); + transformed_point.z = std::max(0.0f, std::min(node_dimensions[0].z - epsilon, transformed_point.z)); + + // Transform point to max-depth node space + transformed_point = transformed_point / node_dimensions[octree32::max_depth]; + + // Encode transformed point as a Morton location code + std::uint32_t location = morton::encode_3d( + static_cast(transformed_point.x), + static_cast(transformed_point.y), + static_cast(transformed_point.z)); + + /// Return max depth node at the determined location + return octree32::node(octree32::max_depth, location); +} + diff --git a/src/antkeeper/geometry/mesh-accelerator.hpp b/src/antkeeper/geometry/mesh-accelerator.hpp new file mode 100644 index 0000000..a142593 --- /dev/null +++ b/src/antkeeper/geometry/mesh-accelerator.hpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2020 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_MESH_ACCELERATOR_HPP +#define ANTKEEPER_MESH_ACCELERATOR_HPP + +#include "mesh.hpp" +#include "octree.hpp" +#include "geometry/aabb.hpp" +#include "intersection.hpp" +#include +#include +#include +#include +using namespace vmq::types; +using namespace vmq::operators; + +/** + * Acceleration structure for querying mesh geometry. + */ +class mesh_accelerator +{ +public: + struct ray_query_result + { + float t; + ::mesh::face* face; + }; + + mesh_accelerator(); + + /** + * Builds the acceleration structure. + */ + void build(const mesh& mesh); + + /** + * Finds the first intersection between a ray and a triangle in the mesh. + * + * @param ray Ray to test for intersection. + * @return Ray query result on intersection, and `std::nullopt` if no intersection occurreda. + */ + std::optional query_nearest(const ray& ray) const; + +private: + aabb get_node_bounds(octree32::node_type node) const; + + void query_nearest_recursive(float& nearest_t, ::mesh::face*& nearest_face, octree32::node_type node, const ray& ray) const; + + /// Returns the max-depth node in which the point is located + octree32::node_type find_node(const float3& point) const; + + octree32 octree; + float3 node_dimensions[octree32::max_depth + 1]; + float3 center_offset; + std::unordered_map> face_map; +}; + +#endif // ANTKEEPER_MESH_ACCELERATOR_HPP + diff --git a/src/antkeeper/geometry/mesh-functions.cpp b/src/antkeeper/geometry/mesh-functions.cpp new file mode 100644 index 0000000..46c4a17 --- /dev/null +++ b/src/antkeeper/geometry/mesh-functions.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2020 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 "mesh-functions.hpp" +#include +#include +#include +#include +#include + +using namespace vmq::operators; + +struct edge_hasher +{ + std::size_t operator()(const std::array& v) const noexcept + { + std::size_t hash = std::hash()(v[0]); + return hash ^ (std::hash()(v[1]) + 0x9e3779b9 + (hash << 6) + (hash >> 2)); + } +}; + +void create_triangle_mesh(mesh& mesh, const std::vector& vertices, const std::vector>& triangles) +{ + for (const auto& vertex: vertices) + mesh.add_vertex(vertex); + + std::unordered_map, ::mesh::edge*, edge_hasher> edge_map; + const std::vector& mesh_vertices = mesh.get_vertices(); + std::vector<::mesh::edge*> loop(3); + + for (const auto& triangle: triangles) + { + ::mesh::vertex* triangle_vertices[3] = + { + mesh_vertices[triangle[0]], + mesh_vertices[triangle[1]], + mesh_vertices[triangle[2]] + }; + + for (int j = 0; j < 3; ++j) + { + ::mesh::vertex* start = triangle_vertices[j]; + ::mesh::vertex* end = triangle_vertices[(j + 1) % 3]; + + if (auto it = edge_map.find({start->index, end->index}); it != edge_map.end()) + { + /* + if (it->second->face) + std::cout << "THIS EDGE ALREADY HAS A FACE!\n" << std::endl; + */ + loop[j] = it->second; + } + else + { + loop[j] = mesh.add_edge(start, end); + edge_map[{start->index, end->index}] = loop[j]; + edge_map[{end->index, start->index}] = loop[j]->symmetric; + } + + } + + mesh.add_face(loop); + } +} + +void calculate_face_normals(float* normals, const mesh& mesh) +{ + const std::vector& faces = mesh.get_faces(); + + for (std::size_t i = 0; i < faces.size(); ++i) + { + const mesh::face& face = *(faces[i]); + float3& normal = reinterpret_cast(normals[i * 3]); + + const float3& a = reinterpret_cast(face.edge->vertex->position); + const float3& b = reinterpret_cast(face.edge->next->vertex->position); + const float3& c = reinterpret_cast(face.edge->previous->vertex->position); + + normal = vmq::normalize(vmq::cross(b - a, c - a)); + } +} + +aabb calculate_bounds(const mesh& mesh) +{ + float3 bounds_min; + float3 bounds_max; + for (int i = 0; i < 3; ++i) + { + bounds_min[i] = std::numeric_limits::infinity(); + bounds_max[i] = -std::numeric_limits::infinity(); + } + + for (const mesh::vertex* vertex: mesh.get_vertices()) + { + const auto& position = vertex->position; + for (int i = 0; i < 3; ++i) + { + bounds_min[i] = std::min(bounds_min[i], position[i]); + bounds_max[i] = std::max(bounds_max[i], position[i]); + } + } + + return aabb{bounds_min, bounds_max}; +} + + diff --git a/src/antkeeper/geometry/mesh-functions.hpp b/src/antkeeper/geometry/mesh-functions.hpp new file mode 100644 index 0000000..b611696 --- /dev/null +++ b/src/antkeeper/geometry/mesh-functions.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 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_MESH_FUNCTIONS_HPP +#define ANTKEEPER_MESH_FUNCTIONS_HPP + +#include "mesh.hpp" +#include +#include +#include +#include "geometry/aabb.hpp" + +using namespace vmq::types; + +/** + * Creates a triangle mesh from a list of vertices and indices. + * + * @param[out] mesh Mesh to which vertices, edges, and faces will be added. + * @param vertices Vertex positions + * @param triangles Triangle indices + * @return Newly created triangle mesh. + */ +void create_triangle_mesh(mesh& mesh, const std::vector& vertices, const std::vector>& triangles); + +/** + * Calculates normals for each face. + * + * @param[out] Array in which normals will be stored. Must be able to hold `3 * face count` floats. + * @param layer Face layer in which the normals will be stored. Must have three components. + */ +void calculate_face_normals(float* normals, const mesh& mesh); + +void calculate_vertex_normals(float* normals, const mesh& mesh); + +/** + * Calculates the AABB bounds of a mesh. + */ +aabb calculate_bounds(const mesh& mesh); + + +#endif // ANTKEEPER_MESH_FUNCTIONS_HPP + diff --git a/src/antkeeper/geometry/mesh.cpp b/src/antkeeper/geometry/mesh.cpp new file mode 100644 index 0000000..b0e023d --- /dev/null +++ b/src/antkeeper/geometry/mesh.cpp @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2020 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 "mesh.hpp" +#include + +mesh::~mesh() +{ + // Deallocate vertices + for (mesh::vertex* vertex: vertices) + { + delete vertex; + } + + // Deallocate edges + for (mesh::edge* edge: edges) + { + delete edge->symmetric; + delete edge; + } + + // Deallocate faces + for (mesh::face* face: faces) + { + delete face; + } +} + +mesh::vertex* mesh::add_vertex(const std::array& position) +{ + mesh::vertex* vertex = new mesh::vertex(); + vertex->edge = nullptr; + vertex->position = position; + vertex->index = vertices.size(); + vertices.push_back(vertex); + + return vertex; +} + +mesh::edge* mesh::add_edge(mesh::vertex* a, mesh::vertex* b) +{ + mesh::edge* ab = new mesh::edge(); + mesh::edge* ba = new mesh::edge(); + + ab->vertex = a; + ab->face = nullptr; + ab->previous = ba; + ab->next = ba; + ab->symmetric = ba; + ab->index = edges.size(); + + ba->vertex = b; + ba->face = nullptr; + ba->previous = ab; + ba->next = ab; + ba->symmetric = ab; + ba->index = edges.size(); + + if (!a->edge) + { + a->edge = ab; + } + else + { + mesh::edge* a_in = find_free_incident(a); + mesh::edge* a_out = a_in->next; + a_in->next = ab; + ab->previous = a_in; + ba->next = a_out; + a_out->previous = ba; + } + + if (!b->edge) + { + b->edge = ba; + } + else + { + mesh::edge* b_in = find_free_incident(b); + mesh::edge* b_out = b_in->next; + b_in->next = ba; + ba->previous = b_in; + ab->next = b_out; + b_out->previous = ab; + } + + // Add edge + edges.push_back(ab); + + return ab; +} + +mesh::face* mesh::add_face(const loop& loop) +{ + if (loop.empty()) + { + throw std::runtime_error("Empty edge loop"); + } + + // Validate edge loop + for (std::size_t i = 0; i < loop.size(); ++i) + { + mesh::edge* current = loop[i]; + mesh::edge* next = loop[(i + 1) % loop.size()]; + + if (current->symmetric->vertex != next->vertex) + { + // Disconnected edge loop + throw std::runtime_error("Disconnected edge loop"); + } + + if (current->face) + { + // This edge already has a face + throw std::runtime_error("Non-manifold mesh 1"); + } + } + + // Make edges adjacent + for (std::size_t i = 0; i < loop.size(); ++i) + { + if (!make_adjacent(loop[i], loop[(i + 1) % loop.size()])) + { + throw std::runtime_error("Non-manifold mesh 2"); + } + } + + // Create face + mesh::face* face = new mesh::face(); + face->edge = loop[0]; + face->index = faces.size(); + + // Add face + faces.push_back(face); + + // Connect edges to the face + for (mesh::edge* edge: loop) + { + edge->face = face; + } + + return face; +} + +void mesh::remove_face(mesh::face* face) +{ + // Nullify pointers to this face + mesh::edge* edge = face->edge; + do + { + edge->face = nullptr; + edge = edge->next; + } + while (edge != face->edge); + + // Adjust indices of faces after this face + for (std::size_t i = face->index + 1; i < faces.size(); ++i) + { + --faces[i]->index; + } + + // Remove face from the faces vector + faces.erase(faces.begin() + face->index); + + // Deallocate face + delete face; +} + +void mesh::remove_edge(mesh::edge* edge) +{ + mesh::edge* ab = edge; + mesh::edge* ba = edge->symmetric; + mesh::vertex* a = ab->vertex; + mesh::edge* a_in = ab->previous; + mesh::edge* a_out = ba->next; + mesh::vertex* b = ba->vertex; + mesh::edge* b_in = ba->previous; + mesh::edge* b_out = ab->next; + + // Remove dependent faces + if (ab->face) + remove_face(ab->face); + if (ba->face) + remove_face(ba->face); + + // Re-link edges + if (a->edge == ab) + a->edge = (a_out == ab) ? nullptr : a_out; + if (b->edge == ba) + b->edge = (b_out == ba) ? nullptr : b_out; + a_in->next = a_out; + a_out->previous = a_in; + b_in->next = b_out; + b_out->previous = b_in; + + // Adjust indices of edges after this edge + for (std::size_t i = edge->index + 1; i < edges.size(); ++i) + { + --edges[i]->index; + --edges[i]->symmetric->index; + } + + // Remove edge from the edges vector + edges.erase(edges.begin() + edge->index); + + // Deallocate edge + delete edge->symmetric; + delete edge; +} + +void mesh::remove_vertex(mesh::vertex* vertex) +{ + // Remove connected edges + if (vertex->edge) + { + mesh::edge* current = nullptr; + mesh::edge* next = vertex->edge; + + do + { + current = next; + + next = next->symmetric->next; + if (next == current) + { + next = next->symmetric->next; + } + + remove_edge(current); + } + while (current != next); + } + + // Adjust indices of vertices after this vertex + for (std::size_t i = vertex->index + 1; i < vertices.size(); ++i) + { + --vertices[i]->index; + } + + // Remove vertex from the vertices vector + vertices.erase(vertices.begin() + vertex->index); + + // Deallocate vertex + delete vertex; +} + +mesh::edge* mesh::find_free_incident(mesh::vertex* vertex) const +{ + mesh::edge* begin = vertex->edge->symmetric; + mesh::edge* current = begin; + + do + { + if (!current->face) + { + return current; + } + + current = current->next->symmetric; + } + while (current != begin); + + return nullptr; +} + +mesh::edge* mesh::find_free_incident(mesh::edge* start_edge, mesh::edge* end_edge) const +{ + if (start_edge == end_edge) + { + return nullptr; + } + + mesh::edge* current = start_edge; + do + { + if (!current->face) + { + return current; + } + + current = current->next->symmetric; + } + while (current != end_edge); + + return nullptr; +} + +bool mesh::make_adjacent(mesh::edge* in, mesh::edge* out) +{ + if (in->next == out) + { + return true; + } + + mesh::edge* b = in->next; + mesh::edge* d = out->previous; + mesh::edge* g = find_free_incident(out->symmetric, in); + if (!g) + { + return false; + } + + mesh::edge* h = g->next; + + in->next = out; + out->previous = in; + + g->next = b; + b->previous = g; + + d->next = h; + h->previous = d; + + return true; +} + diff --git a/src/antkeeper/geometry/mesh.hpp b/src/antkeeper/geometry/mesh.hpp new file mode 100644 index 0000000..5624fda --- /dev/null +++ b/src/antkeeper/geometry/mesh.hpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2020 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_MESH_HPP +#define ANTKEEPER_MESH_HPP + +#include +#include + +/** + * Half-edge mesh. + * + * @see http://kaba.hilvi.org/homepage/blog/halfedge/halfedge.htm + */ +class mesh +{ +public: + struct vertex; + struct edge; + struct face; + typedef std::vector loop; + + /** + * Creates a mesh. + */ + mesh() = default; + + /** + * Destroys a mesh. + */ + ~mesh(); + + /** + * Adds a vertex to the mesh. This vertex initially has a null edge. + * + * @param position Position of the vertex. + * @return Pointer to the added vertex. + */ + mesh::vertex* add_vertex(const std::array& position); + + /** + * Adds an edge to the mesh. + * + * @param a The vertex from which the edge originates. + * @param b The vertex at which the edge ends. + * @return Pointer to the added edge. + */ + mesh::edge* add_edge(mesh::vertex* a, mesh::vertex* b); + + /** + * Adds a face to the mesh. + * + * @param loop List of edges which form the face. + * @return Pointer to the added face. + */ + mesh::face* add_face(const loop& loop); + + /** + * Removes a face from the mesh. + * + * @param face Face to be removed. This face will be deallocated after removal. + */ + void remove_face(mesh::face* face); + + /** + * Removes an edge and all dependent faces from the mesh. + * + * @param edge Edge to be removed. This edge will be deallocated after removal. + */ + void remove_edge(mesh::edge* edge); + + /** + * Removes a vertex, all dependent edges, and all dependent faces from the mesh. + * + * @param vertex Vertex to be removed. This vertex will be deallocated after removal. + */ + void remove_vertex(mesh::vertex* vertex); + + /// Returns the mesh vertices + const std::vector& get_vertices() const; + + /// Returns the mesh edges + const std::vector& get_edges() const; + + /// Returns the mesh faces + const std::vector& get_faces() const; + + /** + * Half-edge vertex which contains a pointer to its parent edge, a position vector, and an index. + */ + struct vertex + { + /// Pointer to the edge to which this vertex belongs + mesh::edge* edge; + + /// Vertex position + std::array position; + + /// Index of this vertex + std::size_t index; + }; + + /** + * Half-edge edge which contains pointers to its starting vertex, parent face, and related edges. + */ + struct edge + { + /// Pointer to the vertex at which the edge starts + mesh::vertex* vertex; + + /// Pointer to the face on the left of this edge + mesh::face* face; + + /// Pointer to the previous edge in the parent face + mesh::edge* previous; + + /// Pointer to the next edge in the parent face + mesh::edge* next; + + /// Pointer to the symmetric edge + mesh::edge* symmetric; + + /// Index of this edge + std::size_t index; + }; + + /** + * Half-edge face which contains a pointer to its first edge and its normal vector. + */ + struct face + { + /// Pointer to the first edge in this face + mesh::edge* edge; + + /// Index of this face + std::size_t index; + }; + +private: + mesh::edge* find_free_incident(mesh::vertex* vertex) const; + mesh::edge* find_free_incident(mesh::edge* start_edge, mesh::edge* end_edge) const; + bool make_adjacent(mesh::edge* in_edge, mesh::edge* out_edge); + + std::vector vertices; + std::vector edges; + std::vector faces; +}; + +inline const std::vector& mesh::get_vertices() const +{ + return vertices; +} + +inline const std::vector& mesh::get_edges() const +{ + return edges; +} + +inline const std::vector& mesh::get_faces() const +{ + return faces; +} + +#endif // ANTKEEPER_MESH_HPP + diff --git a/src/antkeeper/geometry/plane.hpp b/src/antkeeper/geometry/plane.hpp new file mode 100644 index 0000000..b36bebb --- /dev/null +++ b/src/antkeeper/geometry/plane.hpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 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_PLANE_HPP +#define ANTKEEPER_PLANE_HPP + +#include + +using vmq::vector; +using namespace vmq::operators; + +template +struct plane +{ + vector normal; + T distance; + + /** + * Creates a plane given a normal vector and distance. + */ + plane(const vector& normal, T distance); + + /** + * Creates a plane given a normal vector and offset vector. + */ + plane(const vector& normal, const vector& offset); + + /** + * Creates a plane given three points. + */ + plane(const vector& a, const vector& b, const vector& c); + + /** + * Creates a plane given its coefficients. + * + * @param coefficients Vector containing the plane coefficients, A, B, C and D, as x, y, z, and w, respectively. + */ + plane(const vector& coefficients); + + /// Creates an uninitialized plane. + plane() = default; +}; + +template +inline plane::plane(const vector& normal, T distance): + normal(normal), + distance(distance) +{} + +template +plane::plane(const vector& normal, const vector& offset): + normal(normal), + distance(-vmq::dot(normal, offset)) +{} + +template +plane::plane(const vector& a, const vector& b, const vector& c) +{ + normal = vmq::normalize(vmq::cross(c - b, a - b)); + distance = -(vmq::dot(normal, b)); +} + +template +plane::plane(const vector& coefficients) +{ + const vector abc = vmq::resize<3>(coefficients); + const float inverse_length = T(1) / vmq::length(abc); + + normal = abc * inverse_length; + distance = coefficients[3] * inverse_length; +} + +/** + * Calculates the signed distance between a plane and a vector. + * + * @param p Plane. + * @param v Vector. + * @return Signed distance between the plane and a vector. + */ +template +inline T signed_distance(const plane& p, const vector& v) +{ + return p.distance + vmq::dot(p.normal, v); +} + +/** + * Calculates the point of intersection between three planes. + */ +template +vector intersection(const plane& p0, const plane& p1, const plane& p2) +{ + return -(p0.distance * vmq::cross(p1.normal, p2.normal) + p1.distance * vmq::cross(p2.normal, p0.normal) + p2.distance * vmq::cross(p0.normal, p1.normal)) / vmq::dot(p0.normal, vmq::cross(p1.normal, p2.normal)); +} + +#endif // ANTKEEPER_PLANE_HPP + diff --git a/src/antkeeper/geometry/ray.hpp b/src/antkeeper/geometry/ray.hpp new file mode 100644 index 0000000..a81c958 --- /dev/null +++ b/src/antkeeper/geometry/ray.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 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_RAY_HPP +#define ANTKEEPER_RAY_HPP + +#include + +using vmq::vector; +using namespace vmq::operators; + +template +struct ray +{ + vector origin; + vector direction; + + vector extrapolate(T distance) const; +}; + +template +inline vector ray::extrapolate(T distance) const +{ + return origin + direction * distance; +} + +#endif // ANTKEEPER_RAY_HPP + diff --git a/src/antkeeper/geometry/sphere.hpp b/src/antkeeper/geometry/sphere.hpp new file mode 100644 index 0000000..2767374 --- /dev/null +++ b/src/antkeeper/geometry/sphere.hpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 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_SPHERE_HPP +#define ANTKEEPER_SPHERE_HPP + +#include "bounding-volume.hpp" +#include "aabb.hpp" +#include +#include + +using vmq::vector; +using namespace vmq::operators; + +template +struct sphere: public bounding_volume +{ + vector center; + T radius; + + sphere(const vector& center, T radius); + sphere(); + + virtual bounding_volume_type get_bounding_volume_type() const; + virtual bool intersects(const sphere& sphere) const; + virtual bool intersects(const aabb& aabb) const; + virtual bool contains(const sphere& sphere) const; + virtual bool contains(const aabb& aabb) const; + virtual bool contains(const vector& point) const; +}; + +template +sphere::sphere(const vector& center, T radius): + center(center), + radius(radius) +{} + +template +sphere::sphere() +{} + +template +inline bounding_volume_type sphere::get_bounding_volume_type() const +{ + return bounding_volume_type::sphere; +} + +template +bool sphere::intersects(const sphere& sphere) const +{ + vector d = center - sphere.center; + float r = radius + sphere.radius; + return (vmq::dot(d, d) <= r * r); +} + +template +bool sphere::intersects(const aabb& aabb) const +{ + return aabb.intersects(*this); +} + +template +bool sphere::contains(const sphere& sphere) const +{ + float containment_radius = radius - sphere.radius; + if (containment_radius < T(0)) + return false; + + vector d = center - sphere.center; + return (vmq::dot(d, d) <= containment_radius * containment_radius); +} + +template +bool sphere::contains(const aabb& aabb) const +{ + T distance = T(0); + + vector a = center - aabb.min_point; + vector b = center - aabb.max_point; + + distance += std::max(a.x * a.x, b.x * b.x); + distance += std::max(a.y * a.y, b.y * b.y); + distance += std::max(a.z * a.z, b.z * b.z); + + return (distance <= radius * radius); +} + +template +bool sphere::contains(const vector& point) const +{ + vector d = center - point; + return (vmq::dot(d, d) <= radius * radius); +} + +#endif // ANTKEEPER_SPHERE_HPP + diff --git a/src/antkeeper/geometry/view-frustum.hpp b/src/antkeeper/geometry/view-frustum.hpp new file mode 100644 index 0000000..2ebae6b --- /dev/null +++ b/src/antkeeper/geometry/view-frustum.hpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2020 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_VIEW_FRUSTUM_HPP +#define ANTKEEPER_VIEW_FRUSTUM_HPP + +#include "convex-hull.hpp" +#include +#include + +using vmq::vector; +using vmq::matrix; +using namespace vmq::operators; + +template +class view_frustum +{ +public: + /** + * Creates a view frustum from a view-projection matrix. + * + * @param view_projection View-projection matrix. + */ + view_frustum(const matrix& view_projection); + + /// Creates an uninitialized view frustum. + view_frustum(); + + /** + * Recalculates the view frustum from a view-projection matrix. + * + * @param view_projection View-projection matrix. + */ + void set_matrix(const matrix& view_projection); + + /// Returns a convex hull which describes the bounds of the view frustum. + const convex_hull& get_bounds() const; + + /// Returns the left clipping plane. + const plane& get_left() const; + + /// Returns the right clipping plane. + const plane& get_right() const; + + /// Returns the bottom clipping plane. + const plane& get_bottom() const; + + /// Returns the top clipping plane. + const plane& get_top() const; + + /// Returns the near clipping plane. + const plane& get_near() const; + + /// Returns the far clipping plane. + const plane& get_far() const; + + /** + * Returns an array containing the corners of the view frustum bounds. + * + * @return Array containing the corners of the view frustum bounds. Corners are stored in the following order: NTL, NTR, NBL, NBR, FTL, FTR, FBL, FBR; where N is near, F is far, T is top, B is bottom, L is left, and R is right, therefore NTL refers to the corner shared between the near, top, and left clipping planes. + */ + const std::array, 8>& get_corners() const; + +private: + void recalculate_planes(const matrix& view_projection); + void recalculate_corners(); + + convex_hull bounds; + std::array, 8> corners; +}; + +template +view_frustum::view_frustum(const matrix& view_projection): + bounds(6) +{ + set_matrix(view_projection); +} + +template +view_frustum::view_frustum(): + view_frustum(vmq::identity4x4) +{} + +template +void view_frustum::set_matrix(const matrix& view_projection) +{ + recalculate_planes(view_projection); + recalculate_corners(); +} + +template +inline const convex_hull& view_frustum::get_bounds() const +{ + return bounds; +} + +template +inline const plane& view_frustum::get_left() const +{ + return bounds.planes[0]; +} + +template +inline const plane& view_frustum::get_right() const +{ + return bounds.planes[1]; +} + +template +inline const plane& view_frustum::get_bottom() const +{ + return bounds.planes[2]; +} + +template +inline const plane& view_frustum::get_top() const +{ + return bounds.planes[3]; +} + +template +inline const plane& view_frustum::get_near() const +{ + return bounds.planes[4]; +} + +template +inline const plane& view_frustum::get_far() const +{ + return bounds.planes[5]; +} + +template +inline const std::array, 8>& view_frustum::get_corners() const +{ + return corners; +} + +template +void view_frustum::recalculate_planes(const matrix& view_projection) +{ + matrix transpose = vmq::transpose(view_projection); + bounds.planes[0] = plane(transpose[3] + transpose[0]); + bounds.planes[1] = plane(transpose[3] - transpose[0]); + bounds.planes[2] = plane(transpose[3] + transpose[1]); + bounds.planes[3] = plane(transpose[3] - transpose[1]); + bounds.planes[4] = plane(transpose[3] + transpose[2]); + bounds.planes[5] = plane(transpose[3] - transpose[2]); +} + +template +void view_frustum::recalculate_corners() +{ + corners[0] = intersection(get_near(), get_top(), get_left()); + corners[1] = intersection(get_near(), get_top(), get_right()); + corners[2] = intersection(get_near(), get_bottom(), get_left()); + corners[3] = intersection(get_near(), get_bottom(), get_right()); + corners[4] = intersection(get_far(), get_top(), get_left()); + corners[5] = intersection(get_far(), get_top(), get_right()); + corners[6] = intersection(get_far(), get_bottom(), get_left()); + corners[7] = intersection(get_far(), get_bottom(), get_right()); +} + +#endif // ANTKEEPER_VIEW_FRUSTUM_HPP + diff --git a/src/antkeeper/input/control-set.cpp b/src/antkeeper/input/control-set.cpp new file mode 100644 index 0000000..fcb36a7 --- /dev/null +++ b/src/antkeeper/input/control-set.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 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 "control-set.hpp" +#include "control.hpp" + +void control_set::add_control(control* control) +{ + controls.push_back(control); +} + +void control_set::remove_control(control* control) +{ + controls.remove(control); +} + +void control_set::remove_controls() +{ + controls.clear(); +} + +void control_set::update() +{ + for (control* control: controls) + { + control->update(); + } +} + +void control_set::set_callbacks_enabled(bool enabled) +{ + for (control* control: controls) + { + control->set_callbacks_enabled(enabled); + } +} + diff --git a/src/antkeeper/input/control-set.hpp b/src/antkeeper/input/control-set.hpp new file mode 100644 index 0000000..f8625d0 --- /dev/null +++ b/src/antkeeper/input/control-set.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 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_CONTROL_SET_HPP +#define ANTKEEPER_CONTROL_SET_HPP + +#include + +class control; + +/** + * A set of controls which can be managed simultaneously. + * + * @ingroup input + */ +class control_set +{ +public: + /** + * Adds a control to the control set. + * + * @param control Pointer to the control to add. + */ + void add_control(control* control); + + /** + * Removes a control from the control set. + * + * @param control Pointer to the control to remove. + */ + void remove_control(control* control); + + /** + * Removes all controls from the control set. + */ + void remove_controls(); + + /** + * Calls control::update() on each control in this control set. + */ + void update(); + + /** + * Enables or disables callbacks for all controls in the control set. + * + * @param enabled Whether to enable or disable callbacks for all controls in the control set. + */ + void set_callbacks_enabled(bool enabled); + + /** + * Returns the list of controls in the control set. + */ + const std::list* get_controls() const; + +private: + std::list controls; +}; + +inline const std::list* control_set::get_controls() const +{ + return &controls; +} + +#endif // ANTKEEPER_CONTROL_SET_HPP + diff --git a/src/antkeeper/input/control.cpp b/src/antkeeper/input/control.cpp new file mode 100644 index 0000000..8d82967 --- /dev/null +++ b/src/antkeeper/input/control.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 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 "control.hpp" + +control::control(): + deadzone(0.0f), + current_value(0.0f), + previous_value(0.0f), + reset(false), + activated_callback(nullptr), + deactivated_callback(nullptr), + value_changed_callback(nullptr), + callbacks_enabled(true) +{} + +control::~control() +{} + +void control::update() +{ + // Perform callbacks, if enabled + if (callbacks_enabled) + { + if (activated_callback) + { + if (is_active() && !was_active()) + { + activated_callback(); + } + } + + if (deactivated_callback) + { + if (!is_active() && was_active()) + { + deactivated_callback(); + } + } + + if (value_changed_callback) + { + if (current_value != previous_value) + { + if (is_active() || was_active()) + { + value_changed_callback(current_value); + } + } + } + } + + // Update previous value + previous_value = current_value; + + // Reset temporary values + if (reset) + { + current_value = 0.0f; + reset = false; + } +} + +void control::set_current_value(float value) +{ + current_value = value; + reset = false; +} + +void control::set_temporary_value(float value) +{ + current_value = value; + reset = true; +} + +void control::set_deadzone(float value) +{ + deadzone = value; +} + +void control::set_activated_callback(std::function callback) +{ + this->activated_callback = callback; +} + +void control::set_deactivated_callback(std::function callback) +{ + this->deactivated_callback = callback; +} + +void control::set_value_changed_callback(std::function callback) +{ + this->value_changed_callback = callback; +} + diff --git a/src/antkeeper/input/control.hpp b/src/antkeeper/input/control.hpp new file mode 100644 index 0000000..2a10f3d --- /dev/null +++ b/src/antkeeper/input/control.hpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 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_CONTROL_HPP +#define ANTKEEPER_CONTROL_HPP + +#include + +/** + * A control can be bound to multiple types of input events. + * + * @ingroup input + */ +class control +{ +public: + /// Creates a control. + control(); + + /// Destroys a control. + virtual ~control(); + + /** + * Performs callbacks then sets the previous value equal to the current value. + */ + void update(); + + /** + * Sets the current value of the control. + * + * @param value control value. + */ + void set_current_value(float value); + + /** + * This works the same as setting the current value, but causes the value to be reset on the next call to update. + */ + void set_temporary_value(float value); + + /** + * Sets the deadzone value. If the current value of the control is not greater than the deadzone value, the control will not be considered active. + * + * @param value Deadzone value. + */ + void set_deadzone(float value); + + /// Sets the callback for when the control is activated. + void set_activated_callback(std::function callback); + + /// Sets the callback for when the control is deactivated. + void set_deactivated_callback(std::function callback); + + /// Sets the callback for when the control value is changed. + void set_value_changed_callback(std::function callback); + + /** + * Enables or disables callbacks. + * + * @param enabled Whether to enable or disable callbacks. + */ + void set_callbacks_enabled(bool enabled); + + /// Returns the deadzone value. The default value is 0.0. + float get_deadzone() const; + + /// Returns the current value of the control. + float get_current_value() const; + + /// Returns the previous value of the control. + float get_previous_value() const; + + /// Returns true if the control is currently active. + bool is_active() const; + + /// Returns true if the control was previously active when update() was last called. + bool was_active() const; + +private: + float deadzone; + float current_value; + float previous_value; + bool reset; + std::function activated_callback; + std::function deactivated_callback; + std::function value_changed_callback; + bool callbacks_enabled; +}; + +inline void control::set_callbacks_enabled(bool enabled) +{ + this->callbacks_enabled = enabled; +} + +inline float control::get_deadzone() const +{ + return deadzone; +} + +inline float control::get_current_value() const +{ + return current_value; +} + +inline float control::get_previous_value() const +{ + return previous_value; +} + +inline bool control::is_active() const +{ + return current_value > deadzone; +} + +inline bool control::was_active() const +{ + return previous_value > deadzone; +} + +#endif // ANTKEEPER_CONTROL_HPP + diff --git a/src/antkeeper/input/game-controller.cpp b/src/antkeeper/input/game-controller.cpp new file mode 100644 index 0000000..8e59c05 --- /dev/null +++ b/src/antkeeper/input/game-controller.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 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-controller.hpp" +#include "input-events.hpp" +#include "event/event-dispatcher.hpp" +#include + +game_controller::game_controller() +{} + +void game_controller::press(game_controller_button button) +{ + if (!input_device::event_dispatcher) + { + return; + } + + game_controller_button_pressed_event event; + event.game_controller = this; + event.button = button; + + input_device::event_dispatcher->queue(event); +} + +void game_controller::release(game_controller_button button) +{ + if (!input_device::event_dispatcher) + { + return; + } + + game_controller_button_released_event event; + event.game_controller = this; + event.button = button; + + input_device::event_dispatcher->queue(event); +} + +void game_controller::move(game_controller_axis axis, float value) +{ + if (!input_device::event_dispatcher) + { + return; + } + + game_controller_axis_moved_event event; + event.game_controller = this; + event.axis = axis; + event.value = value; + + input_device::event_dispatcher->queue(event); +} + diff --git a/src/antkeeper/input/game-controller.hpp b/src/antkeeper/input/game-controller.hpp new file mode 100644 index 0000000..32668f4 --- /dev/null +++ b/src/antkeeper/input/game-controller.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020 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_CONTROLLER_HPP +#define ANTKEEPER_GAME_CONTROLLER_HPP + +#include "input-device.hpp" + +enum class game_controller_button +{ + a, + b, + x, + y, + back, + guide, + start, + left_stick, + right_stick, + left_shoulder, + right_shoulder, + dpad_up, + dpad_down, + dpad_left, + dpad_right +}; + +enum class game_controller_axis +{ + left_x, + left_y, + right_x, + right_y, + trigger_left, + trigger_right, +}; + +/** + * A virtual game controller which can generate game controller-related input events and pass them to an event dispatcher. + * + * @ingroup input + */ +class game_controller: public input_device +{ +public: + /** + * Creates a game controller input device. + */ + game_controller(); + + /// Destroys a game controller input device. + virtual ~game_controller() = default; + + /** + * Simulates a game controller button press. + * + * @param button Index of the pressed button. + */ + void press(game_controller_button button); + + /** + * Simulates a game controller button release. + * + * @param button Index of the released button. + */ + void release(game_controller_button button); + + /** + * Simulates a game controller axis movement. + * + * @param axis Index of the moved axis. + * @param negative Whether the movement was negative or positive. + * @param value Normalized degree of movement. + */ + void move(game_controller_axis axis, float value); +}; + +#endif // ANTKEEPER_GAME_CONTROLLER_HPP + diff --git a/src/antkeeper/input/input-device.cpp b/src/antkeeper/input/input-device.cpp new file mode 100644 index 0000000..209f89b --- /dev/null +++ b/src/antkeeper/input/input-device.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 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 "input-device.hpp" +#include "event/event-dispatcher.hpp" + +input_device::input_device(): + event_dispatcher(nullptr) +{} + +void input_device::set_event_dispatcher(::event_dispatcher* event_dispatcher) +{ + this->event_dispatcher = event_dispatcher; +} diff --git a/src/antkeeper/input/input-device.hpp b/src/antkeeper/input/input-device.hpp new file mode 100644 index 0000000..3184ef7 --- /dev/null +++ b/src/antkeeper/input/input-device.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 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_INPUT_DEVICE_HPP +#define ANTKEEPER_INPUT_DEVICE_HPP + +#include "input-device.hpp" + +class event_dispatcher; + +/** + * Base class for virtual devices which generate input events. + */ +class input_device +{ +public: + input_device(); + virtual ~input_device() = default; + + void set_event_dispatcher(event_dispatcher* event_dispatcher); + const event_dispatcher* get_event_dispatcher() const; + event_dispatcher* get_event_dispatcher(); + +protected: + event_dispatcher* event_dispatcher; +}; + +inline const event_dispatcher* input_device::get_event_dispatcher() const +{ + return event_dispatcher; +} + +inline event_dispatcher* input_device::get_event_dispatcher() +{ + return event_dispatcher; +} + +#endif // ANTKEEPER_INPUT_DEVICE_HPP + diff --git a/src/antkeeper/input/input-event-router.cpp b/src/antkeeper/input/input-event-router.cpp new file mode 100644 index 0000000..f531cba --- /dev/null +++ b/src/antkeeper/input/input-event-router.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2020 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 "input-event-router.hpp" +#include "control.hpp" +#include "input-mapping.hpp" +#include "mouse.hpp" +#include "event/event-dispatcher.hpp" + +input_event_router::input_event_router(): + event_dispatcher(nullptr) +{} + +input_event_router::~input_event_router() +{ + remove_mappings(); + set_event_dispatcher(nullptr); +} + +void input_event_router::add_mapping(const input_mapping& mapping) +{ + control* control = mapping.control; + + switch (mapping.get_type()) + { + case input_mapping_type::key: + { + ::key_mapping* key_mapping = new ::key_mapping(static_cast(mapping)); + key_mappings.push_back(key_mapping); + controls[control].push_back(key_mapping); + + break; + } + + case input_mapping_type::mouse_motion: + { + ::mouse_motion_mapping* mouse_motion_mapping = new ::mouse_motion_mapping(static_cast(mapping)); + mouse_motion_mappings.push_back(mouse_motion_mapping); + controls[control].push_back(mouse_motion_mapping); + + break; + } + + case input_mapping_type::mouse_wheel: + { + ::mouse_wheel_mapping* mouse_wheel_mapping = new ::mouse_wheel_mapping(static_cast(mapping)); + mouse_wheel_mappings.push_back(mouse_wheel_mapping); + controls[control].push_back(mouse_wheel_mapping); + + break; + } + + case input_mapping_type::mouse_button: + { + ::mouse_button_mapping* mouse_button_mapping = new ::mouse_button_mapping(static_cast(mapping)); + mouse_button_mappings.push_back(mouse_button_mapping); + controls[control].push_back(mouse_button_mapping); + + break; + } + + case input_mapping_type::game_controller_axis: + { + ::game_controller_axis_mapping* game_controller_axis_mapping = new ::game_controller_axis_mapping(static_cast(mapping)); + game_controller_axis_mappings.push_back(game_controller_axis_mapping); + controls[control].push_back(game_controller_axis_mapping); + + break; + } + + case input_mapping_type::game_controller_button: + { + ::game_controller_button_mapping* game_controller_button_mapping = new ::game_controller_button_mapping(static_cast(mapping)); + game_controller_button_mappings.push_back(game_controller_button_mapping); + controls[control].push_back(game_controller_button_mapping); + + break; + } + + default: + break; + } +} + +void input_event_router::remove_mappings(control* control) +{ + auto it = controls.find(control); + if (it != controls.end()) + { + for (input_mapping* mapping: it->second) + { + switch (mapping->get_type()) + { + case input_mapping_type::key: + key_mappings.remove(static_cast(mapping)); + break; + + case input_mapping_type::mouse_motion: + mouse_motion_mappings.remove(static_cast(mapping)); + break; + + case input_mapping_type::mouse_wheel: + mouse_wheel_mappings.remove(static_cast(mapping)); + break; + + case input_mapping_type::mouse_button: + mouse_button_mappings.remove(static_cast(mapping)); + break; + + case input_mapping_type::game_controller_axis: + game_controller_axis_mappings.remove(static_cast(mapping)); + break; + + case input_mapping_type::game_controller_button: + game_controller_button_mappings.remove(static_cast(mapping)); + break; + + default: + break; + } + + delete mapping; + } + + controls.erase(it); + } +} + +void input_event_router::set_event_dispatcher(::event_dispatcher* event_dispatcher) +{ + if (this->event_dispatcher) + { + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + } + + this->event_dispatcher = event_dispatcher; + + if (event_dispatcher) + { + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + } +} + +void input_event_router::remove_mappings() +{ + for (auto it = controls.begin(); it != controls.end(); ++it) + { + for (input_mapping* mapping: it->second) + { + delete mapping; + } + } + + controls.clear(); + key_mappings.clear(); + mouse_motion_mappings.clear(); + mouse_wheel_mappings.clear(); + mouse_button_mappings.clear(); + game_controller_axis_mappings.clear(); + game_controller_button_mappings.clear(); +} + +const std::list* input_event_router::get_mappings(control* control) const +{ + auto it = controls.find(control); + if (it == controls.end()) + { + return nullptr; + } + + return &it->second; +} + +void input_event_router::handle_event(const key_pressed_event& event) +{ + for (const key_mapping* mapping: key_mappings) + { + if ((!mapping->keyboard || mapping->keyboard == event.keyboard) && mapping->scancode == event.scancode) + { + mapping->control->set_current_value(1.0f); + } + } +} + +void input_event_router::handle_event(const key_released_event& event) +{ + for (const key_mapping* mapping: key_mappings) + { + if ((!mapping->keyboard || mapping->keyboard == event.keyboard) && mapping->scancode == event.scancode) + { + mapping->control->set_current_value(0.0f); + } + } +} + +void input_event_router::handle_event(const mouse_moved_event& event) +{ + for (const mouse_motion_mapping* mapping: mouse_motion_mappings) + { + if (!mapping->mouse || mapping->mouse == event.mouse) + { + if (mapping->axis == mouse_motion_axis::negative_x && event.dx < 0) + { + mapping->control->set_temporary_value(-event.dx); + } + else if (mapping->axis == mouse_motion_axis::positive_x && event.dx > 0) + { + mapping->control->set_temporary_value(event.dx); + } + else if (mapping->axis == mouse_motion_axis::negative_y && event.dy < 0) + { + mapping->control->set_temporary_value(-event.dy); + } + else if (mapping->axis == mouse_motion_axis::positive_y && event.dy > 0) + { + mapping->control->set_temporary_value(event.dy); + } + } + } +} + +void input_event_router::handle_event(const mouse_wheel_scrolled_event& event) +{ + for (const mouse_wheel_mapping* mapping: mouse_wheel_mappings) + { + if (!mapping->mouse || mapping->mouse == event.mouse) + { + if (mapping->axis == mouse_wheel_axis::negative_x && event.x < 0) + { + mapping->control->set_temporary_value(-event.x); + } + else if (mapping->axis == mouse_wheel_axis::positive_x && event.x > 0) + { + mapping->control->set_temporary_value(event.x); + } + else if (mapping->axis == mouse_wheel_axis::negative_y && event.y < 0) + { + mapping->control->set_temporary_value(-event.y); + } + else if (mapping->axis == mouse_wheel_axis::positive_y && event.y > 0) + { + mapping->control->set_temporary_value(event.y); + } + } + } +} + +void input_event_router::handle_event(const mouse_button_pressed_event& event) +{ + for (const mouse_button_mapping* mapping: mouse_button_mappings) + { + if ((!mapping->mouse || mapping->mouse == event.mouse) && mapping->button == event.button) + { + mapping->control->set_current_value(1.0f); + } + } +} + +void input_event_router::handle_event(const mouse_button_released_event& event) +{ + for (const mouse_button_mapping* mapping: mouse_button_mappings) + { + if ((!mapping->mouse || mapping->mouse == event.mouse) && mapping->button == event.button) + { + mapping->control->set_current_value(0.0f); + } + } +} + +void input_event_router::handle_event(const game_controller_axis_moved_event& event) +{ + for (const game_controller_axis_mapping* mapping: game_controller_axis_mappings) + { + if ((!mapping->game_controller || mapping->game_controller == event.game_controller) && mapping->axis == event.axis) + { + if (mapping->negative && event.value >= 0.0f || !mapping->negative && event.value <= 0.0f) + { + mapping->control->set_current_value(0.0f); + } + else + { + mapping->control->set_current_value(std::abs(event.value)); + } + } + } +} + +void input_event_router::handle_event(const game_controller_button_pressed_event& event) +{ + for (const game_controller_button_mapping* mapping: game_controller_button_mappings) + { + if ((!mapping->game_controller || mapping->game_controller == event.game_controller) && mapping->button == event.button) + { + mapping->control->set_current_value(1.0f); + } + } +} + +void input_event_router::handle_event(const game_controller_button_released_event& event) +{ + for (const game_controller_button_mapping* mapping: game_controller_button_mappings) + { + if ((!mapping->game_controller || mapping->game_controller == event.game_controller) && mapping->button == event.button) + { + mapping->control->set_current_value(0.0f); + } + } +} + diff --git a/src/antkeeper/input/input-event-router.hpp b/src/antkeeper/input/input-event-router.hpp new file mode 100644 index 0000000..a3ab42b --- /dev/null +++ b/src/antkeeper/input/input-event-router.hpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2020 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 ANTKEEER_INPUT_EVENT_ROUTER_HPP +#define ANTKEEER_INPUT_EVENT_ROUTER_HPP + +#include "input-events.hpp" +#include "event/event-handler.hpp" +#include +#include + +class control; +class event_dispatcher; +class input_mapping; +class key_mapping; +class mouse_motion_mapping; +class mouse_wheel_mapping; +class mouse_button_mapping; +class game_controller_axis_mapping; +class game_controller_button_mapping; +enum class mouse_motion_axis; +enum class mouse_wheel_axis; + +/** + * Uses input mappings to route input events to controls. + */ +class input_event_router: + public event_handler, + public event_handler, + public event_handler, + public event_handler, + public event_handler, + public event_handler, + public event_handler, + public event_handler, + public event_handler +{ +public: + /** + * Creates an input router and subscribes it to the input events of the specified event dispatcher. + */ + input_event_router(); + + /** + * Destroys an input router and unsubscribes it from input events. + */ + ~input_event_router(); + + /** + * Adds an input mapping to the router. + * + * @param mapping Input mapping to add. + */ + void add_mapping(const input_mapping& mapping); + + + /** + * Removes all input mappings from the router that are associated with the specified control. + * + * @param control control with which associated input mappings should be removed. + */ + void remove_mappings(control* control); + + /** + * Sets the event dispatcher to which this input event router will subscribe itself. + */ + void set_event_dispatcher(event_dispatcher* event_dispatcher); + + /** + * Removes all input mappings from the router. + */ + void remove_mappings(); + + /// Returns a list of mappings for the specified control, or nullptr if the control is unmapped. + const std::list* get_mappings(control* control) const; + +private: + virtual void handle_event(const key_pressed_event& event); + virtual void handle_event(const key_released_event& event); + virtual void handle_event(const mouse_moved_event& event); + virtual void handle_event(const mouse_wheel_scrolled_event& event); + virtual void handle_event(const mouse_button_pressed_event& event); + virtual void handle_event(const mouse_button_released_event& event); + virtual void handle_event(const game_controller_axis_moved_event& event); + virtual void handle_event(const game_controller_button_pressed_event& event); + virtual void handle_event(const game_controller_button_released_event& event); + + event_dispatcher* event_dispatcher; + std::map> controls; + std::list key_mappings; + std::list mouse_motion_mappings; + std::list mouse_wheel_mappings; + std::list mouse_button_mappings; + std::list game_controller_axis_mappings; + std::list game_controller_button_mappings; +}; + +#endif // ANTKEEER_INPUT_EVENT_ROUTER_HPP + diff --git a/src/antkeeper/input/input-events.cpp b/src/antkeeper/input/input-events.cpp new file mode 100644 index 0000000..53ef7c0 --- /dev/null +++ b/src/antkeeper/input/input-events.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2020 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 "input-events.hpp" + +event_base* key_pressed_event::clone() const +{ + key_pressed_event* event = new key_pressed_event(); + event->keyboard = keyboard; + event->scancode = scancode; + return event; +} + +event_base* key_released_event::clone() const +{ + key_released_event* event = new key_released_event(); + event->keyboard = keyboard; + event->scancode = scancode; + return event; +} + +event_base* mouse_moved_event::clone() const +{ + mouse_moved_event* event = new mouse_moved_event(); + event->mouse = mouse; + event->x = x; + event->y = y; + event->dx = dx; + event->dy = dy; + return event; +} + +event_base* mouse_button_pressed_event::clone() const +{ + mouse_button_pressed_event* event = new mouse_button_pressed_event(); + event->mouse = mouse; + event->button = button; + event->x = x; + event->y = y; + return event; +} + +event_base* mouse_button_released_event::clone() const +{ + mouse_button_released_event* event = new mouse_button_released_event(); + event->mouse = mouse; + event->button = button; + event->x = x; + event->y = y; + return event; +} + +event_base* mouse_wheel_scrolled_event::clone() const +{ + mouse_wheel_scrolled_event* event = new mouse_wheel_scrolled_event(); + event->mouse = mouse; + event->x = x; + event->y = y; + return event; +} + +event_base* game_controller_connected_event::clone() const +{ + game_controller_connected_event* event = new game_controller_connected_event(); + event->game_controller = game_controller; + event->reconnected = reconnected; + return event; +} + +event_base* game_controller_disconnected_event::clone() const +{ + game_controller_disconnected_event* event = new game_controller_disconnected_event(); + event->game_controller = game_controller; + return event; +} + +event_base* game_controller_button_pressed_event::clone() const +{ + game_controller_button_pressed_event* event = new game_controller_button_pressed_event(); + event->game_controller = game_controller; + event->button = button; + return event; +} + +event_base* game_controller_button_released_event::clone() const +{ + game_controller_button_released_event* event = new game_controller_button_released_event(); + event->game_controller = game_controller; + event->button = button; + return event; +} + +event_base* game_controller_axis_moved_event::clone() const +{ + game_controller_axis_moved_event* event = new game_controller_axis_moved_event(); + event->game_controller = game_controller; + event->axis = axis; + event->value = value; + return event; +} + diff --git a/src/antkeeper/input/input-events.hpp b/src/antkeeper/input/input-events.hpp new file mode 100644 index 0000000..11b94b7 --- /dev/null +++ b/src/antkeeper/input/input-events.hpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2020 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_INPUT_EVENT_HPP +#define ANTKEEPER_INPUT_EVENT_HPP + +#include "event/event.hpp" + +enum class scancode; +enum class game_controller_axis; +enum class game_controller_button; +class keyboard; +class mouse; +class game_controller; + +/** + * Input event which indicates a keyboard key has been pressed. + */ +class key_pressed_event: public event +{ +public: + virtual event_base* clone() const; + + keyboard* keyboard; + scancode scancode; +}; + +/** + * Input event which indicates a keyboard key has been released. + */ +class key_released_event: public event +{ +public: + virtual event_base* clone() const; + + keyboard* keyboard; + scancode scancode; +}; + +/** + * Input event which indicates a mouse has been moved. + */ +class mouse_moved_event: public event +{ +public: + virtual event_base* clone() const; + + mouse* mouse; + int x; + int y; + int dx; + int dy; +}; + +/** + * Input event which indicates a mouse button has been pressed. + */ +class mouse_button_pressed_event: public event +{ +public: + virtual event_base* clone() const; + + mouse* mouse; + int button; + int x; + int y; +}; + +/** + * Input event which indicates a mouse button has been released. + */ +class mouse_button_released_event: public event +{ +public: + virtual event_base* clone() const; + + mouse* mouse; + int button; + int x; + int y; +}; + +/** + * Input event which indicates a mouse wheel has been scrolled. + */ +class mouse_wheel_scrolled_event: public event +{ +public: + virtual event_base* clone() const; + + mouse* mouse; + int x; + int y; +}; + +/** + * Input event which indicates a controller has been connected. + */ +class game_controller_connected_event: public event +{ +public: + virtual event_base* clone() const; + + game_controller* game_controller; + bool reconnected; +}; + +/** + * Input event which indicates a controller has been disconnected. + */ +class game_controller_disconnected_event: public event +{ +public: + virtual event_base* clone() const; + + game_controller* game_controller; +}; + +/** + * Input event which indicates a controller button has been pressed. + */ +class game_controller_button_pressed_event: public event +{ +public: + virtual event_base* clone() const; + + game_controller* game_controller; + game_controller_button button; +}; + +/** + * Input event which indicates a controller button has been released. + */ +class game_controller_button_released_event: public event +{ +public: + virtual event_base* clone() const; + + game_controller* game_controller; + game_controller_button button; +}; + +/** + * Input event which indicates a controller axis has been moved. + */ +class game_controller_axis_moved_event: public event +{ +public: + virtual event_base* clone() const; + + game_controller* game_controller; + game_controller_axis axis; + float value; +}; + +#endif // ANTKEEPER_INPUT_EVENT_HPP + diff --git a/src/antkeeper/input/input-mapper.cpp b/src/antkeeper/input/input-mapper.cpp new file mode 100644 index 0000000..17e7b90 --- /dev/null +++ b/src/antkeeper/input/input-mapper.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2020 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 "input-mapper.hpp" +#include "mouse.hpp" +#include "event/event-dispatcher.hpp" + +input_mapper::input_mapper(): + event_dispatcher(nullptr), + control(nullptr), + callback(nullptr), + enabled(false) +{} + +input_mapper::~input_mapper() +{ + set_event_dispatcher(nullptr); +} + +void input_mapper::set_event_dispatcher(::event_dispatcher* event_dispatcher) +{ + if (this->event_dispatcher) + { + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + this->event_dispatcher->unsubscribe(this); + } + + this->event_dispatcher = event_dispatcher; + + if (event_dispatcher) + { + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + event_dispatcher->subscribe(this); + } +} + +void input_mapper::set_control(::control* control) +{ + this->control = control; +} + +void input_mapper::set_callback(std::function callback) +{ + this->callback = callback; +} + +void input_mapper::set_enabled(bool enabled) +{ + this->enabled = enabled; +} + +void input_mapper::handle_event(const key_pressed_event& event) +{ + if (!is_enabled() || !callback) + { + return; + } + + callback(key_mapping(control, event.keyboard, event.scancode)); +} + +void input_mapper::handle_event(const mouse_moved_event& event) +{ + if (!is_enabled() || !callback) + { + return; + } + + if (event.dx != 0) + { + mouse_motion_axis axis = (event.dx < 0) ? mouse_motion_axis::negative_x : mouse_motion_axis::positive_x; + callback(mouse_motion_mapping(control, event.mouse, axis)); + } + + if (event.dy != 0) + { + mouse_motion_axis axis = (event.dy < 0) ? mouse_motion_axis::negative_y : mouse_motion_axis::positive_y; + callback(mouse_motion_mapping(control, event.mouse, axis)); + } +} + +void input_mapper::handle_event(const mouse_button_pressed_event& event) +{ + if (!is_enabled() || !callback) + { + return; + } + + callback(mouse_button_mapping(control, event.mouse, event.button)); +} + +void input_mapper::handle_event(const mouse_wheel_scrolled_event& event) +{ + if (!is_enabled() || !callback) + { + return; + } + + if (event.x != 0) + { + mouse_wheel_axis axis = (event.x < 0) ? mouse_wheel_axis::negative_x : mouse_wheel_axis::positive_x; + callback(mouse_wheel_mapping(control, event.mouse, axis)); + } + + if (event.y != 0) + { + mouse_wheel_axis axis = (event.y < 0) ? mouse_wheel_axis::negative_y : mouse_wheel_axis::positive_y; + callback(mouse_wheel_mapping(control, event.mouse, axis)); + } +} + +void input_mapper::handle_event(const game_controller_button_pressed_event& event) +{ + if (!is_enabled() || !callback) + { + return; + } + + callback(game_controller_button_mapping(control, event.game_controller, event.button)); +} + +void input_mapper::handle_event(const game_controller_axis_moved_event& event) +{ + if (!is_enabled() || !callback) + { + return; + } + + callback(game_controller_axis_mapping(control, event.game_controller, event.axis, (event.value < 0.0f))); +} + diff --git a/src/antkeeper/input/input-mapper.hpp b/src/antkeeper/input/input-mapper.hpp new file mode 100644 index 0000000..6e9f5c4 --- /dev/null +++ b/src/antkeeper/input/input-mapper.hpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2020 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_INPUT_MAPPER_HPP +#define ANTKEEPER_INPUT_MAPPER_HPP + +#include "input-events.hpp" +#include "input-mapping.hpp" +#include "event/event-handler.hpp" +#include + +class event_dispatcher; + +/** + * An input mapper takes a control and listens to input events then generates corresponding input mappings which can be added to an input router. + */ +class input_mapper: + public event_handler, + public event_handler, + public event_handler, + public event_handler, + public event_handler, + public event_handler +{ +public: + /** + * Creates an input mapper. + */ + input_mapper(); + + /** + * Destroys an input mapper. + */ + virtual ~input_mapper(); + + /** + * Sets the event dispatcher to which this input event router will subscribe itself. + */ + void set_event_dispatcher(event_dispatcher* event_dispatcher); + + /** + * Sets the control for which input mappings will be generated. + * + * @param control ::control for which input mappings will be generated. + */ + void set_control(::control* control); + + /** + * Sets the callback function to the input mappings generated by this input mapper. + * + * @param callback Callback function operates on an input mapping. + */ + void set_callback(std::function callback); + + /** + * Enables or disables the input mapping generation. + * + * @param enabled Whether to enable input mapping generation or not. + */ + void set_enabled(bool enabled); + + /** + * Returns true if input mapping generation is enabled. + */ + bool is_enabled() const; + +private: + void handle_event(const key_pressed_event& event); + void handle_event(const mouse_moved_event& event); + void handle_event(const mouse_wheel_scrolled_event& event); + void handle_event(const mouse_button_pressed_event& event); + void handle_event(const game_controller_axis_moved_event& event); + void handle_event(const game_controller_button_pressed_event& event); + + event_dispatcher* event_dispatcher; + ::control* control; + std::function callback; + bool enabled; +}; + +inline bool input_mapper::is_enabled() const +{ + return enabled; +} + +#endif // ANTKEEPER_INPUT_MAPPER_HPP + diff --git a/src/antkeeper/input/input-mapping.cpp b/src/antkeeper/input/input-mapping.cpp new file mode 100644 index 0000000..569e09d --- /dev/null +++ b/src/antkeeper/input/input-mapping.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2020 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 "input-mapping.hpp" + +input_mapping::input_mapping(::control* control): + control(control) +{} + +key_mapping::key_mapping(const key_mapping& mapping) +{ + *this = mapping; +} + +key_mapping::key_mapping(::control* control, ::keyboard* keyboard, ::scancode scancode): + input_mapping(control), + keyboard(keyboard), + scancode(scancode) +{} + +key_mapping& key_mapping::operator=(const key_mapping& mapping) +{ + control = mapping.control; + keyboard = mapping.keyboard; + scancode = mapping.scancode; + return *this; +} + +mouse_motion_mapping::mouse_motion_mapping(const mouse_motion_mapping& mapping) +{ + *this = mapping; +} + +mouse_motion_mapping::mouse_motion_mapping(::control* control, ::mouse* mouse, mouse_motion_axis axis): + input_mapping(control), + mouse(mouse), + axis(axis) +{} + +mouse_motion_mapping& mouse_motion_mapping::operator=(const mouse_motion_mapping& mapping) +{ + control = mapping.control; + mouse = mapping.mouse; + axis = mapping.axis; + return *this; +} + +mouse_wheel_mapping::mouse_wheel_mapping(const mouse_wheel_mapping& mapping) +{ + *this = mapping; +} + +mouse_wheel_mapping::mouse_wheel_mapping(::control* control, ::mouse* mouse, ::mouse_wheel_axis axis): + input_mapping(control), + mouse(mouse), + axis(axis) +{} + +mouse_wheel_mapping& mouse_wheel_mapping::operator=(const mouse_wheel_mapping& mapping) +{ + control = mapping.control; + mouse = mapping.mouse; + axis = mapping.axis; + return *this; +} + +mouse_button_mapping::mouse_button_mapping(const mouse_button_mapping& mapping) +{ + *this = mapping; +} + +mouse_button_mapping::mouse_button_mapping(::control* control, ::mouse* mouse, int button): + input_mapping(control), + mouse(mouse), + button(button) +{} + +mouse_button_mapping& mouse_button_mapping::operator=(const mouse_button_mapping& mapping) +{ + control = mapping.control; + mouse = mapping.mouse; + button = mapping.button; + return *this; +} + +game_controller_axis_mapping::game_controller_axis_mapping(const game_controller_axis_mapping& mapping) +{ + *this = mapping; +} + +game_controller_axis_mapping::game_controller_axis_mapping(::control* control, ::game_controller* game_controller, game_controller_axis axis, bool negative): + input_mapping(control), + game_controller(game_controller), + axis(axis), + negative(negative) +{} + +game_controller_axis_mapping& game_controller_axis_mapping::operator=(const game_controller_axis_mapping& mapping) +{ + control = mapping.control; + game_controller = mapping.game_controller; + axis = mapping.axis; + negative = mapping.negative; + return *this; +} + +game_controller_button_mapping::game_controller_button_mapping(const game_controller_button_mapping& mapping) +{ + *this = mapping; +} + +game_controller_button_mapping::game_controller_button_mapping(::control* control, ::game_controller* game_controller, game_controller_button button): + input_mapping(control), + game_controller(game_controller), + button(button) +{} + +game_controller_button_mapping& game_controller_button_mapping::operator=(const game_controller_button_mapping& mapping) +{ + control = mapping.control; + game_controller = mapping.game_controller; + button = mapping.button; + return *this; +} + diff --git a/src/antkeeper/input/input-mapping.hpp b/src/antkeeper/input/input-mapping.hpp new file mode 100644 index 0000000..b0f81d5 --- /dev/null +++ b/src/antkeeper/input/input-mapping.hpp @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2020 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_INPUT_MAPPING_HPP +#define ANTKEEPER_INPUT_MAPPING_HPP + +enum class mouse_motion_axis; +enum class mouse_wheel_axis; +enum class scancode; +enum class game_controller_axis; +enum class game_controller_button; +class control; +class keyboard; +class mouse; +class game_controller; + +/** + * Enumerates the supported types of control mappings. + */ +enum class input_mapping_type +{ + key, + mouse_motion, + mouse_wheel, + mouse_button, + game_controller_axis, + game_controller_button +}; + +/** + * Abstract base class for input mappings. + */ +class input_mapping +{ +public: + input_mapping() = default; + input_mapping(::control* control); + virtual ~input_mapping() = default; + + /// Returns this control mapping's type. + virtual input_mapping_type get_type() const = 0; + + control* control; +}; + +/** + * A mapping between a control and a keyboard key. + */ +class key_mapping: public input_mapping +{ +public: + key_mapping() = default; + key_mapping(const key_mapping& mapping); + key_mapping(::control* control, keyboard* keyboard, scancode scancode); + virtual ~key_mapping() = default; + key_mapping& operator=(const key_mapping& mapping); + virtual input_mapping_type get_type() const; + + keyboard* keyboard; + scancode scancode; +}; + +inline input_mapping_type key_mapping::get_type() const +{ + return input_mapping_type::key; +} + +/** + * A mapping between a control and a mouse motion axis. + */ +class mouse_motion_mapping: public input_mapping +{ +public: + mouse_motion_mapping() = default; + mouse_motion_mapping(const mouse_motion_mapping& mapping); + mouse_motion_mapping(::control* control, ::mouse* mouse, mouse_motion_axis axis); + virtual ~mouse_motion_mapping() = default; + mouse_motion_mapping& operator=(const mouse_motion_mapping& mapping); + virtual input_mapping_type get_type() const; + + mouse* mouse; + mouse_motion_axis axis; +}; + +inline input_mapping_type mouse_motion_mapping::get_type() const +{ + return input_mapping_type::mouse_motion; +} + +/** + * A mapping between a control and a mouse wheel axis. + */ +class mouse_wheel_mapping: public input_mapping +{ +public: + mouse_wheel_mapping() = default; + mouse_wheel_mapping(const mouse_wheel_mapping& mapping); + mouse_wheel_mapping(::control* control, mouse* mouse, mouse_wheel_axis axis); + virtual ~mouse_wheel_mapping() = default; + mouse_wheel_mapping& operator=(const mouse_wheel_mapping& mapping); + virtual input_mapping_type get_type() const; + + mouse* mouse; + mouse_wheel_axis axis; +}; + +inline input_mapping_type mouse_wheel_mapping::get_type() const +{ + return input_mapping_type::mouse_wheel; +} + +/** + * A mapping between a control and a mouse button. + */ +class mouse_button_mapping: public input_mapping +{ +public: + mouse_button_mapping() = default; + mouse_button_mapping(const mouse_button_mapping& mapping); + mouse_button_mapping(::control* control, mouse* mouse, int button); + virtual ~mouse_button_mapping() = default; + mouse_button_mapping& operator=(const mouse_button_mapping& mapping); + virtual input_mapping_type get_type() const; + + mouse* mouse; + int button; +}; + +inline input_mapping_type mouse_button_mapping::get_type() const +{ + return input_mapping_type::mouse_button; +} + +/** + * A mapping between a control and a game controller axis. + */ +class game_controller_axis_mapping: public input_mapping +{ +public: + game_controller_axis_mapping() = default; + game_controller_axis_mapping(const game_controller_axis_mapping& mapping); + game_controller_axis_mapping(::control* control, game_controller* game_controller, game_controller_axis axis, bool negative); + virtual ~game_controller_axis_mapping() = default; + game_controller_axis_mapping& operator=(const game_controller_axis_mapping& mapping); + virtual input_mapping_type get_type() const; + + game_controller* game_controller; + game_controller_axis axis; + bool negative; +}; + +inline input_mapping_type game_controller_axis_mapping::get_type() const +{ + return input_mapping_type::game_controller_axis; +} + +/** + * A mapping between a control and a game controller button. + * + * @ingroup input. + */ +class game_controller_button_mapping: public input_mapping +{ +public: + game_controller_button_mapping() = default; + game_controller_button_mapping(const game_controller_button_mapping& mapping); + game_controller_button_mapping(::control* control, game_controller* game_controller, game_controller_button button); + virtual ~game_controller_button_mapping() = default; + game_controller_button_mapping& operator=(const game_controller_button_mapping& mapping); + virtual input_mapping_type get_type() const; + + game_controller* game_controller; + game_controller_button button; +}; + +inline input_mapping_type game_controller_button_mapping::get_type() const +{ + return input_mapping_type::game_controller_button; +} + +#endif // ANTKEEPER_INPUT_MAPPING_HPP + diff --git a/src/antkeeper/input/keyboard.cpp b/src/antkeeper/input/keyboard.cpp new file mode 100644 index 0000000..7a2b2f3 --- /dev/null +++ b/src/antkeeper/input/keyboard.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2020 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 "keyboard.hpp" +#include "scancode.hpp" +#include "event/event-dispatcher.hpp" +#include "input-events.hpp" + +const char* keyboard::get_scancode_name(scancode scancode) +{ + return scancode_names[static_cast(scancode)]; +}; + +scancode keyboard::get_scancode_from_name(const char* name) +{ + auto it = scancode_map.find(std::string(name)); + if (it == scancode_map.end()) + { + return scancode::unknown; + } + + return it->second; +} + +keyboard::keyboard() +{} + +keyboard::~keyboard() +{} + +void keyboard::press(scancode scancode) +{ + if (!input_device::event_dispatcher) + { + return; + } + + key_pressed_event event; + event.keyboard = this; + event.scancode = scancode; + + input_device::event_dispatcher->queue(event); +} + +void keyboard::release(scancode scancode) +{ + if (!input_device::event_dispatcher) + { + return; + } + + key_released_event event; + event.keyboard = this; + event.scancode = scancode; + + input_device::event_dispatcher->queue(event); +} + +std::map keyboard::build_scancode_map() +{ + std::map scancode_map; + for (std::size_t i = 0; i <= static_cast(scancode::audio_fast_forward); ++i) + { + if (scancode_names[i] != nullptr) + { + std::string scancode_name = scancode_names[i]; + scancode_map[scancode_name] = static_cast(i); + } + } + + return scancode_map; +} + +const char* keyboard::scancode_names[] = +{ + nullptr, // UNKNOWN + "A", // A + "B", // B + "C", // C + "D", // D + "E", // E + "F", // F + "G", // G + "H", // H + "I", // I + "J", // J + "K", // K + "L", // L + "M", // M + "N", // N + "O", // O + "P", // P + "Q", // Q + "R", // R + "S", // S + "T", // T + "U", // U + "V", // V + "W", // W + "X", // X + "Y", // Y + "Z", // Z + "1", // ONE + "2", // TWO + "3", // THREE + "4", // FOUR + "5", // FIVE + "6", // SIX + "7", // SEVEN + "8", // EIGHT + "9", // NINE + "0", // ZERO + "Enter", // ENTER + "Escape", // ESCAPE + "Backspace", // BACKSPACE + "Tab", // TAB + "Space", // SPACE + "-", // MINUS + "=", // EQUALS + "[", // LEFTBRACKET + "]", // RIGHTBRACKET + "\\", // BACKSLASH + "#", // NONUSHASH + ";", // SEMICOLON + "'", // APOSTROPHE + "`", // GRAVE + ",", // COMMA + ".", // PERIOD + "/", // SLASH + "Caps Lock", // CAPSLOCK + "F1", // F1 + "F2", // F2 + "F3", // F3 + "F4", // F4 + "F5", // F5 + "F6", // F6 + "F7", // F7 + "F8", // F8 + "F9", // F9 + "F10", // F10 + "F11", // F11 + "F12", // F12 + "Print Screen", // PRINTSCREEN + "Scroll Lock", // SCROLLLOCK + "Pause", // PAUSE + "Insert", // INSERT + "Home", // HOME + "Page Up", // PAGEUP + "Delete", // DELETE + "End", // END + "Page Down", // PAGEDOWN + "Right", // RIGHT + "Left", // LEFT + "Down", // DOWN + "Up", // UP + "Num Lock", // NUMLOCKCLEAR + "Keypad /", // KP_DIVIDE + "Keypad *", // KP_MULTIPLY + "Keypad -", // KP_MINUS + "Keypad +", // KP_PLUS + "Keypad Enter", // KP_ENTER + "Keypad 1", // KP_1 + "Keypad 2", // KP_2 + "Keypad 3", // KP_3 + "Keypad 4", // KP_4 + "Keypad 5", // KP_5 + "Keypad 6", // KP_6 + "Keypad 7", // KP_7 + "Keypad 8", // KP_8 + "Keypad 9", // KP_9 + "Keypad 0", // KP_0 + "Keypad .", // KP_PERIOD + nullptr, // NONUSBACKSLASH + "Application", // APPLICATION + "Power", // POWER + "Keypad =", // KP_EQUALS + "F13", // F13 + "F14", // F14 + "F15", // F15 + "F16", // F16 + "F17", // F17 + "F18", // F18 + "F19", // F19 + "F20", // F20 + "F21", // F21 + "F22", // F22 + "F23", // F23 + "F24", // F24 + "Execute", // EXECUTE + "Help", // HELP + "Menu", // MENU + "Select", // SELECT + "Stop", // STOP + "Again", // AGAIN + "Undo", // UNDO + "Cut", // CUT + "Copy", // COPY + "Paste", // PASTE + "Find", // FIND + "Mute", // MUTE + "Volume Up", // VOLUMEUP + "Volume Down", // VOLUMEDOWN + nullptr, // LOCKINGCAPSLOCK + nullptr, // LOCKINGNUMLOCK + nullptr, // LOCKINGSCROLLLOCK + "Keypad ,", // KP_COMMA + "Keypad = (AS400)", // KP_EQUALSAS400 + nullptr, // INTERNATIONAL1 + nullptr, // INTERNATIONAL2 + nullptr, // INTERNATIONAL3 + nullptr, // INTERNATIONAL4 + nullptr, // INTERNATIONAL5 + nullptr, // INTERNATIONAL6 + nullptr, // INTERNATIONAL7 + nullptr, // INTERNATIONAL8 + nullptr, // INTERNATIONAL9 + nullptr, // LANG1 + nullptr, // LANG2 + nullptr, // LANG3 + nullptr, // LANG4 + nullptr, // LANG5 + nullptr, // LANG6 + nullptr, // LANG7 + nullptr, // LANG8 + nullptr, // LANG9 + "Alt Erase", // ALTERASE + "Sys Req", // SYSREQ + "Cancel", // CANCEL + "Clear", // CLEAR + "Prior", // PRIOR + "Return", // RETURN2 + "Separator", // SEPARATOR + "Out", // OUT + "Oper", // OPER + "Clear/Again", // CLEARAGAIN + "CrSel", // CRSEL + "ExSel", // EXSEL + "Keypad 00", // KP_00 + "Keypad 000", // KP_000 + "Thousands Separator", // THOUSANDSSEPARATOR + "Decimal Separator", // DECIMALSEPARATOR + "Currency Unit", // CURRENCYUNIT + "Currency Sub-Unit", // CURRENCYSUBUNIT + "Keypad (", // KP_LEFTPAREN + "Keypad )", // KP_RIGHTPAREN + "Keypad {", // KP_LEFTBRACE + "Keypad }", // KP_RIGHTBRACE + "Keypad Tab", // KP_TAB + "Keypad Backspace", // KP_BACKSPACE + "Keypad A", // KP_A + "Keypad B", // KP_B + "Keypad C", // KP_C + "Keypad D", // KP_D + "Keypad E", // KP_E + "Keypad F", // KP_F + "Keypad XOR", // KP_XOR + "Keypad ^", // KP_POWER + "Keypad %", // KP_PERCENT + "Keypad <", // KP_LESS + "Keypad >", // KP_GREATER + "Keypad &", // KP_AMPERSAND + "Keypad &&", // KP_DBLAMPERSAND + "Keypad |", // KP_VERTICALBAR + "Keypad ||", // KP_DBLVERTICALBAR + "Keypad :", // KP_COLON + "Keypad #", // KP_HASH + "Keypad Space", // KP_SPACE + "Keypad @", // KP_AT + "Keypad !", // KP_EXCLAM + "Keypad Mem Store", // KP_MEMSTORE + "Keypad Mem Recall", // KP_MEMRECALL + "Keypad Mem Clear", // KP_MEMCLEAR + "Keypad Mem Add", // KP_MEMADD + "Keypad Mem Subtract", // KP_MEMSUBTRACT + "Keypad Mem Multiply", // KP_MEMMULTIPLY + "Keypad Mem Divide", // KP_MEMDIVIDE + "Keypad +/-", // KP_PLUSMINUS + "Keypad Clear", // KP_CLEAR + "Keypad Clear Entry", // KP_CLEARENTRY + "Keypad Binary", // KP_BINARY + "Keypad Octal", // KP_OCTAL + "Keypad Decimal", // KP_DECIMAL + "Keypad Hexadecimal", // KP_HEXADECIMAL + "Left Ctrl", // LCTRL + "Left Shift", // LSHIFT + "Left Alt", // LALT + "Left GUI", // LGUI + "Right Ctrl", // RCTRL + "Right Shift", // RSHIFT + "Right Alt", // RALT + "Right GUI", // RGUI + "Mode Switch", // MODE + "Audio Next", // AUDIONEXT + "Audio Prev", // AUDIOPREV + "Audio Stop", // AUDIOSTOP + "Audio Play", // AUDIOPLAY + "Audio Mute", // AUDIOMUTE + "Media Select", // MEDIASELECT + "WWW", // WWW + "Mail", // MAIL + "Calculator", // CALCULATOR + "Computer", // COMPUTER + "AC Search", // AC_SEARCH + "AC Home", // AC_HOME + "AC Back", // AC_BACK + "AC Forward", // AC_FORWARD + "AC Stop", // AC_STOP + "AC Refresh", // AC_REFRESH + "AC Bookmarks", // AC_BOOKMARKS + "Brightness Down", // BRIGHTNESSDOWN + "Brightness Up", // BRIGHTNESSUP + "Display Switch", // DISPLAYSWITCH + "KBD Illum Toggle", // KBDILLUMTOGGLE + "KBD Illum Down", // KBDILLUMDOWN + "KBD Illum Up", // KBDILLUMUP + "Eject", // EJECT + "Sleep", // SLEEP + "App 1", // APP1 + "App 2", // APP2 + "Audio Rewind", // AUDIOREWIND + "Audio Fast-Forward", // AUDIOFASTFORWARD +}; + +std::map keyboard::scancode_map = keyboard::build_scancode_map(); + diff --git a/src/antkeeper/input/keyboard.hpp b/src/antkeeper/input/keyboard.hpp new file mode 100644 index 0000000..04540ba --- /dev/null +++ b/src/antkeeper/input/keyboard.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 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_KEYBOARD_HPP +#define ANTKEEPER_KEYBOARD_HPP + +#include "input-device.hpp" +#include +#include + +enum class scancode; + +/** + * A virtual keyboard which can generate keyboard-related input events and pass them to an event dispatcher. + */ +class keyboard: public input_device +{ +public: + /** + * Returns the UTF-8 encoded name of a scancode. + */ + static const char* get_scancode_name(scancode scancode); + + /** + * Returns the scancode corresponding to a scancode name. + * + * @param name Name of a scancode. + * @return Corresponding scancode, or nullptr if a matching scancode was not found. + */ + static scancode get_scancode_from_name(const char* name); + + /** + * Creates a keyboard input device. + */ + keyboard(); + + /// Destroys a keyboard input device. + virtual ~keyboard(); + + /** + * Simulates a key press. + * + * @param scancode scancode of the simulated key press. + */ + void press(scancode scancode); + + /** + * Simulates a key release. + * + * @param scancode scancode of the simulated key release. + */ + void release(scancode scancode); + +private: + static std::map build_scancode_map(); + static const char* scancode_names[]; + static std::map scancode_map; +}; + +#endif // ANTKEEPER_KEYBOARD_HPP + diff --git a/src/antkeeper/input/mouse.cpp b/src/antkeeper/input/mouse.cpp new file mode 100644 index 0000000..991b98f --- /dev/null +++ b/src/antkeeper/input/mouse.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2020 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 "mouse.hpp" +#include "input-events.hpp" +#include "event/event-dispatcher.hpp" + +mouse::mouse() +{} + +void mouse::press(int button, int x, int y) +{ + if (!input_device::event_dispatcher) + { + return; + } + + mouse_button_pressed_event event; + event.mouse = this; + event.button = button; + event.x = x; + event.y = y; + + input_device::event_dispatcher->queue(event); +} + +void mouse::release(int button, int x, int y) +{ + if (!input_device::event_dispatcher) + { + return; + } + + mouse_button_released_event event; + event.mouse = this; + event.button = button; + event.x = x; + event.y = y; + + input_device::event_dispatcher->queue(event); +} + +void mouse::move(int x, int y, int dx, int dy) +{ + previous_position = current_position; + current_position = {x, y}; + + if (!input_device::event_dispatcher) + { + return; + } + + mouse_moved_event event; + event.mouse = this; + event.x = x; + event.y = y; + event.dx = dx; + event.dy = dy; + + input_device::event_dispatcher->queue(event); +} + +void mouse::scroll(int x, int y) +{ + if (!input_device::event_dispatcher) + { + return; + } + + mouse_wheel_scrolled_event event; + event.mouse = this; + event.x = x; + event.y = y; + + input_device::event_dispatcher->queue(event); +} + diff --git a/src/antkeeper/input/mouse.hpp b/src/antkeeper/input/mouse.hpp new file mode 100644 index 0000000..2297361 --- /dev/null +++ b/src/antkeeper/input/mouse.hpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2020 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_MOUSE_HPP +#define ANTKEEPER_MOUSE_HPP + +#include "input-device.hpp" +#include + +/** + * Enumerates the mouse motion axes. + */ +enum class mouse_motion_axis +{ + /// Indicates the positive X-axis. + positive_x, + + /// Indicates the negative X-axis. + negative_x, + + /// Indicates the positive Y-axis. + positive_y, + + /// Indicates the negative Y-axis. + negative_y +}; + +/** + * Enumerates the mouse wheel axes. + */ +enum class mouse_wheel_axis +{ + /// Indicates the positive X-axis. + positive_x, + + /// Indicates the negative X-axis. + negative_x, + + /// Indicates the positive Y-axis. + positive_y, + + /// Indicates the negative Y-axis. + negative_y +}; + +/** + * A virtual mouse which can generate mouse-related input events and pass them to an event dispatcher. + */ +class mouse: public input_device +{ +public: + /** + * Creates a mouse input device. + */ + mouse(); + + /// Destroys a mouse input device. + virtual ~mouse() = default; + + /** + * Simulates a mouse button press. + * + * @param button Index of the pressed button. + * @param x X-coordinate of the mouse position. + * @param y Y-coordinate of the mouse position. + */ + void press(int button, int x, int y); + + /** + * Simulates a mouse button release. + * + * @param button Index of the released button. + * @param x X-coordinate of the mouse position. + * @param y Y-coordinate of the mouse position. + */ + void release(int button, int x, int y); + + /** + * Simulates mouse movement. + * + * @param x X-coordinate of the mouse position. + * @param y Y-coordinate of the mouse position. + * @param dx Relative movement on the X-axis. + * @param dy Relative movement on the Y-axis. + */ + void move(int x, int y, int dx, int dy); + + /** + * Simulates mouse wheel scrolling. + */ + void scroll(int x, int y); + + /// Returns the current mouse position. + const std::tuple& get_current_position() const; + + /// Returns the previous mouse position. + const std::tuple& get_previous_position() const; + +private: + std::tuple current_position; + std::tuple previous_position; +}; + +inline const std::tuple& mouse::get_current_position() const +{ + return current_position; +} + +inline const std::tuple& mouse::get_previous_position() const +{ + return previous_position; +} + +#endif // ANTKEEPER_MOUSE_HPP + diff --git a/src/antkeeper/input/scancode.hpp b/src/antkeeper/input/scancode.hpp new file mode 100644 index 0000000..e7e7cc6 --- /dev/null +++ b/src/antkeeper/input/scancode.hpp @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2020 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_SCANCODE_HPP +#define ANTKEEPER_SCANCODE_HPP + +/** + * Enumerates keyboard scancodes. + */ +enum class scancode +{ + unknown, + a, + b, + c, + d, + e, + f, + g, + h, + i, + j, + k, + l, + m, + n, + o, + p, + q, + r, + s, + t, + u, + v, + w, + x, + y, + z, + one, + two, + three, + four, + five, + six, + seven, + eight, + nine, + zero, + enter, + escape, + backspace, + tab, + space, + minus, + equal, + left_brace, + right_brace, + backslash, + non_us_hash, + semicolon, + apostrophe, + grave, + comma, + dot, + slash, + caps_lock, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + print_screen, + scroll_lock, + pause, + insert, + home, + page_up, + del, + end, + page_down, + right, + left, + down, + up, + num_lock, + kp_slash, + kp_asterisk, + kp_minus, + kp_plus, + kp_enter, + kp_1, + kp_2, + kp_3, + kp_4, + kp_5, + kp_6, + kp_7, + kp_8, + kp_9, + kp_0, + kp_dot, + non_us_backslash, + application, + power, + kp_equal, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + f22, + f23, + f24, + execute, + help, + menu, + select, + stop, + again, + undo, + cut, + copy, + paste, + find, + mute, + volume_up, + volume_down, + locking_caps_lock, + locking_num_lock, + locking_scroll_lock, + kp_comma, + kp_equal_as400, + international_1, + international_2, + international_3, + international_4, + international_5, + international_6, + international_7, + international_8, + international_9, + lang_1, + lang_2, + lang_3, + lang_4, + lang_5, + lang_6, + lang_7, + lang_8, + lang_9, + alt_erase, + sys_req, + cancel, + clear, + prior, + return_2, + separator, + _out, + oper, + clear_again, + cr_sel, + ex_sel, + kp_00, + kp_000, + thousands_separator, + decimal_separator, + currency_unit, + currency_sub_unit, + kp_left_paren, + kp_right_paren, + kp_left_brace, + kp_right_brace, + kp_tab, + kp_backspace, + kp_a, + kp_b, + kp_c, + kp_d, + kp_e, + kp_f, + kp_xor, + kp_power, + kp_percent, + kp_less, + kp_greater, + kp_ampersand, + kp_double_ampersand, + kp_vertical_bar, + kp_double_vertical_bar, + kp_colon, + kp_hash, + kp_space, + kp_at, + kp_exclam, + kp_mem_store, + kp_mem_recall, + kp_mem_clear, + kp_mem_add, + kp_mem_subtract, + kp_mem_multiply, + kp_mem_divide, + kp_plus_minus, + kp_clear, + kp_clear_entry, + kp_binary, + kp_octal, + kp_decimal, + kp_hexadecimal, + left_ctrl, + left_shift, + left_alt, + left_gui, + right_ctrl, + right_shift, + right_alt, + right_gui, + mode, + audio_next, + audio_prev, + audio_stop, + audio_play, + audio_mute, + media_select, + www, + mail, + calculator, + computer, + ac_search, + ac_home, + ac_back, + ac_forward, + ac_stop, + ac_refresh, + ac_bookmarks, + brightness_down, + brightness_up, + display_switch, + kbd_illum_toggle, + kbd_illum_down, + kbd_illum_up, + eject, + sleep, + app_1, + app_2, + audio_rewind, + audio_fast_forward +}; + +#endif // ANTKEEPER_SCANCODE_HPP + diff --git a/src/antkeeper/input/sdl-game-controller-tables.cpp b/src/antkeeper/input/sdl-game-controller-tables.cpp new file mode 100644 index 0000000..e47912f --- /dev/null +++ b/src/antkeeper/input/sdl-game-controller-tables.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 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 "sdl-game-controller-tables.hpp" +#include "game-controller.hpp" + +const game_controller_button sdl_button_table[15] = +{ + game_controller_button::a, // SDL_CONTROLLER_BUTTON_A, + game_controller_button::b, // SDL_CONTROLLER_BUTTON_B, + game_controller_button::x, // SDL_CONTROLLER_BUTTON_X, + game_controller_button::y, // SDL_CONTROLLER_BUTTON_Y, + game_controller_button::back, // SDL_CONTROLLER_BUTTON_BACK, + game_controller_button::guide, // SDL_CONTROLLER_BUTTON_GUIDE, + game_controller_button::start, // SDL_CONTROLLER_BUTTON_START, + game_controller_button::left_stick, // SDL_CONTROLLER_BUTTON_LEFTSTICK, + game_controller_button::right_stick, // SDL_CONTROLLER_BUTTON_RIGHTSTICK, + game_controller_button::left_shoulder, // SDL_CONTROLLER_BUTTON_LEFTSHOULDER, + game_controller_button::right_shoulder, // SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, + game_controller_button::dpad_up, // SDL_CONTROLLER_BUTTON_DPAD_UP, + game_controller_button::dpad_down, // SDL_CONTROLLER_BUTTON_DPAD_DOWN, + game_controller_button::dpad_left, // SDL_CONTROLLER_BUTTON_DPAD_LEFT, + game_controller_button::dpad_right, // SDL_CONTROLLER_BUTTON_DPAD_RIGHT, +}; + +const game_controller_axis sdl_axis_table[6] = +{ + game_controller_axis::left_x, // SDL_CONTROLLER_AXIS_LEFTX, + game_controller_axis::left_y, // SDL_CONTROLLER_AXIS_LEFTY, + game_controller_axis::right_x, // SDL_CONTROLLER_AXIS_RIGHTX, + game_controller_axis::right_y, // SDL_CONTROLLER_AXIS_RIGHTY, + game_controller_axis::trigger_left, // SDL_CONTROLLER_AXIS_TRIGGERLEFT, + game_controller_axis::trigger_right, // SDL_CONTROLLER_AXIS_TRIGGERRIGHT, +}; + diff --git a/src/antkeeper/input/sdl-game-controller-tables.hpp b/src/antkeeper/input/sdl-game-controller-tables.hpp new file mode 100644 index 0000000..bbadd82 --- /dev/null +++ b/src/antkeeper/input/sdl-game-controller-tables.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 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_SDL_GAME_CONTROLLER_TABLES_HPP +#define ANTKEEPER_SDL_GAME_CONTROLLER_TABLES_HPP + +enum class game_controller_button; +enum class game_controller_axis; + +extern const game_controller_button sdl_button_table[15]; +extern const game_controller_axis sdl_axis_table[6]; + +#endif // ANTKEEPER_SDL_GAME_CONTROLLER_TABLES_HPP + diff --git a/src/antkeeper/input/sdl-scancode-table.cpp b/src/antkeeper/input/sdl-scancode-table.cpp new file mode 100644 index 0000000..46dd862 --- /dev/null +++ b/src/antkeeper/input/sdl-scancode-table.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2020 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 "sdl-scancode-table.hpp" +#include "scancode.hpp" + +const scancode sdl_scancode_table[287] = +{ + scancode::unknown, // SDL_SCANCODE_UNKNOWN = 0, + scancode::unknown, // Unassigned = 1 + scancode::unknown, // Unassigned = 2 + scancode::unknown, // Unassigned = 3 + scancode::a, // SDL_SCANCODE_A = 4, + scancode::b, // SDL_SCANCODE_B = 5, + scancode::c, // SDL_SCANCODE_C = 6, + scancode::d, // SDL_SCANCODE_D = 7, + scancode::e, // SDL_SCANCODE_E = 8, + scancode::f, // SDL_SCANCODE_F = 9, + scancode::g, // SDL_SCANCODE_G = 10, + scancode::h, // SDL_SCANCODE_H = 11, + scancode::i, // SDL_SCANCODE_I = 12, + scancode::j, // SDL_SCANCODE_J = 13, + scancode::k, // SDL_SCANCODE_K = 14, + scancode::l, // SDL_SCANCODE_L = 15, + scancode::m, // SDL_SCANCODE_M = 16, + scancode::n, // SDL_SCANCODE_N = 17, + scancode::o, // SDL_SCANCODE_O = 18, + scancode::p, // SDL_SCANCODE_P = 19, + scancode::q, // SDL_SCANCODE_Q = 20, + scancode::r, // SDL_SCANCODE_R = 21, + scancode::s, // SDL_SCANCODE_S = 22, + scancode::t, // SDL_SCANCODE_T = 23, + scancode::u, // SDL_SCANCODE_U = 24, + scancode::v, // SDL_SCANCODE_V = 25, + scancode::w, // SDL_SCANCODE_W = 26, + scancode::x, // SDL_SCANCODE_X = 27, + scancode::y, // SDL_SCANCODE_Y = 28, + scancode::z, // SDL_SCANCODE_Z = 29, + scancode::one, // SDL_SCANCODE_1 = 30, + scancode::two, // SDL_SCANCODE_2 = 31, + scancode::three, // SDL_SCANCODE_3 = 32, + scancode::four, // SDL_SCANCODE_4 = 33, + scancode::five, // SDL_SCANCODE_5 = 34, + scancode::six, // SDL_SCANCODE_6 = 35, + scancode::seven, // SDL_SCANCODE_7 = 36, + scancode::eight, // SDL_SCANCODE_8 = 37, + scancode::nine, // SDL_SCANCODE_9 = 38, + scancode::zero, // SDL_SCANCODE_0 = 39, + scancode::enter, // SDL_SCANCODE_RETURN = 40, + scancode::escape, // SDL_SCANCODE_ESCAPE = 41, + scancode::backspace, // SDL_SCANCODE_BACKSPACE = 42, + scancode::tab, // SDL_SCANCODE_TAB = 43, + scancode::space, // SDL_SCANCODE_SPACE = 44, + scancode::minus, // SDL_SCANCODE_MINUS = 45, + scancode::equal, // SDL_SCANCODE_EQUALS = 46, + scancode::left_brace, // SDL_SCANCODE_LEFTBRACKET = 47, + scancode::right_brace, // SDL_SCANCODE_RIGHTBRACKET = 48, + scancode::backslash, // SDL_SCANCODE_BACKSLASH = 49, + scancode::non_us_hash, // SDL_SCANCODE_NONUSHASH = 50, + scancode::semicolon, // SDL_SCANCODE_SEMICOLON = 51, + scancode::apostrophe, // SDL_SCANCODE_APOSTROPHE = 52, + scancode::grave, // SDL_SCANCODE_GRAVE = 53, + scancode::comma, // SDL_SCANCODE_COMMA = 54, + scancode::dot, // SDL_SCANCODE_PERIOD = 55, + scancode::slash, // SDL_SCANCODE_SLASH = 56, + scancode::caps_lock, // SDL_SCANCODE_CAPSLOCK = 57, + scancode::f1, // SDL_SCANCODE_F1 = 58, + scancode::f2, // SDL_SCANCODE_F2 = 59, + scancode::f3, // SDL_SCANCODE_F3 = 60, + scancode::f4, // SDL_SCANCODE_F4 = 61, + scancode::f5, // SDL_SCANCODE_F5 = 62, + scancode::f6, // SDL_SCANCODE_F6 = 63, + scancode::f7, // SDL_SCANCODE_F7 = 64, + scancode::f8, // SDL_SCANCODE_F8 = 65, + scancode::f9, // SDL_SCANCODE_F9 = 66, + scancode::f10, // SDL_SCANCODE_F10 = 67, + scancode::f11, // SDL_SCANCODE_F11 = 68, + scancode::f12, // SDL_SCANCODE_F12 = 69, + scancode::print_screen, // SDL_SCANCODE_PRINTSCREEN = 70, + scancode::scroll_lock, // SDL_SCANCODE_SCROLLLOCK = 71, + scancode::pause, // SDL_SCANCODE_PAUSE = 72, + scancode::insert, // SDL_SCANCODE_INSERT = 73, + scancode::home, // SDL_SCANCODE_HOME = 74, + scancode::page_up, // SDL_SCANCODE_PAGEUP = 75, + scancode::del, // SDL_SCANCODE_DELETE = 76, + scancode::end, // SDL_SCANCODE_END = 77, + scancode::page_down, // SDL_SCANCODE_PAGEDOWN = 78, + scancode::right, // SDL_SCANCODE_RIGHT = 79, + scancode::left, // SDL_SCANCODE_LEFT = 80, + scancode::down, // SDL_SCANCODE_DOWN = 81, + scancode::up, // SDL_SCANCODE_UP = 82, + scancode::num_lock, // SDL_SCANCODE_NUMLOCKCLEAR = 83, + scancode::kp_slash, // SDL_SCANCODE_KP_DIVIDE = 84, + scancode::kp_asterisk, // SDL_SCANCODE_KP_MULTIPLY = 85, + scancode::kp_minus, // SDL_SCANCODE_KP_MINUS = 86, + scancode::kp_plus, // SDL_SCANCODE_KP_PLUS = 87, + scancode::kp_enter, // SDL_SCANCODE_KP_ENTER = 88, + scancode::kp_1, // SDL_SCANCODE_KP_1 = 89, + scancode::kp_2, // SDL_SCANCODE_KP_2 = 90, + scancode::kp_3, // SDL_SCANCODE_KP_3 = 91, + scancode::kp_4, // SDL_SCANCODE_KP_4 = 92, + scancode::kp_5, // SDL_SCANCODE_KP_5 = 93, + scancode::kp_6, // SDL_SCANCODE_KP_6 = 94, + scancode::kp_7, // SDL_SCANCODE_KP_7 = 95, + scancode::kp_8, // SDL_SCANCODE_KP_8 = 96, + scancode::kp_9, // SDL_SCANCODE_KP_9 = 97, + scancode::kp_0, // SDL_SCANCODE_KP_0 = 98, + scancode::kp_dot, // SDL_SCANCODE_KP_PERIOD = 99, + scancode::non_us_backslash, // SDL_SCANCODE_NONUSBACKSLASH = 100, + scancode::application, // SDL_SCANCODE_APPLICATION = 101, + scancode::power, // SDL_SCANCODE_POWER = 102, + scancode::kp_equal, // SDL_SCANCODE_KP_EQUALS = 103, + scancode::f13, // SDL_SCANCODE_F13 = 104, + scancode::f14, // SDL_SCANCODE_F14 = 105, + scancode::f15, // SDL_SCANCODE_F15 = 106, + scancode::f16, // SDL_SCANCODE_F16 = 107, + scancode::f17, // SDL_SCANCODE_F17 = 108, + scancode::f18, // SDL_SCANCODE_F18 = 109, + scancode::f19, // SDL_SCANCODE_F19 = 110, + scancode::f20, // SDL_SCANCODE_F20 = 111, + scancode::f21, // SDL_SCANCODE_F21 = 112, + scancode::f22, // SDL_SCANCODE_F22 = 113, + scancode::f23, // SDL_SCANCODE_F23 = 114, + scancode::f24, // SDL_SCANCODE_F24 = 115, + scancode::execute, // SDL_SCANCODE_EXECUTE = 116, + scancode::help, // SDL_SCANCODE_HELP = 117, + scancode::menu, // SDL_SCANCODE_MENU = 118, + scancode::select, // SDL_SCANCODE_SELECT = 119, + scancode::stop, // SDL_SCANCODE_STOP = 120, + scancode::again, // SDL_SCANCODE_AGAIN = 121, + scancode::undo, // SDL_SCANCODE_UNDO = 122, + scancode::cut, // SDL_SCANCODE_CUT = 123, + scancode::copy, // SDL_SCANCODE_COPY = 124, + scancode::paste, // SDL_SCANCODE_PASTE = 125, + scancode::find, // SDL_SCANCODE_FIND = 126, + scancode::mute, // SDL_SCANCODE_MUTE = 127, + scancode::volume_up, // SDL_SCANCODE_VOLUMEUP = 128, + scancode::volume_down, // SDL_SCANCODE_VOLUMEDOWN = 129, + scancode::locking_caps_lock, // Unassigned = 130 + scancode::locking_num_lock, // Unassigned = 131 + scancode::locking_scroll_lock, // Unassigned = 132 + scancode::kp_comma, // SDL_SCANCODE_KP_COMMA = 133, + scancode::kp_equal_as400, // SDL_SCANCODE_KP_EQUALSAS400 = 134, + scancode::international_1, // SDL_SCANCODE_INTERNATIONAL1 = 135, + scancode::international_2, // SDL_SCANCODE_INTERNATIONAL2 = 136, + scancode::international_3, // SDL_SCANCODE_INTERNATIONAL3 = 137, + scancode::international_4, // SDL_SCANCODE_INTERNATIONAL4 = 138, + scancode::international_5, // SDL_SCANCODE_INTERNATIONAL5 = 139, + scancode::international_6, // SDL_SCANCODE_INTERNATIONAL6 = 140, + scancode::international_7, // SDL_SCANCODE_INTERNATIONAL7 = 141, + scancode::international_8, // SDL_SCANCODE_INTERNATIONAL8 = 142, + scancode::international_9, // SDL_SCANCODE_INTERNATIONAL9 = 143, + scancode::lang_1, // SDL_SCANCODE_LANG1 = 144, + scancode::lang_2, // SDL_SCANCODE_LANG2 = 145, + scancode::lang_3, // SDL_SCANCODE_LANG3 = 146, + scancode::lang_4, // SDL_SCANCODE_LANG4 = 147, + scancode::lang_5, // SDL_SCANCODE_LANG5 = 148, + scancode::lang_6, // SDL_SCANCODE_LANG6 = 149, + scancode::lang_7, // SDL_SCANCODE_LANG7 = 150, + scancode::lang_8, // SDL_SCANCODE_LANG8 = 151, + scancode::lang_9, // SDL_SCANCODE_LANG9 = 152, + scancode::alt_erase, // SDL_SCANCODE_ALTERASE = 153, + scancode::sys_req, // SDL_SCANCODE_SYSREQ = 154, + scancode::cancel, // SDL_SCANCODE_CANCEL = 155, + scancode::clear, // SDL_SCANCODE_CLEAR = 156, + scancode::prior, // SDL_SCANCODE_PRIOR = 157, + scancode::return_2, // SDL_SCANCODE_RETURN2 = 158, + scancode::separator, // SDL_SCANCODE_SEPARATOR = 159, + scancode::_out, // SDL_SCANCODE_OUT = 160, + scancode::oper, // SDL_SCANCODE_OPER = 161, + scancode::clear_again, // SDL_SCANCODE_CLEARAGAIN = 162, + scancode::cr_sel, // SDL_SCANCODE_CRSEL = 163, + scancode::ex_sel, // SDL_SCANCODE_EXSEL = 164, + scancode::unknown, // Unassigned = 165 + scancode::unknown, // Unassigned = 166 + scancode::unknown, // Unassigned = 167 + scancode::unknown, // Unassigned = 168 + scancode::unknown, // Unassigned = 169 + scancode::unknown, // Unassigned = 170 + scancode::unknown, // Unassigned = 171 + scancode::unknown, // Unassigned = 172 + scancode::unknown, // Unassigned = 173 + scancode::unknown, // Unassigned = 174 + scancode::unknown, // Unassigned = 175 + scancode::kp_00, // SDL_SCANCODE_KP_00 = 176, + scancode::kp_000, // SDL_SCANCODE_KP_000 = 177, + scancode::thousands_separator, // SDL_SCANCODE_THOUSANDSSEPARATOR = 178, + scancode::decimal_separator, // SDL_SCANCODE_DECIMALSEPARATOR = 179, + scancode::currency_unit, // SDL_SCANCODE_CURRENCYUNIT = 180, + scancode::currency_sub_unit, // SDL_SCANCODE_CURRENCYSUBUNIT = 181, + scancode::kp_left_paren, // SDL_SCANCODE_KP_LEFTPAREN = 182, + scancode::kp_right_paren, // SDL_SCANCODE_KP_RIGHTPAREN = 183, + scancode::kp_left_brace, // SDL_SCANCODE_KP_LEFTBRACE = 184, + scancode::kp_right_brace, // SDL_SCANCODE_KP_RIGHTBRACE = 185, + scancode::kp_tab, // SDL_SCANCODE_KP_TAB = 186, + scancode::kp_backspace, // SDL_SCANCODE_KP_BACKSPACE = 187, + scancode::kp_a, // SDL_SCANCODE_KP_A = 188, + scancode::kp_b, // SDL_SCANCODE_KP_B = 189, + scancode::kp_c, // SDL_SCANCODE_KP_C = 190, + scancode::kp_d, // SDL_SCANCODE_KP_D = 191, + scancode::kp_e, // SDL_SCANCODE_KP_E = 192, + scancode::kp_f, // SDL_SCANCODE_KP_F = 193, + scancode::kp_xor, // SDL_SCANCODE_KP_XOR = 194, + scancode::kp_power, // SDL_SCANCODE_KP_POWER = 195, + scancode::kp_percent, // SDL_SCANCODE_KP_PERCENT = 196, + scancode::kp_less, // SDL_SCANCODE_KP_LESS = 197, + scancode::kp_greater, // SDL_SCANCODE_KP_GREATER = 198, + scancode::kp_ampersand, // SDL_SCANCODE_KP_AMPERSAND = 199, + scancode::kp_double_ampersand, // SDL_SCANCODE_KP_DBLAMPERSAND = 200, + scancode::kp_vertical_bar, // SDL_SCANCODE_KP_VERTICALBAR = 201, + scancode::kp_double_vertical_bar, // SDL_SCANCODE_KP_DBLVERTICALBAR = 202, + scancode::kp_colon, // SDL_SCANCODE_KP_COLON = 203, + scancode::kp_hash, // SDL_SCANCODE_KP_HASH = 204, + scancode::kp_space, // SDL_SCANCODE_KP_SPACE = 205, + scancode::kp_at, // SDL_SCANCODE_KP_AT = 206, + scancode::kp_exclam, // SDL_SCANCODE_KP_EXCLAM = 207, + scancode::kp_mem_store, // SDL_SCANCODE_KP_MEMSTORE = 208, + scancode::kp_mem_recall, // SDL_SCANCODE_KP_MEMRECALL = 209, + scancode::kp_mem_clear, // SDL_SCANCODE_KP_MEMCLEAR = 210, + scancode::kp_mem_add, // SDL_SCANCODE_KP_MEMADD = 211, + scancode::kp_mem_subtract, // SDL_SCANCODE_KP_MEMSUBTRACT = 212, + scancode::kp_mem_multiply, // SDL_SCANCODE_KP_MEMMULTIPLY = 213, + scancode::kp_mem_divide, // SDL_SCANCODE_KP_MEMDIVIDE = 214, + scancode::kp_plus_minus, // SDL_SCANCODE_KP_PLUSMINUS = 215, + scancode::kp_clear, // SDL_SCANCODE_KP_CLEAR = 216, + scancode::kp_clear_entry, // SDL_SCANCODE_KP_CLEARENTRY = 217, + scancode::kp_binary, // SDL_SCANCODE_KP_BINARY = 218, + scancode::kp_octal, // SDL_SCANCODE_KP_OCTAL = 219, + scancode::kp_decimal, // SDL_SCANCODE_KP_DECIMAL = 220, + scancode::kp_hexadecimal, // SDL_SCANCODE_KP_HEXADECIMAL = 221, + scancode::unknown, // Unassigned = 222 + scancode::unknown, // Unassigned = 223 + scancode::left_ctrl, // SDL_SCANCODE_LCTRL = 224, + scancode::left_shift, // SDL_SCANCODE_LSHIFT = 225, + scancode::left_alt, // SDL_SCANCODE_LALT = 226, + scancode::left_gui, // SDL_SCANCODE_LGUI = 227, + scancode::right_ctrl, // SDL_SCANCODE_RCTRL = 228, + scancode::right_shift, // SDL_SCANCODE_RSHIFT = 229, + scancode::right_alt, // SDL_SCANCODE_RALT = 230, + scancode::right_gui, // SDL_SCANCODE_RGUI = 231, + scancode::unknown, // Unassigned = 232 + scancode::unknown, // Unassigned = 233 + scancode::unknown, // Unassigned = 234 + scancode::unknown, // Unassigned = 235 + scancode::unknown, // Unassigned = 236 + scancode::unknown, // Unassigned = 237 + scancode::unknown, // Unassigned = 238 + scancode::unknown, // Unassigned = 239 + scancode::unknown, // Unassigned = 240 + scancode::unknown, // Unassigned = 241 + scancode::unknown, // Unassigned = 242 + scancode::unknown, // Unassigned = 243 + scancode::unknown, // Unassigned = 244 + scancode::unknown, // Unassigned = 245 + scancode::unknown, // Unassigned = 246 + scancode::unknown, // Unassigned = 247 + scancode::unknown, // Unassigned = 248 + scancode::unknown, // Unassigned = 249 + scancode::unknown, // Unassigned = 250 + scancode::unknown, // Unassigned = 251 + scancode::unknown, // Unassigned = 252 + scancode::unknown, // Unassigned = 253 + scancode::unknown, // Unassigned = 254 + scancode::unknown, // Unassigned = 255 + scancode::unknown, // Unassigned = 256 + scancode::mode, // SDL_SCANCODE_MODE = 257, + scancode::audio_next, // SDL_SCANCODE_AUDIONEXT = 258, + scancode::audio_prev, // SDL_SCANCODE_AUDIOPREV = 259, + scancode::audio_stop, // SDL_SCANCODE_AUDIOSTOP = 260, + scancode::audio_play, // SDL_SCANCODE_AUDIOPLAY = 261, + scancode::audio_mute, // SDL_SCANCODE_AUDIOMUTE = 262, + scancode::media_select, // SDL_SCANCODE_MEDIASELECT = 263, + scancode::www, // SDL_SCANCODE_WWW = 264, + scancode::mail, // SDL_SCANCODE_MAIL = 265, + scancode::calculator, // SDL_SCANCODE_CALCULATOR = 266, + scancode::computer, // SDL_SCANCODE_COMPUTER = 267, + scancode::ac_search, // SDL_SCANCODE_AC_SEARCH = 268, + scancode::ac_home, // SDL_SCANCODE_AC_HOME = 269, + scancode::ac_back, // SDL_SCANCODE_AC_BACK = 270, + scancode::ac_forward, // SDL_SCANCODE_AC_FORWARD = 271, + scancode::ac_stop, // SDL_SCANCODE_AC_STOP = 272, + scancode::ac_refresh, // SDL_SCANCODE_AC_REFRESH = 273, + scancode::ac_bookmarks, // SDL_SCANCODE_AC_BOOKMARKS = 274, + scancode::brightness_down, // SDL_SCANCODE_BRIGHTNESSDOWN = 275, + scancode::brightness_up, // SDL_SCANCODE_BRIGHTNESSUP = 276, + scancode::display_switch, // SDL_SCANCODE_DISPLAYSWITCH = 277, + scancode::kbd_illum_toggle, // SDL_SCANCODE_KBDILLUMTOGGLE = 278, + scancode::kbd_illum_down, // SDL_SCANCODE_KBDILLUMDOWN = 279, + scancode::kbd_illum_up, // SDL_SCANCODE_KBDILLUMUP = 280, + scancode::eject, // SDL_SCANCODE_EJECT = 281, + scancode::sleep, // SDL_SCANCODE_SLEEP = 282, + scancode::app_1, // SDL_SCANCODE_APP1 = 283, + scancode::app_2, // SDL_SCANCODE_APP2 = 284, + scancode::audio_rewind, // SDL_SCANCODE_AUDIOREWIND = 285, + scancode::audio_fast_forward, // SDL_SCANCODE_AUDIOFASTFORWARD = 286, +}; + diff --git a/src/antkeeper/input/sdl-scancode-table.hpp b/src/antkeeper/input/sdl-scancode-table.hpp new file mode 100644 index 0000000..c50dcfc --- /dev/null +++ b/src/antkeeper/input/sdl-scancode-table.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 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_SDL_SCANCODE_TABLE_HPP +#define ANTKEEPER_SDL_SCANCODE_TABLE_HPP + +enum class scancode; + +extern const scancode sdl_scancode_table[287]; + +#endif // ANTKEEPER_SDL_SCANCODE_TABLE_HPP + diff --git a/src/main.cpp b/src/antkeeper/main.cpp similarity index 63% rename from src/main.cpp rename to src/antkeeper/main.cpp index 2968b88..be3482e 100644 --- a/src/main.cpp +++ b/src/antkeeper/main.cpp @@ -1,35 +1,37 @@ -/* - * Copyright (C) 2017-2019 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.hpp" - -int main(int argc, char* argv[]) -{ - try - { - return Game(argc, argv).execute(); - } - catch (const std::exception& e) - { - std::cerr << "Exception caught: \"" << e.what() << "\"" << std::endl; - } - - return EXIT_FAILURE; -} - +/* + * Copyright (C) 2020 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 "application.hpp" +#include +#include + +int main(int argc, char* argv[]) +{ + try + { + return application(argc, argv).execute(); + } + catch (const std::exception& e) + { + std::cerr << "Exception caught: \"" << e.what() << "\"" << std::endl; + } + + return EXIT_FAILURE; +} + diff --git a/src/antkeeper/marching-cubes.cpp b/src/antkeeper/marching-cubes.cpp new file mode 100644 index 0000000..95d0599 --- /dev/null +++ b/src/antkeeper/marching-cubes.cpp @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2020 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 "marching-cubes.hpp" +#include + +namespace mc { + +static constexpr std::uint_fast8_t vertex_table[12][2] = +{ + {0, 1}, + {1, 2}, + {2, 3}, + {3, 0}, + {4, 5}, + {5, 6}, + {6, 7}, + {7, 4}, + {0, 4}, + {1, 5}, + {2, 6}, + {3, 7} +}; + +static constexpr std::uint_fast16_t edge_table[256] = +{ + 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, + 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x099, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, + 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x033, 0x13a, 0x636, 0x73f, 0x435, 0x53c, + 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0x0aa, 0x7a6, 0x6af, 0x5a5, 0x4ac, + 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x066, 0x16f, 0x265, 0x36c, + 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0x0ff, 0x3f5, 0x2fc, + 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x055, 0x15c, + 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0x0cc, + 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, + 0x0cc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, + 0x15c, 0x055, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, + 0x2fc, 0x3f5, 0x0ff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, + 0x36c, 0x265, 0x16f, 0x066, 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, + 0x4ac, 0x5a5, 0x6af, 0x7a6, 0x0aa, 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, + 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x033, 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, + 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x099, 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, + 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 +}; + +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}, + {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-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) +{ + *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) + 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]; + if (!edge_flags) + return; + + // Get vertex indices of the case + const std::int_fast8_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) + { + // 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]; + const float* v_a = corners + a * 3; + const float* v_b = corners + b * 3; + float f_a = distances[a]; + float f_b = distances[b]; + + // Determine interpolation ratio + static const float epsilon = 0.00001f; + float t; + if (std::fabs(f_a) < epsilon) + t = 1.0f; + else if (std::fabs(f_b) < epsilon) + t = 0.0f; + else if (std::fabs(f_b - f_a) < epsilon) + t = 0.5f; // Paul Bourke suggested this be 1.0f? Why? + else + t = (-f_a) / (f_b - f_a); + + // Interpolate between vertices + float* v = vertex_buffer + i * 3; + v[0] = v_a[0] + t * (v_b[0] - v_a[0]); + v[1] = v_a[1] + t * (v_b[1] - v_a[1]); + v[2] = v_a[2] + t * (v_b[2] - v_a[2]); + } + } + + // Remap vertex buffer to be stored contiguously + std::int_fast8_t vertex_remap[12]; + for (std::uint_fast8_t i = 0; i < 12; ++i) + vertex_remap[i] = -1; + for (std::uint_fast8_t i = 0; indices[i] != -1; ++i) + { + if (vertex_remap[indices[i]] == -1) + { + std::int_fast8_t index = indices[i] * 3; + *(vertices++) = vertex_buffer[ index]; + *(vertices++) = vertex_buffer[++index]; + *(vertices++) = vertex_buffer[++index]; + vertex_remap[indices[i]] = (*vertex_count)++; + } + } + + // Form triangles + for (std::uint_fast8_t i = 0; indices[i] != -1;) + { + *(triangles++) = vertex_remap[indices[i++]]; + *(triangles++) = vertex_remap[indices[i++]]; + *(triangles++) = vertex_remap[indices[i++]]; + ++(*triangle_count); + } +} + +} // namespace mc + diff --git a/src/antkeeper/marching-cubes.hpp b/src/antkeeper/marching-cubes.hpp new file mode 100644 index 0000000..3b00421 --- /dev/null +++ b/src/antkeeper/marching-cubes.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 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_MARCHING_CUBES_HPP +#define ANTKEEPER_MARCHING_CUBES_HPP + +#include + +/** + * Contains functions and constants related to the Marching Cubes (MC) algorithm. + */ +namespace mc { + +/** + * Uses the marching cubes algorithm to polygonize a single cell. + * + * @param[out] vertices Array which can hold at least 12 vertices (36 floats). + * @param[out] vertex_count Number of generated vertices. + * @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); + +/** + * Vertices of a unit cube. + */ +constexpr float unit_cube[8][3] = +{ + {0, 0, 0}, + {1, 0, 0}, + {1, 1, 0}, + {0, 1, 0}, + {0, 0, 1}, + {1, 0, 1}, + {1, 1, 1}, + {0, 1, 1} +}; + +} // namespace mc + +#endif // ANTKEEPER_MARCHING_CUBES_HPP + diff --git a/src/antkeeper/math.hpp b/src/antkeeper/math.hpp new file mode 100644 index 0000000..ab4559d --- /dev/null +++ b/src/antkeeper/math.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 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_MATH_HPP +#define ANTKEEPER_MATH_HPP + +#include +#include +#include + +using namespace vmq::operators; + +inline float frand(float start, float end) +{ + float f = (float)std::rand() / RAND_MAX; + return start + f * (end - start); +} + +namespace math { + +/** + * Reinterprets data as an `N`-dimensional vector of type `T`. + * + * @tparam N Number of vector dimensions. + * @tparam T Scalar type. + * @param data Data to reinterpret. + */ +template +inline vmq::vector& as_vector(T& data) +{ + static_assert(std::is_pod>::value); + return reinterpret_cast&>(data); +} + +/** + * Reinterprets data as an `NxM` matrix of type `T`. + * + * @tparam N Number of columns. + * @tparam M Number of rows. + * @tparam T Element type. + * @param data Data to reinterpret. + */ +template +inline vmq::matrix& as_matrix(T& data) +{ + static_assert(std::is_pod>::value); + return reinterpret_cast&>(data); +} + +template +vmq::vector project_on_plane(const vmq::vector& v, const vmq::vector& p, const vmq::vector& n) +{ + return v - n * vmq::dot(v - p, n); +} + +} // namespace math + +#endif // ANTKEEPER_MATH_HPP + diff --git a/src/antkeeper/morton.cpp b/src/antkeeper/morton.cpp new file mode 100644 index 0000000..73a1b13 --- /dev/null +++ b/src/antkeeper/morton.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 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 "morton.hpp" + +namespace morton { + +std::uint32_t encode_2d(std::uint32_t x, std::uint32_t y) +{ + auto interleave = [](std::uint32_t x) -> std::uint32_t + { + x &= 0x0000ffff; + x = (x ^ (x << 8)) & 0x00ff00ff; + x = (x ^ (x << 4)) & 0x0f0f0f0f; + x = (x ^ (x << 2)) & 0x33333333; + x = (x ^ (x << 1)) & 0x55555555; + return x; + }; + + return (interleave(y) << 1) + interleave(x); +} + +std::uint32_t encode_3d(std::uint32_t x, std::uint32_t y, std::uint32_t z) +{ + auto interleave = [](std::uint32_t x) -> std::uint32_t + { + x &= 0x000003ff; + x = (x ^ (x << 16)) & 0xff0000ff; + x = (x ^ (x << 8)) & 0x0300f00f; + x = (x ^ (x << 4)) & 0x030c30c3; + x = (x ^ (x << 2)) & 0x09249249; + return x; + }; + + return interleave(x) | (interleave(y) << 1) | (interleave(z) << 2); +} + +std::array decode_2d(std::uint32_t code) +{ + auto unravel = [](std::uint32_t x) -> std::uint32_t + { + x &= 0x55555555; + x = (x ^ (x >> 1)) & 0x33333333; + x = (x ^ (x >> 2)) & 0x0f0f0f0f; + x = (x ^ (x >> 4)) & 0x00ff00ff; + x = (x ^ (x >> 8)) & 0x0000ffff; + return x; + }; + + return {unravel(code >> 0), unravel(code >> 1)}; +} + +std::array decode_3d(std::uint32_t code) +{ + auto unravel = [](std::uint32_t x) -> std::uint32_t + { + x &= 0x09249249; + x = (x ^ (x >> 2)) & 0x030c30c3; + x = (x ^ (x >> 4)) & 0x0300f00f; + x = (x ^ (x >> 8)) & 0xff0000ff; + x = (x ^ (x >> 16)) & 0x000003ff; + return x; + }; + + return {unravel(code >> 0), unravel(code >> 1), unravel(code >> 2)}; +} + +} // namespace morton + diff --git a/src/antkeeper/morton.hpp b/src/antkeeper/morton.hpp new file mode 100644 index 0000000..de55acf --- /dev/null +++ b/src/antkeeper/morton.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 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_MORTON_HPP +#define ANTKEEPER_MORTON_HPP + +#include +#include + +/** + * Contains functions for encoding and decoding Morton location codes. + */ +namespace morton { + +/// Encodes 2D coordinates as a 32-bit Morton location code. +std::uint32_t encode_2d(std::uint32_t x, std::uint32_t y); + +/// Encodes 3D coordinates as a 32-bit Morton location code. +std::uint32_t encode_3d(std::uint32_t x, std::uint32_t y, std::uint32_t z); + +/// Decodes X and Y coordinates from a 32-bit Morton location code. +std::array decode_2d(std::uint32_t code); + +/// Decodes X, Y, and Z coordinates from a 32-bit Morton location code. +std::array decode_3d(std::uint32_t code); + +} // namespace morton + +#endif // ANTKEEPER_MORTON_HPP + diff --git a/src/antkeeper/nest.cpp b/src/antkeeper/nest.cpp new file mode 100644 index 0000000..90dd59f --- /dev/null +++ b/src/antkeeper/nest.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2020 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 "nest.hpp" +#include "animation/easings.hpp" +#include "marching-cubes.hpp" +#include "geometry/mesh-functions.hpp" +#include "sdf.hpp" +#include +#include +#include +#include + +#include "math.hpp" +#include +#include + +nest::nest() +{ + dig_radius = 1.25f; +} + +float3 nest::extend_shaft(shaft& shaft) +{ + float3 dig_position = get_shaft_position(shaft, shaft.current_depth); + + float dr = frand(dig_radius * 0.75f, dig_radius * 1.25f); + + shaft.current_depth += dr * 0.5f; + + return dig_position; +} + +float3 nest::expand_chamber(chamber& chamber) +{ + float dig_angle = frand(0.0f, vmq::two_pi); + float2 dig_direction = vmq::normalize(float2{std::cos(dig_angle), std::sin(dig_angle)}); + + float3 chamber_center = get_shaft_position(*chamber.shaft, chamber.depth); + float3 dig_position = chamber_center; + + float dr = frand(dig_radius * 0.75f, dig_radius * 1.25f); + + float t = frand(0.0f, 1.0f); + dig_position.x += dig_direction.x * (chamber.outer_radius - dr) * t; + dig_position.z += dig_direction.y * (chamber.outer_radius - dr) * t; + + return dig_position; +} + +void nest::set_tunnel_radius(float radius) +{ + tunnel_radius = radius; +} + +float nest::get_shaft_angle(const shaft& shaft, float depth) const +{ + float shaft_length = shaft.depth[1] - shaft.depth[0]; + float depth_factor = (depth - shaft.depth[0]) / shaft_length; + float pitch = ease_linear(shaft.pitch[0], shaft.pitch[1], depth_factor); + return shaft.rotation + (depth / pitch) * shaft.chirality; +} + +float3 nest::get_shaft_position(const shaft& shaft, float depth) const +{ + float shaft_length = shaft.depth[1] - shaft.depth[0]; + float depth_factor = (depth - shaft.depth[0]) / shaft_length; + + float pitch = ease_linear(shaft.pitch[0], shaft.pitch[1], depth_factor); + float radius = ease_out_expo(shaft.radius[0], shaft.radius[1], depth_factor); + float translation_x = ease_linear(shaft.translation[0][0], shaft.translation[1][0], depth_factor); + float translation_z = ease_linear(shaft.translation[0][1], shaft.translation[1][1], depth_factor); + float angle = shaft.rotation + (depth / pitch) * shaft.chirality; + + float3 position; + position[0] = std::cos(angle) * radius + translation_x; + position[1] = -std::max(shaft.depth[0], std::min(shaft.depth[1], depth)); + position[2] = std::sin(angle) * radius + translation_z; + + return position; + +} + diff --git a/src/antkeeper/nest.hpp b/src/antkeeper/nest.hpp new file mode 100644 index 0000000..57270e2 --- /dev/null +++ b/src/antkeeper/nest.hpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 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_NEST_HPP +#define ANTKEEPER_NEST_HPP + +#include "geometry/mesh.hpp" +#include +#include +#include + +using namespace vmq::types; +using namespace vmq::operators; + +class nest +{ +public: + struct chamber; + + struct shaft + { + std::array depth; ///< start and end shaft depth + float chirality; ///< 1 = right-handed, -1 = left-handed + float rotation; ///< starting helix angle, in radians + std::array radius; ///< start and end helix radius + std::array pitch; ///< start and end helix pitch + std::array translation; ///< start and end helix translation + std::vector chambers; + float current_depth; + }; + + struct chamber + { + shaft* shaft; ///< parent shaft + float depth; ///< chamber depth, relative to parent shaft + float rotation; ///< chamber rotation, relative to helix angle + float sector_angle; ///< + float inner_radius; + float outer_radius; + }; + + /** + * Creates a nest. + */ + nest(); + + float3 extend_shaft(shaft& shaft); + float3 expand_chamber(chamber& chamber); + + void regenerate(); + + void set_tunnel_radius(float radius); + + shaft* get_central_shaft(); + + /** + * Calculates the position on a shaft at the specified depth. + */ + float3 get_shaft_position(const shaft& shaft, float depth) const; + + float get_shaft_angle(const shaft& shaft, float depth) const; + +private: + float tunnel_radius; + shaft central_shaft; + float dig_radius; +}; + +inline nest::shaft* nest::get_central_shaft() +{ + return ¢ral_shaft; +} + +#endif // ANTKEEPER_NEST_HPP + diff --git a/src/antkeeper/octree.hpp b/src/antkeeper/octree.hpp new file mode 100644 index 0000000..9b15d52 --- /dev/null +++ b/src/antkeeper/octree.hpp @@ -0,0 +1,455 @@ +/* + * Copyright (C) 2020 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_OCTREE_HPP +#define ANTKEEPER_OCTREE_HPP + +#include +#include +#include +#include +#include + +/** + * A general purpose (hashed) linear octree. Nodes are integer identifiers and no other data is stored in the octree. + * + * @tparam T Integer node type. Must be 16-bit, 32-bit, or 64-bit. + * + * @see http://codervil.blogspot.com/2015/10/octree-node-identifiers.html + * @see https://geidav.wordpress.com/2014/08/18/advanced-octrees-2-node-representations/ + */ +template +class octree +{ +private: + /// Compile-time calculation of the minimum bits required to represent `n` state changes. + static constexpr T ceil_log2(T n); + +public: + static_assert(std::is_integral::value, "Node type must be integral."); + static_assert(sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Node type must be 16-bit, 32-bit, or 64-bit."); + + /// Maximum octree depth + static constexpr T max_depth = (sizeof(T) == 2) ? 3 : (sizeof(T) == 4) ? 8 : 18; + + /// Number of bits in the node type + static constexpr T node_bits = sizeof(T) * 8; + + /// Number of bits used to encode the depth of a node. + static constexpr T depth_bits = ceil_log2(max_depth + 1); + + /// Number of bits used to encode the Morton code location a node. + static constexpr T location_bits = (max_depth + 1) * 3; + + /// Integer node type. + typedef T node_type; + + /// Root node which is always guaranteed to exist. + static constexpr node_type root = 0; + + /** + * Accesses nodes in their internal hashmap order. + */ + struct unordered_iterator + { + inline unordered_iterator(const unordered_iterator& other): set_iterator(other.set_iterator) {}; + inline unordered_iterator& operator=(const unordered_iterator& other) { this->set_iterator = other.set_iterator; return *this; }; + inline unordered_iterator& operator++() { ++(this->set_iterator); return *this; }; + inline unordered_iterator& operator--() { --(this->set_iterator); return *this; }; + inline bool operator==(const unordered_iterator& other) const { return this->set_iterator == other.set_iterator; }; + inline bool operator!=(const unordered_iterator& other) const { return this->set_iterator != other.set_iterator; }; + inline const node_type& operator*() const { return *this->set_iterator; }; + private: + friend class octree; + inline explicit unordered_iterator(const typename std::unordered_set::const_iterator& it): set_iterator(it) {}; + typename std::unordered_set::const_iterator set_iterator; + }; + + /** + * Accesses nodes in z-order. TODO: I think this can be done without a stack. + */ + struct iterator + { + inline iterator(const iterator& other): octree(other.octree), stack(other.stack) {}; + inline iterator& operator=(const iterator& other) { this->octree = other.octree; this->stack = other.stack; return *this; }; + iterator& operator++(); + inline bool operator==(const iterator& other) const { return **this == *other; }; + inline bool operator!=(const iterator& other) const { return **this != *other; }; + inline const node_type& operator*() const { return stack.top(); }; + private: + friend class octree; + inline explicit iterator(const octree* octree, node_type node): octree(octree), stack({node}) {}; + const octree* octree; + std::stack stack; + }; + + /** + * Returns the depth of a node. + * + * @param node Node. + * @return Depth of the node. + */ + static T depth(node_type node); + + /** + * Returns the Morton code location of a node. + * + * @param node Node. + * @return Morton code location of the node. + */ + static T location(node_type node); + + /** + * Returns the node at the given depth and location. + * + * @param depth Node depth. + * @param location Node Morton code location. + */ + static node_type node(T depth, T location); + + /** + * Returns the ancestor of a node at the specified depth. + * + * @param node Node whose ancestor will be located. + * @param depth Absolute depth of the ancestors. + * @return Ancestral node. + */ + static node_type ancestor(node_type node, T depth); + + /** + * Returns the parent of a node. + * + * @param node Node. + * @return Parent node. + */ + static node_type parent(node_type node); + + /** + * Returns the nth sibling of a node. + * + * @param node Node. + * @param n Offset to next sibling. (Automatically wraps to 0..7) + * @return Next sibling node. + */ + static node_type sibling(node_type node, T n); + + /** + * Returns the nth child of a node. + * + * @param node Parent node. + * @param n Offset to the nth sibling of the first child node. (Automatically wraps to 0..7) + * @return nth child node. + */ + static node_type child(node_type node, T n); + + /** + * Calculates the first common ancestor of two nodes. + * + * @param a First node. + * @param b Second node. + * @return First common ancestor of the two nodes. + */ + static node_type common_ancestor(node_type a, node_type b); + + /// Creates an octree with a single root node. + octree(); + + /// Returns a z-order iterator to the root node. + iterator begin() const; + + /// Returns a z-order iterator indicating the end of a traversal. + iterator end() const; + + /// Returns an iterator to the specified node. + iterator find(node_type node) const; + + /// Returns an unordered iterator indicating the beginning of a traversal. + unordered_iterator unordered_begin() const; + + /// Returns an unordered iterator indicating the end of a traversal. + unordered_iterator unordered_end() const; + + /** + * Inserts a node and its siblings into the octree, creating its ancestors as necessary. Note: The root node is persistent and cannot be inserted. + * + * @param node Node to insert. + */ + void insert(node_type node); + + /** + * Erases a node along with its siblings and descendants. Note: The root node is persistent and cannot be erased. + * + * @param node Node to erase. + */ + void erase(node_type node); + + /** + * Erases all nodes except the root. + */ + void clear(); + + /// Returns `true` if the node exists in the octree, and `false` otherwise. + bool exists(node_type node) const; + + /// Returns `true` if the node has no children, and `false` otherwise. + bool is_leaf(node_type node) const; + + /// Returns the number of nodes in the octree. + std::size_t size() const; + +private: + /// Compile-time pow() + static constexpr T pow(T x, T exponent); + + /// Count leading zeros + static T clz(T x); + + std::unordered_set nodes; +}; + +/** + * Octree with a 16-bit node type and a maximum depth of `3`. + */ +typedef octree octree16; + +/** + * Octree with a 32-bit node type and a maximum depth of `8`. + */ +typedef octree octree32; + +/** + * Octree with a 64-bit node type and a maximum depth of `18`. + */ +typedef octree octree64; + +template +typename octree::iterator& octree::iterator::operator++() +{ + // Get next node from top of stack + node_type node = stack.top(); + stack.pop(); + + // If the node has children + if (!octree->is_leaf(node)) + { + // Push first child onto the stack + for (T i = 0; i < 8; ++i) + stack.push(child(node, 7 - i)); + } + + if (stack.empty()) + stack.push(std::numeric_limits::max()); + + return *this; +} + +template +constexpr T octree::ceil_log2(T n) +{ + return (n <= 1) ? 0 : ceil_log2((n + 1) / 2) + 1; +} + +template +inline T octree::depth(node_type node) +{ + // Extract depth using a bit mask + constexpr T mask = pow(2, depth_bits) - 1; + return node & mask; +} + +template +inline T octree::location(node_type node) +{ + return node >> ((node_bits - 1) - depth(node) * 3); +} + +template +inline typename octree::node_type octree::node(T depth, T location) +{ + return (location << ((node_bits - 1) - depth * 3)) | depth; +} + +template +inline typename octree::node_type octree::ancestor(node_type node, T depth) +{ + const T mask = std::numeric_limits::max() << ((node_bits - 1) - depth * 3); + return (node & mask) | depth; +} + +template +inline typename octree::node_type octree::parent(node_type node) +{ + return ancestor(node, depth(node) - 1); +} + +template +inline typename octree::node_type octree::sibling(node_type node, T n) +{ + T depth = octree::depth(node); + T location = node >> ((node_bits - 1) - depth * 3); + return octree::node(depth, (location & (~0b111)) | ((location + n) & 0b111)); +} + +template +inline typename octree::node_type octree::child(node_type node, T n) +{ + return sibling(node + 1, n); +} + +template +inline typename octree::node_type octree::common_ancestor(node_type a, node_type b) +{ + T bits = std::min(depth(a), depth(b)) * 3; + T marker = (T(1) << (node_bits - 1)) >> bits; + T depth = clz((a ^ b) | marker) / 3; + return ancestor(a, depth); +} + +template +inline octree::octree(): + nodes({0}) +{} + +template +void octree::insert(node_type node) +{ + if (exists(node)) + return; + + // Insert node + nodes.emplace(node); + + // Insert siblings + for (T i = 1; i < 8; ++i) + nodes.emplace(sibling(node, i)); + + // Insert parent as necessary + node_type parent = octree::parent(node); + if (!exists(parent)) + insert(parent); +} + +template +void octree::erase(node_type node) +{ + // Don't erase the root! + if (node == root) + return; + + for (T i = 0; i < 8; ++i) + { + // Erase node + nodes.erase(node); + + // Erase descendants + if (!is_leaf(node)) + { + for (T j = 0; j < 8; ++j) + erase(child(node, j)); + } + + // Go to next sibling + if (i < 7) + node = sibling(node, i); + } +} + +template +void octree::clear() +{ + nodes = {0}; +} + +template +inline bool octree::exists(node_type node) const +{ + return (nodes.find(node) != nodes.end()); +} + +template +inline bool octree::is_leaf(node_type node) const +{ + return !exists(child(node, 0)); +} + +template +inline std::size_t octree::size() const +{ + return nodes.size(); +} + +template +typename octree::iterator octree::begin() const +{ + return iterator(this, octree::root); +} + +template +typename octree::iterator octree::end() const +{ + return iterator(this, std::numeric_limits::max()); +} + +template +typename octree::iterator octree::find(node_type node) const +{ + return exists(node) ? iterator(node) : end(); +} + +template +typename octree::unordered_iterator octree::unordered_begin() const +{ + return unordered_iterator(nodes.begin()); +} + +template +typename octree::unordered_iterator octree::unordered_end() const +{ + return unordered_iterator(nodes.end()); +} + +template +constexpr T octree::pow(T x, T exponent) +{ + return (exponent == 0) ? 1 : x * pow(x, exponent - 1); +} + +template +T octree::clz(T x) +{ + if (!x) + return sizeof(T) * 8; + + #if defined(__GNU__) + return __builtin_clz(x); + #else + T n = 0; + + while ((x & (T(1) << (8 * sizeof(x) - 1))) == 0) + { + x <<= 1; + ++n; + } + + return n; + #endif +} + +#endif // ANTKEEPER_OCTREE_HPP + diff --git a/src/antkeeper/orbit-cam.cpp b/src/antkeeper/orbit-cam.cpp new file mode 100644 index 0000000..1a88da2 --- /dev/null +++ b/src/antkeeper/orbit-cam.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2020 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 "orbit-cam.hpp" +#include "scene/camera.hpp" +#include +#include + +using namespace vmq::operators; + +template +static inline T lerp(const T& x, const T& y, float a) +{ + return x * (1.0f - a) + y * a; +} + +orbit_cam::orbit_cam(): + elevation_rotation(vmq::identity_quaternion), + azimuth_rotation(vmq::identity_quaternion), + target_elevation_rotation(vmq::identity_quaternion), + target_azimuth_rotation(vmq::identity_quaternion), + target_rotation(vmq::identity_quaternion) +{} + +orbit_cam::~orbit_cam() +{} + +void orbit_cam::update(float dt) +{ + float interpolation_factor = 1.0f; + + // Calculate rotation and target rotation quaternions + //rotation = azimuth_rotation * elevation_rotation; + target_rotation = vmq::normalize(target_azimuth_rotation * target_elevation_rotation); + + // Calculate target translation + target_translation = target_focal_point + target_rotation * float3{0.0f, 0.0f, target_focal_distance}; + + // Interpolate rotation + //rotation = glm::mix(rotation, target_rotation, interpolation_factor); + + // Interpolate angles + set_elevation(lerp(elevation, target_elevation, interpolation_factor)); + set_azimuth(lerp(azimuth, target_azimuth, interpolation_factor)); + + // Calculate rotation + set_rotation(vmq::normalize(azimuth_rotation * elevation_rotation)); + + // Interpolate focal point and focal distance + focal_point = vmq::lerp(focal_point, target_focal_point, interpolation_factor); + focal_distance = lerp(focal_distance, target_focal_distance, interpolation_factor); + + // Caluclate translation + set_translation(focal_point + get_rotation() * float3{0.0f, 0.0f, focal_distance}); + /* + // Recalculate azimuth + azimuth_rotation = rotation; + azimuth_rotation.x = 0.0f; + azimuth_rotation.z = 0.0f; + azimuth_rotation = glm::normalize(azimuth_rotation); + azimuth = 2.0f * std::acos(azimuth_rotation.w); + + // Recalculate elevation + elevation_rotation = rotation; + elevation_rotation.y = 0.0f; + elevation_rotation.z = 0.0f; + elevation_rotation = glm::normalize(elevation_rotation); + elevation = 2.0f * std::acos(elevation_rotation.w); + */ + + // Update camera + if (get_camera() != nullptr) + { + transform transform = vmq::identity_transform; + transform.translation = get_translation(); + transform.rotation = get_rotation(); + get_camera()->set_transform(transform); + //get_camera()->look_at(get_translation(), get_translation() + get_forward(), get_up()); + } +} + +void orbit_cam::move(const float2& direction) +{ + target_focal_point += azimuth_rotation * float3{direction[0], 0.0f, direction[1]}; +} + +void orbit_cam::rotate(float angle) +{ + set_target_azimuth(target_azimuth + angle); +} + +void orbit_cam::tilt(float angle) +{ + set_target_elevation(target_elevation + angle); +} + +void orbit_cam::zoom(float distance) +{ + set_target_focal_distance(target_focal_distance - distance); +} + +void orbit_cam::set_focal_point(const float3& point) +{ + focal_point = point; +} + +void orbit_cam::set_focal_distance(float distance) +{ + focal_distance = distance; +} + +void orbit_cam::set_elevation(float angle) +{ + elevation = angle; + elevation_rotation = vmq::angle_axis(elevation, float3{-1.0f, 0.0f, 0.0f}); +} + +void orbit_cam::set_azimuth(float angle) +{ + azimuth = angle; + azimuth_rotation = vmq::angle_axis(azimuth, float3{0.0f, 1.0f, 0.0f}); +} + +void orbit_cam::set_target_focal_point(const float3& point) +{ + target_focal_point = point; +} + +void orbit_cam::set_target_focal_distance(float distance) +{ + target_focal_distance = distance; +} + +void orbit_cam::set_target_elevation(float angle) +{ + target_elevation = angle; + target_elevation_rotation = vmq::angle_axis(target_elevation, float3{-1.0f, 0.0f, 0.0f}); +} + +void orbit_cam::set_target_azimuth(float angle) +{ + target_azimuth = angle; + target_azimuth_rotation = vmq::angle_axis(target_azimuth, float3{0.0f, 1.0f, 0.0f}); +} + diff --git a/src/antkeeper/orbit-cam.hpp b/src/antkeeper/orbit-cam.hpp new file mode 100644 index 0000000..9bcbc52 --- /dev/null +++ b/src/antkeeper/orbit-cam.hpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2020 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_ORBIT_CAM_HPP +#define ANTKEEPER_ORBIT_CAM_HPP + +#include "camera-rig.hpp" + +/** + * Rig which orbits around a focal point. + */ +class orbit_cam: public camera_rig +{ +public: + orbit_cam(); + virtual ~orbit_cam(); + virtual void update(float dt); + + /// @param direction Specifies the movement direction and speed scale on the XZ plane + void move(const float2& direction); + void rotate(float angle); + void tilt(float angle); + void zoom(float distance); + + void set_focal_point(const float3& point); + void set_focal_distance(float distance); + void set_elevation(float angle); + void set_azimuth(float angle); + void set_target_focal_point(const float3& point); + void set_target_focal_distance(float distance); + void set_target_elevation(float angle); + void set_target_azimuth(float angle); + + const float3& get_focal_point() const; + float get_focal_distance() const; + float get_elevation() const; + float get_azimuth() const; + const float3& get_target_focal_point() const; + float get_target_focal_distance() const; + float get_target_elevation() const; + float get_target_azimuth() const; + const float3& get_target_translation() const; + const vmq::quaternion& get_target_rotation() const; + +private: + float3 focal_point; + float focal_distance; + float elevation; + float azimuth; + + float3 target_focal_point; + float target_focal_distance; + float target_elevation; + float target_azimuth; + + vmq::quaternion elevation_rotation; + vmq::quaternion azimuth_rotation; + vmq::quaternion target_elevation_rotation; + vmq::quaternion target_azimuth_rotation; + vmq::quaternion target_rotation; + float3 target_translation; +}; + +inline const float3& orbit_cam::get_focal_point() const +{ + return focal_point; +} + +inline float orbit_cam::get_focal_distance() const +{ + return focal_distance; +} + +inline float orbit_cam::get_elevation() const +{ + return elevation; +} + +inline float orbit_cam::get_azimuth() const +{ + return azimuth; +} + +inline const float3& orbit_cam::get_target_focal_point() const +{ + return target_focal_point; +} + +inline float orbit_cam::get_target_focal_distance() const +{ + return target_focal_distance; +} + +inline float orbit_cam::get_target_elevation() const +{ + return target_elevation; +} + +inline float orbit_cam::get_target_azimuth() const +{ + return target_azimuth; +} + +inline const float3& orbit_cam::get_target_translation() const +{ + return target_translation; +} + +inline const vmq::quaternion& orbit_cam::get_target_rotation() const +{ + return target_rotation; +} + +#endif // ANTKEEPER_ORBIT_CAM_HPP diff --git a/src/antkeeper/pheromone-matrix.cpp b/src/antkeeper/pheromone-matrix.cpp new file mode 100644 index 0000000..b1031b3 --- /dev/null +++ b/src/antkeeper/pheromone-matrix.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 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 "pheromone-matrix.hpp" +#include + +void convolve(pheromone_matrix* matrix, const float* kernel, int kernel_size) +{ + float* front = matrix->buffers[matrix->current]; + float* back = matrix->buffers[(matrix->current + 1) % 2]; + const int kernel_radius = kernel_size << 1; + + // For each pheromone matrix row + for (int i = 0; i < matrix->rows; ++i) + { + // For each pheromone in the row + for (int j = 0; j < matrix->columns; ++j) + { + // Reset accumulator + float accumulator = 0.0f; + + // For each kernel row + for (int k = -kernel_radius; k <= kernel_radius; ++k) + { + // For each kernel element + for (int l = -kernel_radius; l <= kernel_radius; ++l) + { + // Determine row and column of pheromone corresponding to the kernel element. + int m = i + k; + int n = j + l; + + // If the row and column indices are within the matrix + if (m >= 0 && m < matrix->rows && n >= 0 && n < matrix->columns) + { + // Multiply pheromone strength in the front buffer by the kernel value and add it to the accumulator. + accumulator += front[m * matrix->columns + n] * kernel[(k + kernel_radius) * kernel_size + l + kernel_radius]; + } + } + } + + // Set pheromone strength in the back buffer equal to the accumulator value + back[i * matrix->columns + j] = accumulator; + } + } + + // Swap buffers + matrix->current = (matrix->current + 1) % 2; +} + +void evaporate(pheromone_matrix* matrix, float factor) +{ + const int size = matrix->columns * matrix->rows; + + for (int i = 0; i < size; ++i) + { + matrix->buffers[matrix->current][i] *= factor; + } +} + +void diffuse(pheromone_matrix* matrix) +{ + const vmq::matrix diffusion_kernel = + vmq::mul( + vmq::matrix + {{ + {1, 2, 1}, + {2, 4, 2}, + {1, 2, 1} + }}, + 1.0f / 16.0f); + + convolve(matrix, &diffusion_kernel[0][0], 3); +} + diff --git a/src/antkeeper/pheromone-matrix.hpp b/src/antkeeper/pheromone-matrix.hpp new file mode 100644 index 0000000..e96e99b --- /dev/null +++ b/src/antkeeper/pheromone-matrix.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 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_PHEROMONE_MATRIX_HPP +#define ANTKEEPER_PHEROMONE_MATRIX_HPP + +/** + * A double-buffered matrix containing floating point pheromone strengths. + */ +struct pheromone_matrix +{ + /// Number of columns in the matrix. + int columns; + + /// Number of rows in the matrix. + int rows; + + /// Two buffers containing pheromone strengths + float** buffers; + + /// Index of the current buffer + int current; +}; + +/** + * Performs a convolution on a pheromone matrix. + * + * @param matrix Pointer to a pheromone matrix. + * @param kernel Convolution kernel. + * @param kernel_size Size of the kernel. + */ +void convolve(pheromone_matrix* matrix, const float* kernel, int kernel_size); + +/** + * Causes all pheromones in a pheromone matrix to decrease in strength according to the specified evaporation rate. + * + * @param matrix Pointer to the pheromone matrix. + * @param factor Evaporation factor by which each pheromone strength will be multiplied. + */ +void evaporate(pheromone_matrix* matrix, float rate); + +/** + * Causes all pheromones in a pheromone matrix to diffuse. + */ +void diffuse(pheromone_matrix* matrix); + +#endif // ANTKEEPER_PHEROMONE_MATRIX_HPP + diff --git a/src/dpi-aware.manifest b/src/antkeeper/platform/windows/dpi-aware.manifest similarity index 95% rename from src/dpi-aware.manifest rename to src/antkeeper/platform/windows/dpi-aware.manifest index b25d695..23da6f4 100644 --- a/src/dpi-aware.manifest +++ b/src/antkeeper/platform/windows/dpi-aware.manifest @@ -1,8 +1,8 @@ - - - - - true - - + + + + + true + + \ No newline at end of file diff --git a/src/antkeeper/platform/windows/icon.rc.in b/src/antkeeper/platform/windows/icon.rc.in new file mode 100644 index 0000000..ce699d6 --- /dev/null +++ b/src/antkeeper/platform/windows/icon.rc.in @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "@ICON_FILE@" \ No newline at end of file diff --git a/src/antkeeper/rasterizer/buffer-usage.cpp b/src/antkeeper/rasterizer/buffer-usage.cpp new file mode 100644 index 0000000..bbabfd5 --- /dev/null +++ b/src/antkeeper/rasterizer/buffer-usage.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 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_BUFFER_USAGE_HPP +#define ANTKEEPER_BUFFER_USAGE_HPP + +enum class buffer_usage +{ + stream_draw, + stream_read, + stream_copy, + static_draw, + static_read, + static_copy, + dynamic_draw, + dynamic_read, + dynamic_copy +}; + +#endif // ANTKEEPER_BUFFER_USAGE_HPP + diff --git a/src/antkeeper/rasterizer/buffer-usage.hpp b/src/antkeeper/rasterizer/buffer-usage.hpp new file mode 100644 index 0000000..bbabfd5 --- /dev/null +++ b/src/antkeeper/rasterizer/buffer-usage.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 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_BUFFER_USAGE_HPP +#define ANTKEEPER_BUFFER_USAGE_HPP + +enum class buffer_usage +{ + stream_draw, + stream_read, + stream_copy, + static_draw, + static_read, + static_copy, + dynamic_draw, + dynamic_read, + dynamic_copy +}; + +#endif // ANTKEEPER_BUFFER_USAGE_HPP + diff --git a/src/antkeeper/rasterizer/drawing-mode.hpp b/src/antkeeper/rasterizer/drawing-mode.hpp new file mode 100644 index 0000000..4b56839 --- /dev/null +++ b/src/antkeeper/rasterizer/drawing-mode.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 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_DRAWING_MODE_HPP +#define ANTKEEPER_DRAWING_MODE_HPP + +enum class drawing_mode +{ + points, + line_strip, + line_loop, + lines, + line_strip_adjacency, + lines_adjacency, + triangle_strip, + triangle_fan, + triangles, + triangle_strip_adjacency, + triangles_adjacency +}; + +#endif // ANTKEEPER_DRAWING_MODE_HPP + diff --git a/src/antkeeper/rasterizer/element-array-type.hpp b/src/antkeeper/rasterizer/element-array-type.hpp new file mode 100644 index 0000000..5a2e175 --- /dev/null +++ b/src/antkeeper/rasterizer/element-array-type.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 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_ELEMENT_ARRAY_TYPE_HPP +#define ANTKEEPER_ELEMENT_ARRAY_TYPE_HPP + +enum class element_array_type +{ + uint_8, + uint_16, + uint_32 +}; + +#endif // ANTKEEPER_ELEMENT_ARRAY_TYPE_HPP + diff --git a/src/antkeeper/rasterizer/framebuffer.cpp b/src/antkeeper/rasterizer/framebuffer.cpp new file mode 100644 index 0000000..01a6a37 --- /dev/null +++ b/src/antkeeper/rasterizer/framebuffer.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 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 "rasterizer/framebuffer.hpp" +#include "rasterizer/texture-2d.hpp" +#include + +static constexpr GLenum attachment_lut[] = +{ + GL_COLOR_ATTACHMENT0, + GL_DEPTH_ATTACHMENT, + GL_STENCIL_ATTACHMENT +}; + +framebuffer::framebuffer(int width, int height): + gl_framebuffer_id(0), + dimensions({width, height}), + color_attachment(nullptr), + depth_attachment(nullptr), + stencil_attachment(nullptr) +{ + glGenFramebuffers(1, &gl_framebuffer_id); +} + +framebuffer::framebuffer(): + gl_framebuffer_id(0), + color_attachment(nullptr), + depth_attachment(nullptr), + stencil_attachment(nullptr) +{} + +framebuffer::~framebuffer() +{ + if (gl_framebuffer_id) + { + glDeleteFramebuffers(1, &gl_framebuffer_id); + } +} + +void framebuffer::resize(int width, int height) +{ + dimensions = {width, height}; +} + +void framebuffer::attach(framebuffer_attachment_type attachment_type, texture_2d* texture) +{ + glBindFramebuffer(GL_FRAMEBUFFER, gl_framebuffer_id); + + GLenum gl_attachment = attachment_lut[static_cast(attachment_type)]; + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attachment, GL_TEXTURE_2D, texture->gl_texture_id, 0); + + if (attachment_type == framebuffer_attachment_type::color) + color_attachment = texture; + else if (attachment_type == framebuffer_attachment_type::depth) + depth_attachment = texture; + else if (attachment_type == framebuffer_attachment_type::stencil) + stencil_attachment = texture; + + if (!color_attachment) + { + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + } + else + { + glDrawBuffer(GL_COLOR_ATTACHMENT0); + glReadBuffer(GL_COLOR_ATTACHMENT0); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} diff --git a/src/antkeeper/rasterizer/framebuffer.hpp b/src/antkeeper/rasterizer/framebuffer.hpp new file mode 100644 index 0000000..7fa86f5 --- /dev/null +++ b/src/antkeeper/rasterizer/framebuffer.hpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2020 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_FRAMEBUFFER_HPP +#define ANTKEEPER_FRAMEBUFFER_HPP + +#include + +class rasterizer; +class texture_2d; + +enum class framebuffer_attachment_type +{ + color, + depth, + stencil +}; + +class framebuffer +{ +public: + /** + * Creates a framebuffer. + */ + framebuffer(int width, int height); + + /// Destroys a framebuffer. + ~framebuffer(); + + /** + * Resizes the framebuffer. Note: This does not resize any attached textures. + * + * @param width New width of the framebuffer. + * @param height New height of the framebuffer. + */ + void resize(int width, int height); + + /** + * Attaches a color, depth, or stencil texture to the framebuffer. + * + * @param attachment_type Type of attachment. + */ + void attach(framebuffer_attachment_type attachment_type, texture_2d* texture); + + /// Returns the dimensions of the framebuffer, in pixels. + const std::tuple& get_dimensions() const; + + const texture_2d* get_color_attachment() const; + texture_2d* get_color_attachment(); + const texture_2d* get_depth_attachment() const; + texture_2d* get_depth_attachment(); + const texture_2d* get_stencil_attachment() const; + texture_2d* get_stencil_attachment(); + +private: + friend class rasterizer; + + framebuffer(); + + unsigned int gl_framebuffer_id; + std::tuple dimensions; + texture_2d* color_attachment; + texture_2d* depth_attachment; + texture_2d* stencil_attachment; +}; + +inline const std::tuple& framebuffer::get_dimensions() const +{ + return dimensions; +} + +inline const texture_2d* framebuffer::get_color_attachment() const +{ + return color_attachment; +} + +inline texture_2d* framebuffer::get_color_attachment() +{ + return color_attachment; +} + +inline const texture_2d* framebuffer::get_depth_attachment() const +{ + return depth_attachment; +} + +inline texture_2d* framebuffer::get_depth_attachment() +{ + return depth_attachment; +} + +inline const texture_2d* framebuffer::get_stencil_attachment() const +{ + return stencil_attachment; +} + +inline texture_2d* framebuffer::get_stencil_attachment() +{ + return stencil_attachment; +} + + +#endif // ANTKEEPER_FRAMEBUFFER_HPP + diff --git a/src/antkeeper/rasterizer/pixel-format.hpp b/src/antkeeper/rasterizer/pixel-format.hpp new file mode 100644 index 0000000..5600b93 --- /dev/null +++ b/src/antkeeper/rasterizer/pixel-format.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 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_PIXEL_FORMAT_HPP +#define ANTKEEPER_PIXEL_FORMAT_HPP + +enum class pixel_format +{ + 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 +}; + +#endif // ANTKEEPER_PIXEL_FORMAT_HPP + diff --git a/src/entity/components/transform-component.cpp b/src/antkeeper/rasterizer/pixel-type.hpp similarity index 50% rename from src/entity/components/transform-component.cpp rename to src/antkeeper/rasterizer/pixel-type.hpp index 8533213..b74083c 100644 --- a/src/entity/components/transform-component.cpp +++ b/src/antkeeper/rasterizer/pixel-type.hpp @@ -1,28 +1,36 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#include "transform-component.hpp" +#ifndef ANTKEEPER_PIXEL_TYPE_HPP +#define ANTKEEPER_PIXEL_TYPE_HPP -ComponentBase* TransformComponent::clone() const +enum class pixel_type { - TransformComponent* component = new TransformComponent(); - component->transform = transform; - return component; -} + int_8, + uint_8, + int_16, + uint_16, + int_32, + uint_32, + float_16, + float_32 +}; + +#endif // ANTKEEPER_PIXEL_TYPE_HPP diff --git a/src/antkeeper/rasterizer/rasterizer.cpp b/src/antkeeper/rasterizer/rasterizer.cpp new file mode 100644 index 0000000..b881c42 --- /dev/null +++ b/src/antkeeper/rasterizer/rasterizer.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2020 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 "rasterizer/rasterizer.hpp" +#include "rasterizer/framebuffer.hpp" +#include "rasterizer/shader-program.hpp" +#include "rasterizer/vertex-array.hpp" +#include + +static constexpr GLenum drawing_mode_lut[] = +{ + GL_POINTS, + GL_LINE_STRIP, + GL_LINE_LOOP, + GL_LINES, + GL_LINE_STRIP_ADJACENCY, + GL_LINES_ADJACENCY, + GL_TRIANGLE_STRIP, + GL_TRIANGLE_FAN, + GL_TRIANGLES, + GL_TRIANGLE_STRIP_ADJACENCY, + GL_TRIANGLES_ADJACENCY +}; + +static constexpr GLenum element_array_type_lut[] = +{ + GL_UNSIGNED_BYTE, + GL_UNSIGNED_SHORT, + GL_UNSIGNED_INT +}; + +rasterizer::rasterizer(): + bound_vao(nullptr), + bound_shader_program(nullptr) +{ + // Determine dimensions of default framebuffer + GLint scissor_box[4] = {0, 0, 0, 0}; + glGetIntegerv(GL_SCISSOR_BOX, scissor_box); + + // Setup default framebuffer + default_framebuffer = new framebuffer(); + default_framebuffer->gl_framebuffer_id = 0; + default_framebuffer->dimensions = {scissor_box[2], scissor_box[3]}; + + // Bind default framebuffer + bound_framebuffer = default_framebuffer; +} + +rasterizer::~rasterizer() +{ + delete default_framebuffer; +} + +void rasterizer::window_resized(int width, int height) +{ + default_framebuffer->dimensions = {width, height}; +} + +void rasterizer::use_framebuffer(const ::framebuffer& framebuffer) +{ + if (bound_framebuffer != &framebuffer) + { + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.gl_framebuffer_id); + bound_framebuffer = &framebuffer; + } +} + +void rasterizer::set_clear_color(float r, float g, float b, float a) +{ + glClearColor(r, g, b, a); +} + +void rasterizer::set_clear_depth(float depth) +{ + glClearDepth(depth); +} + +void rasterizer::set_clear_stencil(int s) +{ + glClearStencil(s); +} + +void rasterizer::clear_framebuffer(bool color, bool depth, bool stencil) +{ + GLbitfield mask = 0; + + if (color) mask |= GL_COLOR_BUFFER_BIT; + if (depth) mask |= GL_DEPTH_BUFFER_BIT; + if (stencil) mask |= GL_STENCIL_BUFFER_BIT; + + glClear(mask); +} + +void rasterizer::set_viewport(int x, int y, int width, int height) +{ + glViewport(x, y, static_cast(width), static_cast(height)); +} + +void rasterizer::use_program(const shader_program& program) +{ + if (bound_shader_program != &program) + { + glUseProgram(program.gl_program_id); + bound_shader_program = &program; + } +} + +void rasterizer::draw_arrays(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count) +{ + GLenum gl_mode = drawing_mode_lut[static_cast(mode)]; + + if (bound_vao != &vao) + { + glBindVertexArray(vao.gl_array_id); + bound_vao = &vao; + } + + glDrawArrays(gl_mode, static_cast(offset), static_cast(count)); +} + +void rasterizer::draw_arrays_instanced(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count, std::size_t instance_count) +{ + GLenum gl_mode = drawing_mode_lut[static_cast(mode)]; + + if (bound_vao != &vao) + { + glBindVertexArray(vao.gl_array_id); + bound_vao = &vao; + } + + glDrawArraysInstanced(gl_mode, static_cast(offset), static_cast(count), static_cast(instance_count)); +} + +void rasterizer::draw_elements(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count, element_array_type type) +{ + GLenum gl_mode = drawing_mode_lut[static_cast(mode)]; + GLenum gl_type = element_array_type_lut[static_cast(type)]; + + if (bound_vao != &vao) + { + glBindVertexArray(vao.gl_array_id); + bound_vao = &vao; + } + + glDrawElements(gl_mode, static_cast(count), gl_type, (const GLvoid*)offset); +} + diff --git a/src/antkeeper/rasterizer/rasterizer.hpp b/src/antkeeper/rasterizer/rasterizer.hpp new file mode 100644 index 0000000..e64f0e0 --- /dev/null +++ b/src/antkeeper/rasterizer/rasterizer.hpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2020 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_RASTERIZER_HPP +#define ANTKEEPER_RASTERIZER_HPP + +#include + +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. + */ +class rasterizer +{ +public: + /** + * Creates a rasterizer. Warning: This must be called after an OpenGL context has been created. + */ + rasterizer(); + + /// Destroys a rasterizer. + ~rasterizer(); + + /** + * This should be called when the window associated with the OpenGL context is resized, and will effectively changed the reported dimensions of the default framebuffer. + */ + void window_resized(int width, int height); + + /** + * Sets the active framebuffer. + * + * @param framebuffer Framebuffer to use. + */ + void use_framebuffer(const ::framebuffer& framebuffer); + + /** + * Sets the color to be used when the color buffer of a framebuffer is cleared. + * + * @param r Red color component. + * @param g Green color component. + * @param b Blue color component. + * @param a Alpha color component. + */ + void set_clear_color(float r, float g, float b, float a); + + /** + * Sets the depth value to be used when the depth buffer of a framebuffer is cleared. + * + * @param depth Depth value. + */ + void set_clear_depth(float depth); + + /** + * Sets the stencil value to be used when the stencil buffer of a framebuffer is cleared. + * + * @param s Stencil value. + */ + void set_clear_stencil(int s); + + /** + * Clears the buffers attached to a framebuffer. + * + * @param color Specifies whether the color buffer should be cleared. + * @param depth Specifies whether the depth buffer should be cleared. + * @param stencil Specifies whether the stencil buffer should be cleared. + */ + void clear_framebuffer(bool color, bool depth, bool stencil); + + /** + * Sets the active viewport. + * + * @param x X-coordinate of the viewport. + * @param y Y-coordinate of the viewport. + * @param width Width of the viewport. + * @param height Height of the viewport. + */ + void set_viewport(int x, int y, int width, int height); + + /** + * Binds a shader program. + * + * @param program Shader program to bind. + */ + void use_program(const shader_program& program); + + /** + * + */ + void draw_arrays(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count); + + void draw_arrays_instanced(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count, std::size_t instance_count); + + /** + * + */ + void draw_elements(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count, element_array_type type); + + /** + * Returns the default framebuffer associated with the OpenGL context of a window. + */ + const framebuffer& get_default_framebuffer() const; + +private: + framebuffer* 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; +} + +#endif // ANTKEEPER_RASTERIZER_HPP + diff --git a/src/antkeeper/rasterizer/shader-input.cpp b/src/antkeeper/rasterizer/shader-input.cpp new file mode 100644 index 0000000..3531d9e --- /dev/null +++ b/src/antkeeper/rasterizer/shader-input.cpp @@ -0,0 +1,673 @@ +/* + * Copyright (C) 2020 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 "rasterizer/shader-input.hpp" +#include "rasterizer/texture-2d.hpp" +#include "rasterizer/texture-cube.hpp" +#include + +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; +} + +#include +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_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_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_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_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_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; +} + diff --git a/src/antkeeper/rasterizer/shader-input.hpp b/src/antkeeper/rasterizer/shader-input.hpp new file mode 100644 index 0000000..5d455af --- /dev/null +++ b/src/antkeeper/rasterizer/shader-input.hpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2020 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_SHADER_INPUT_HPP +#define ANTKEEPER_SHADER_INPUT_HPP + +#include +#include + +class shader_program; +class texture_2d; +class texture_cube; +enum class shader_variable_type; +using namespace vmq::types; + +/** + * 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_2d* 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_2d* 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_2d** 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; +} + +#endif // ANTKEEPER_SHADER_INPUT_HPP + diff --git a/src/antkeeper/rasterizer/shader-program.cpp b/src/antkeeper/rasterizer/shader-program.cpp new file mode 100644 index 0000000..ae78a5f --- /dev/null +++ b/src/antkeeper/rasterizer/shader-program.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2020 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 "rasterizer/shader-program.hpp" +#include "rasterizer/shader.hpp" +#include "rasterizer/shader-variable-type.hpp" +#include "rasterizer/shader-input.hpp" +#include +#include + +shader_program::shader_program(const std::list& shaders): + gl_program_id(0) +{ + gl_program_id = glCreateProgram(); + + for (shader* shader: shaders) + { + glAttachShader(gl_program_id, shader->gl_shader_id); + } + + glLinkProgram(gl_program_id); + + GLint status; + glGetProgramiv(gl_program_id, GL_LINK_STATUS, &status); + if (status == GL_FALSE) + { + throw std::runtime_error(get_info_log().c_str()); + } + + for (shader* shader: shaders) + { + glDetachShader(gl_program_id, shader->gl_shader_id); + } + + // Find shader inputs + find_inputs(); +} + +shader_program::~shader_program() +{ + glDeleteProgram(gl_program_id); + + for (shader_input* input: inputs) + { + delete input; + } +} + +void shader_program::find_inputs() +{ + // 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]; + + // 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; + + // For each active uniform + for (GLuint uniform_index = 0; uniform_index < static_cast(active_uniform_count); ++uniform_index) + { + // Get information about uniform + 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]); + + // 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) + { + input_name = input_name.substr(0, bracketPos); + } + + // Determine corresponding shader variable data type + shader_variable_type variable_type; + int texture_unit = -1; + bool unsupported = false; + switch (uniform_type) + { + case GL_BOOL: + variable_type = shader_variable_type::bool1; + break; + case GL_BOOL_VEC2: + variable_type = shader_variable_type::bool2; + break; + case GL_BOOL_VEC3: + variable_type = shader_variable_type::bool3; + break; + case GL_BOOL_VEC4: + variable_type = shader_variable_type::bool4; + break; + + case GL_INT: + variable_type = shader_variable_type::int1; + break; + case GL_INT_VEC2: + variable_type = shader_variable_type::int2; + break; + case GL_INT_VEC3: + variable_type = shader_variable_type::int3; + break; + case GL_INT_VEC4: + variable_type = shader_variable_type::int4; + break; + + case GL_UNSIGNED_INT: + variable_type = shader_variable_type::uint1; + break; + case GL_UNSIGNED_INT_VEC2: + variable_type = shader_variable_type::uint2; + break; + case GL_UNSIGNED_INT_VEC3: + variable_type = shader_variable_type::uint3; + break; + case GL_UNSIGNED_INT_VEC4: + variable_type = shader_variable_type::uint4; + break; + + case GL_FLOAT: + variable_type = shader_variable_type::float1; + break; + case GL_FLOAT_VEC2: + variable_type = shader_variable_type::float2; + break; + case GL_FLOAT_VEC3: + variable_type = shader_variable_type::float3; + break; + case GL_FLOAT_VEC4: + variable_type = shader_variable_type::float4; + break; + + case GL_FLOAT_MAT2: + variable_type = shader_variable_type::float2x2; + break; + case GL_FLOAT_MAT3: + variable_type = shader_variable_type::float3x3; + break; + case GL_FLOAT_MAT4: + variable_type = shader_variable_type::float4x4; + 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; + break; + + case GL_SAMPLER_CUBE: + variable_type = shader_variable_type::texture_cube; + texture_unit = available_texture_unit; + available_texture_unit += uniform_size; + break; + + default: + unsupported = true; + 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()); + } + + // 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); + } + + // Free uniform name buffer + delete[] uniform_name; +} + +std::string shader_program::get_info_log() const +{ + GLint length; + glGetProgramiv(gl_program_id, GL_INFO_LOG_LENGTH, &length); + + if (length > 0) + { + std::string log(length, '\0'); + glGetProgramInfoLog(gl_program_id, length, &length, &log[0]); + return log; + } + + return std::string(); +} + diff --git a/src/antkeeper/rasterizer/shader-program.hpp b/src/antkeeper/rasterizer/shader-program.hpp new file mode 100644 index 0000000..880ca02 --- /dev/null +++ b/src/antkeeper/rasterizer/shader-program.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 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_SHADER_PROGRAM_HPP +#define ANTKEEPER_SHADER_PROGRAM_HPP + +#include +#include +#include +#include + +class shader; +class rasterizer; +class shader_input; + +class shader_program +{ +public: + /** + * Creates a shader program. + * + * @param shaders List of shaders to be linked to the program. Note that after the shader program has been created, these shaders can be deleted if desired with no effect on the shader program. + */ + explicit shader_program(const std::list& shaders); + ~shader_program(); + + 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; + + void find_inputs(); + std::string get_info_log() const; + + unsigned int gl_program_id; + std::list inputs; + std::map input_map; +}; + +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()) + { + return nullptr; + } + + return it->second; +} + +#endif // ANTKEEPER_SHADER_PROGRAM_HPP + diff --git a/src/entity/components/ant-hill-component.cpp b/src/antkeeper/rasterizer/shader-type.hpp similarity index 52% rename from src/entity/components/ant-hill-component.cpp rename to src/antkeeper/rasterizer/shader-type.hpp index 3a2ec98..49261a8 100644 --- a/src/entity/components/ant-hill-component.cpp +++ b/src/antkeeper/rasterizer/shader-type.hpp @@ -1,27 +1,31 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#include "ant-hill-component.hpp" +#ifndef ANTKEEPER_SHADER_TYPE_HPP +#define ANTKEEPER_SHADER_TYPE_HPP -ComponentBase* AntHillComponent::clone() const +enum class shader_type { - AntHillComponent* component = new AntHillComponent(); - return component; -} + vertex, + fragment, + geometry +}; + +#endif // ANTKEEPER_SHADER_TYPE_HPP diff --git a/src/antkeeper/rasterizer/shader-variable-type.hpp b/src/antkeeper/rasterizer/shader-variable-type.hpp new file mode 100644 index 0000000..4e0b8c3 --- /dev/null +++ b/src/antkeeper/rasterizer/shader-variable-type.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 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_SHADER_VARIABLE_TYPE_HPP +#define ANTKEEPER_SHADER_VARIABLE_TYPE_HPP + +enum class shader_variable_type +{ + bool1, + bool2, + bool3, + bool4, + int1, + int2, + int3, + int4, + uint1, + uint2, + uint3, + uint4, + float1, + float2, + float3, + float4, + float2x2, + float3x3, + float4x4, + texture_2d, + texture_cube +}; + +#endif // ANTKEEPER_SHADER_VARIABLE_TYPE_HPP + diff --git a/src/antkeeper/rasterizer/shader.cpp b/src/antkeeper/rasterizer/shader.cpp new file mode 100644 index 0000000..2b26cbc --- /dev/null +++ b/src/antkeeper/rasterizer/shader.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 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 "rasterizer/shader.hpp" +#include +#include + +static constexpr GLenum shader_type_lut[] = +{ + GL_VERTEX_SHADER, + GL_FRAGMENT_SHADER, + GL_GEOMETRY_SHADER +}; + +shader::shader(shader_type type, const std::string& source): + gl_shader_id(0), + type(type) +{ + GLenum gl_shader_type = shader_type_lut[static_cast(type)]; + const char* source_c_str = source.c_str(); + + gl_shader_id = glCreateShader(gl_shader_type); + glShaderSource(gl_shader_id, 1, &source_c_str, nullptr); + glCompileShader(gl_shader_id); + + GLint status; + glGetShaderiv(gl_shader_id, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) + { + throw std::runtime_error(get_info_log().c_str()); + } +} + +shader::~shader() +{ + glDeleteShader(gl_shader_id); +} + +std::string shader::get_info_log() const +{ + GLint length; + glGetShaderiv(gl_shader_id, GL_INFO_LOG_LENGTH, &length); + + if (length > 0) + { + std::string log(length, '\0'); + glGetShaderInfoLog(gl_shader_id, length, &length, &log[0]); + return log; + } + + return std::string(); +} + diff --git a/src/antkeeper/rasterizer/shader.hpp b/src/antkeeper/rasterizer/shader.hpp new file mode 100644 index 0000000..3418446 --- /dev/null +++ b/src/antkeeper/rasterizer/shader.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 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_SHADER_HPP +#define ANTKEEPER_SHADER_HPP + +#include +#include + +enum class shader_type; +class shader_program; + +class shader +{ +public: + shader(shader_type type, const std::string& source); + ~shader(); + + shader(const shader&) = delete; + shader& operator=(const shader&) = delete; + + shader_type get_type() const; + +private: + friend class shader_program; + + std::string get_info_log() const; + + unsigned int gl_shader_id; + shader_type type; +}; + +inline shader_type shader::get_type() const +{ + return type; +} + +#endif // ANTKEEPER_SHADER_HPP + diff --git a/src/antkeeper/rasterizer/texture-2d.cpp b/src/antkeeper/rasterizer/texture-2d.cpp new file mode 100644 index 0000000..0a07060 --- /dev/null +++ b/src/antkeeper/rasterizer/texture-2d.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2020 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 "rasterizer/texture-2d.hpp" +#include "rasterizer/texture-wrapping.hpp" +#include "rasterizer/texture-filter.hpp" +#include +#include + +static constexpr GLenum pixel_format_lut[] = +{ + GL_DEPTH_COMPONENT, + GL_DEPTH_STENCIL, + GL_RED, + GL_RG, + GL_RGB, + GL_BGR, + GL_RGBA, + GL_BGRA +}; + +static constexpr GLenum pixel_type_lut[] = +{ + GL_BYTE, + GL_UNSIGNED_BYTE, + GL_SHORT, + GL_UNSIGNED_SHORT, + GL_INT, + GL_UNSIGNED_INT, + GL_HALF_FLOAT, + GL_FLOAT +}; + +static constexpr GLenum internal_format_lut[][8] = +{ + {GL_NONE, GL_NONE, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT32, GL_NONE, GL_DEPTH_COMPONENT32F}, + + // Note: GL_DEPTH32F_STENCIL8 is actually a 64-bit format, 32 depth bits, 8 stencil bits, and 24 alignment bits. + {GL_NONE, GL_NONE, GL_NONE, GL_NONE, GL_DEPTH24_STENCIL8, GL_DEPTH24_STENCIL8, GL_NONE, GL_DEPTH32F_STENCIL8}, + + {GL_R8, GL_R8, GL_R16, GL_R16, GL_R32F, GL_R32F, GL_R16F, GL_R32F}, + {GL_RG8, GL_RG8, GL_RG16, GL_RG16, GL_RG32F, GL_RG32F, GL_RG16F, GL_RG32F}, + {GL_RGB8, GL_RGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F}, + {GL_RGB8, GL_RGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F}, + {GL_RGBA8, GL_RGBA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F}, + {GL_RGBA8, GL_RGBA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F} + +}; + +static constexpr GLint swizzle_mask_lut[][4] = +{ + {GL_RED, GL_RED, GL_RED, GL_ONE}, + {GL_RED, GL_GREEN, GL_ZERO, GL_ONE}, + {GL_RED, GL_RED, GL_RED, GL_ONE}, + {GL_RED, GL_RED, GL_RED, GL_GREEN}, + {GL_RED, GL_GREEN, GL_BLUE, GL_ONE}, + {GL_RED, GL_GREEN, GL_BLUE, GL_ONE}, + {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}, + {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA} +}; + +static constexpr GLenum wrapping_lut[] = +{ + GL_CLAMP_TO_EDGE, + GL_REPEAT, + GL_MIRRORED_REPEAT +}; + +static constexpr GLenum min_filter_lut[] = +{ + GL_NEAREST, + GL_LINEAR, + GL_NEAREST_MIPMAP_NEAREST, + GL_LINEAR_MIPMAP_NEAREST, + GL_NEAREST_MIPMAP_LINEAR, + GL_LINEAR_MIPMAP_LINEAR +}; + +static constexpr GLenum mag_filter_lut[] = +{ + GL_NEAREST, + GL_LINEAR +}; + +texture_2d::texture_2d(int width, int height, ::pixel_type type, ::pixel_format format, const void* data): + gl_texture_id(0), + dimensions({0, 0}), + wrapping({texture_wrapping::repeat, texture_wrapping::repeat}), + filters({texture_min_filter::linear_mipmap_linear, texture_mag_filter::linear}), + max_anisotropy(0.0f) +{ + glGenTextures(1, &gl_texture_id); + resize(width, height, type, format, data); + set_wrapping(std::get<0>(wrapping), std::get<1>(wrapping)); + set_filters(std::get<0>(filters), std::get<1>(filters)); + set_max_anisotropy(max_anisotropy); +} + +texture_2d::~texture_2d() +{ + glDeleteTextures(1, &gl_texture_id); +} + +void texture_2d::resize(int width, int height, ::pixel_type type, ::pixel_format format, const void* data) +{ + dimensions = {width, height}; + pixel_type = type; + pixel_format = format; + + GLenum gl_internal_format = internal_format_lut[static_cast(format)][static_cast(type)]; + GLenum gl_format = pixel_format_lut[static_cast(format)]; + const GLint* gl_swizzle_mask = swizzle_mask_lut[static_cast(format)]; + + GLenum gl_type = pixel_type_lut[static_cast(type)]; + + // Special cases for depth + stencil pixel formats + if (gl_internal_format == GL_DEPTH24_STENCIL8) + gl_type = GL_UNSIGNED_INT_24_8; + else if (gl_internal_format == GL_DEPTH32F_STENCIL8) + gl_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glBindTexture(GL_TEXTURE_2D, gl_texture_id); + glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, width, height, 0, gl_format, gl_type, data); + glGenerateMipmap(GL_TEXTURE_2D); + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle_mask); + + /// TODO: remove this + if (format == pixel_format::d) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + } +} + +void texture_2d::set_wrapping(texture_wrapping wrap_s, texture_wrapping wrap_t) +{ + wrapping = {wrap_s, wrap_t}; + + GLenum gl_wrap_s = wrapping_lut[static_cast(wrap_s)]; + GLenum gl_wrap_t = wrapping_lut[static_cast(wrap_t)]; + + glBindTexture(GL_TEXTURE_2D, gl_texture_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gl_wrap_s); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gl_wrap_t); +} + +void texture_2d::set_filters(texture_min_filter min_filter, texture_mag_filter mag_filter) +{ + filters = {min_filter, mag_filter}; + + GLenum gl_min_filter = min_filter_lut[static_cast(min_filter)]; + GLenum gl_mag_filter = mag_filter_lut[static_cast(mag_filter)]; + + glBindTexture(GL_TEXTURE_2D, gl_texture_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_min_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_mag_filter); +} + +void texture_2d::set_max_anisotropy(float anisotropy) +{ + this->max_anisotropy = std::max(0.0f, std::min(1.0f, anisotropy)); + + // Lerp between 1.0f and GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + float gl_max_texture_max_anisotropy; + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_max_texture_max_anisotropy); + float gl_max_anisotropy = 1.0f + this->max_anisotropy * (gl_max_texture_max_anisotropy - 1.0f); + + glBindTexture(GL_TEXTURE_2D, gl_texture_id); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_max_anisotropy); +} + diff --git a/src/antkeeper/rasterizer/texture-2d.hpp b/src/antkeeper/rasterizer/texture-2d.hpp new file mode 100644 index 0000000..24abba0 --- /dev/null +++ b/src/antkeeper/rasterizer/texture-2d.hpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2020 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_TEXTURE_2D_HPP +#define ANTKEEPER_TEXTURE_2D_HPP + +#include +#include "rasterizer/pixel-format.hpp" +#include "rasterizer/pixel-type.hpp" + +class framebuffer; +class shader_input; +enum class texture_mag_filter; +enum class texture_min_filter; +enum class texture_wrapping; + +/** + * A 2D texture which can be uploaded to shaders via shader inputs. + */ +class texture_2d +{ +public: + /** + * Creates a 2D texture. + */ + texture_2d(int width, int height, ::pixel_type type = ::pixel_type::uint_8, ::pixel_format format = ::pixel_format::rgba, const void* data = nullptr); + + /** + * Destroys a 2D texture. + */ + ~texture_2d(); + + /** + * Resizes the texture. + */ + void resize(int width, int height, ::pixel_type type, ::pixel_format format, const void* data); + + /** + * Sets the texture wrapping modes. + * + * @param wrap_s Wrapping mode for s-coordinates. + * @param wrap_t Wrapping mode for t-coordinates. + */ + void set_wrapping(texture_wrapping wrap_s, texture_wrapping wrap_t); + + /** + * Sets the texture filtering modes. + * + * @param min_filter Texture minification filter. + * @param mag_filter Texture magnification filter. + */ + void set_filters(texture_min_filter min_filter, texture_mag_filter mag_filter); + + /** + * Sets the maximum anisotropy. + * + * @param level Max anisotropy on `[0.0, 1.0]`, with `0.0` indicating normal filtering, and `1.0` indicating maximum anisotropic filtering. + */ + void set_max_anisotropy(float anisotropy); + + /// Returns the dimensions of the texture, in pixels. + const std::tuple& get_dimensions() const; + + /// Returns the pixel type enumeration. + const pixel_type& get_pixel_type() const; + + /// Returns the pixel format enumeration. + const pixel_format& get_pixel_format() const; + + /// Returns the wrapping modes of the texture. + const std::tuple get_wrapping() const; + + /// Returns the filtering modes of the texture. + const std::tuple get_filters() const; + + /// Returs the maximum anisotropy. + float get_max_anisotropy() const; + +private: + friend class framebuffer; + friend class shader_input; + + unsigned int gl_texture_id; + std::tuple dimensions; + ::pixel_type pixel_type; + ::pixel_format pixel_format; + std::tuple wrapping; + std::tuple filters; + float max_anisotropy; +}; + +inline const std::tuple& texture_2d::get_dimensions() const +{ + return dimensions; +} + +inline const pixel_type& texture_2d::get_pixel_type() const +{ + return pixel_type; +} + +inline const pixel_format& texture_2d::get_pixel_format() const +{ + return pixel_format; +} + +inline const std::tuple texture_2d::get_wrapping() const +{ + return wrapping; +} + +inline const std::tuple texture_2d::get_filters() const +{ + return filters; +} + +inline float texture_2d::get_max_anisotropy() const +{ + return max_anisotropy; +} + +#endif // ANTKEEPER_TEXTURE_2D_HPP + diff --git a/src/antkeeper/rasterizer/texture-cube.cpp b/src/antkeeper/rasterizer/texture-cube.cpp new file mode 100644 index 0000000..323b452 --- /dev/null +++ b/src/antkeeper/rasterizer/texture-cube.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 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 "rasterizer/texture-cube.hpp" +#include + +texture_cube::texture_cube(): + gl_texture_id(0), + face_size(0) +{ + glGenTextures(1, &gl_texture_id); +} + +texture_cube::~texture_cube() +{ + glDeleteTextures(1, &gl_texture_id); +} + diff --git a/src/antkeeper/rasterizer/texture-cube.hpp b/src/antkeeper/rasterizer/texture-cube.hpp new file mode 100644 index 0000000..0c8eed4 --- /dev/null +++ b/src/antkeeper/rasterizer/texture-cube.hpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 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_TEXTURE_CUBE_HPP +#define ANTKEEPER_TEXTURE_CUBE_HPP + +class shader_input; + +/** + * A cube texture which can be uploaded to shaders via shader inputs. + */ +class texture_cube +{ +public: + /** + * Creates a cube texture. + */ + texture_cube(); + + /** + * Destroys a cube texture. + */ + ~texture_cube(); + + /// 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; +}; + +inline int texture_cube::get_face_size() const +{ + return face_size; +} + +#endif // ANTKEEPER_TEXTURE_CUBE_HPP + diff --git a/src/antkeeper/rasterizer/texture-filter.hpp b/src/antkeeper/rasterizer/texture-filter.hpp new file mode 100644 index 0000000..47534eb --- /dev/null +++ b/src/antkeeper/rasterizer/texture-filter.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 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_TEXTURE_FILTER_HPP +#define ANTKEEPER_TEXTURE_FILTER_HPP + +enum class texture_min_filter +{ + nearest, + linear, + nearest_mipmap_nearest, + linear_mipmap_nearest, + nearest_mipmap_linear, + linear_mipmap_linear +}; + +enum class texture_mag_filter +{ + nearest, + linear +}; + +#endif // ANTKEEPER_TEXTURE_FILTER_HPP + diff --git a/src/entity/entity-id.hpp b/src/antkeeper/rasterizer/texture-wrapping.hpp similarity index 51% rename from src/entity/entity-id.hpp rename to src/antkeeper/rasterizer/texture-wrapping.hpp index dca665d..07fc474 100644 --- a/src/entity/entity-id.hpp +++ b/src/antkeeper/rasterizer/texture-wrapping.hpp @@ -1,28 +1,31 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#ifndef ENTITY_ID_HPP -#define ENTITY_ID_HPP +#ifndef ANTKEEPER_TEXTURE_WRAPPING_HPP +#define ANTKEEPER_TEXTURE_WRAPPING_HPP -#include +enum class texture_wrapping +{ + clamp, + repeat, + mirrored_repeat +}; -typedef std::uint32_t EntityID; - -#endif // ENTITY_ID_HPP +#endif // ANTKEEPER_TEXTURE_WRAPPING_HPP diff --git a/src/antkeeper/rasterizer/vertex-array.cpp b/src/antkeeper/rasterizer/vertex-array.cpp new file mode 100644 index 0000000..de275f0 --- /dev/null +++ b/src/antkeeper/rasterizer/vertex-array.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 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 "rasterizer/vertex-array.hpp" +#include "rasterizer/vertex-buffer.hpp" +#include + +static constexpr GLenum vertex_attribute_type_lut[] = +{ + GL_BYTE, + GL_UNSIGNED_BYTE, + GL_SHORT, + GL_UNSIGNED_SHORT, + GL_INT, + GL_UNSIGNED_INT, + GL_HALF_FLOAT, + GL_FLOAT, + GL_DOUBLE +}; + +vertex_array::vertex_array(): + gl_array_id(0) +{ + glGenVertexArrays(1, &gl_array_id); +} + +vertex_array::~vertex_array() +{ + glDeleteVertexArrays(1, &gl_array_id); +} + +void vertex_array::bind_attribute(unsigned int index, const vertex_buffer& buffer, int size, vertex_attribute_type type, int stride, std::size_t offset) +{ + GLenum gl_type = vertex_attribute_type_lut[static_cast(type)]; + + glBindVertexArray(gl_array_id); + glBindBuffer(GL_ARRAY_BUFFER, buffer.gl_buffer_id); + glVertexAttribPointer(index, size, gl_type, GL_FALSE, stride, (const GLvoid*)offset); + glEnableVertexAttribArray(index); +} + +void vertex_array::bind_elements(const vertex_buffer& buffer) +{ + glBindVertexArray(gl_array_id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.gl_buffer_id); +} + diff --git a/src/antkeeper/rasterizer/vertex-array.hpp b/src/antkeeper/rasterizer/vertex-array.hpp new file mode 100644 index 0000000..0224262 --- /dev/null +++ b/src/antkeeper/rasterizer/vertex-array.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 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_VERTEX_ARRAY_HPP +#define ANTKEEPER_VERTEX_ARRAY_HPP + +#include + +class rasterizer; +class vertex_buffer; +enum class vertex_attribute_type; + +class vertex_array +{ +public: + explicit vertex_array(); + ~vertex_array(); + + vertex_array(const vertex_array&) = delete; + vertex_array& operator=(const vertex_array&) = delete; + + void bind_attribute(unsigned int index, const vertex_buffer& buffer, int size, vertex_attribute_type type, int stride, std::size_t offset); + void bind_elements(const vertex_buffer& buffer); + +private: + friend class rasterizer; + + unsigned int gl_array_id; +}; + +#endif // ANTKEEPER_VERTEX_ARRAY_HPP + diff --git a/src/antkeeper/rasterizer/vertex-attribute-type.hpp b/src/antkeeper/rasterizer/vertex-attribute-type.hpp new file mode 100644 index 0000000..8cb7b9b --- /dev/null +++ b/src/antkeeper/rasterizer/vertex-attribute-type.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 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_VERTEX_ATTRIBUTE_TYPE_HPP +#define ANTKEEPER_VERTEX_ATTRIBUTE_TYPE_HPP + +enum class vertex_attribute_type +{ + int_8, + uint_8, + int_16, + uint_16, + int_32, + uint_32, + float_16, + float_32, + float_64 +}; + +#endif // ANTKEEPER_VERTEX_ATTRIBUTE_TYPE_HPP + diff --git a/src/antkeeper/rasterizer/vertex-buffer.cpp b/src/antkeeper/rasterizer/vertex-buffer.cpp new file mode 100644 index 0000000..e44d722 --- /dev/null +++ b/src/antkeeper/rasterizer/vertex-buffer.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 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 "rasterizer/vertex-buffer.hpp" +#include + +static constexpr GLenum buffer_usage_lut[] = +{ + GL_STREAM_DRAW, + GL_STREAM_READ, + GL_STREAM_COPY, + GL_STATIC_DRAW, + GL_STATIC_READ, + GL_STATIC_COPY, + GL_DYNAMIC_DRAW, + GL_DYNAMIC_READ, + 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) +{ + GLenum gl_usage = buffer_usage_lut[static_cast(usage)]; + + glGenBuffers(1, &gl_buffer_id); + glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); + glBufferData(GL_ARRAY_BUFFER, size, data, gl_usage); +} + +vertex_buffer::vertex_buffer(): + vertex_buffer(0, nullptr, buffer_usage::static_draw) +{} + +vertex_buffer::~vertex_buffer() +{ + glDeleteBuffers(1, &gl_buffer_id); +} + +void vertex_buffer::repurpose(std::size_t size, const void* data, buffer_usage usage) +{ + this->size = size; + this->usage = usage; + + GLenum gl_usage = buffer_usage_lut[static_cast(usage)]; + + glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); + glBufferData(GL_ARRAY_BUFFER, size, data, gl_usage); +} + +void vertex_buffer::resize(std::size_t size, const void* data) +{ + repurpose(size, data, usage); +} + +void vertex_buffer::update(int offset, std::size_t size, const void* data) +{ + glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); + glBufferSubData(GL_ARRAY_BUFFER, offset, size, data); +} + diff --git a/src/antkeeper/rasterizer/vertex-buffer.hpp b/src/antkeeper/rasterizer/vertex-buffer.hpp new file mode 100644 index 0000000..5eda254 --- /dev/null +++ b/src/antkeeper/rasterizer/vertex-buffer.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 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_VERTEX_BUFFER_HPP +#define ANTKEEPER_VERTEX_BUFFER_HPP + +#include +#include "rasterizer/buffer-usage.hpp" + +class vertex_array; + +class vertex_buffer +{ +public: + explicit vertex_buffer(std::size_t size, const void* data = nullptr, buffer_usage usage = buffer_usage::static_draw); + vertex_buffer(); + ~vertex_buffer(); + + vertex_buffer(const vertex_buffer&) = delete; + vertex_buffer& operator=(const vertex_buffer&) = delete; + + void repurpose(std::size_t size, const void* data = nullptr, buffer_usage usage = buffer_usage::static_draw); + void resize(std::size_t size, const void* data = nullptr); + + void update(int offset, std::size_t size, const void* data); + + std::size_t get_size() const; + buffer_usage get_usage() const; + +private: + friend class vertex_array; + + unsigned int gl_buffer_id; + std::size_t size; + buffer_usage usage; +}; + +inline std::size_t vertex_buffer::get_size() const +{ + return size; +} + +inline buffer_usage vertex_buffer::get_usage() const +{ + return usage; +} + +#endif // ANTKEEPER_VERTEX_BUFFER_HPP + diff --git a/src/antkeeper/renderer/compositor.cpp b/src/antkeeper/renderer/compositor.cpp new file mode 100644 index 0000000..98ef822 --- /dev/null +++ b/src/antkeeper/renderer/compositor.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 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 "renderer/compositor.hpp" +#include "renderer/render-pass.hpp" + +void compositor::add_pass(render_pass* pass) +{ + passes.push_back(pass); +} + +void compositor::remove_pass(render_pass* pass) +{ + passes.remove(pass); +} + +void compositor::remove_passes() +{ + passes.clear(); +} + +void compositor::composite(render_context* context) const +{ + for (const render_pass* pass: passes) + { + if (pass->is_enabled()) + { + pass->render(context); + } + } +} + diff --git a/src/antkeeper/renderer/compositor.hpp b/src/antkeeper/renderer/compositor.hpp new file mode 100644 index 0000000..a246950 --- /dev/null +++ b/src/antkeeper/renderer/compositor.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 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_COMPOSITOR_HPP +#define ANTKEEPER_COMPOSITOR_HPP + +#include + +class render_pass; +struct render_context; + +/** + * + */ +class compositor +{ +public: + void add_pass(render_pass* pass); + void remove_pass(render_pass* pass); + void remove_passes(); + + void composite(render_context* context) const; + + const std::list* get_passes() const; + +private: + std::list passes; +}; + +inline const std::list* compositor::get_passes() const +{ + return &passes; +} + +#endif // ANTKEEPER_COMPOSITOR_HPP + diff --git a/src/antkeeper/renderer/material-flags.hpp b/src/antkeeper/renderer/material-flags.hpp new file mode 100644 index 0000000..7aa89c8 --- /dev/null +++ b/src/antkeeper/renderer/material-flags.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 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_MATERIAL_FLAGS_HPP +#define ANTKEEPER_MATERIAL_FLAGS_HPP + +#define MATERIAL_FLAG_OPAQUE 0x00 +#define MATERIAL_FLAG_TRANSLUCENT 0x01 +#define MATERIAL_FLAG_FRONT_FACES 0x00 +#define MATERIAL_FLAG_BACK_FACES 0x02 +#define MATERIAL_FLAG_FRONT_AND_BACK_FACES 0x04 +#define MATERIAL_FLAG_SHADOW_CASTER 0x00 +#define MATERIAL_FLAG_NOT_SHADOW_CASTER 0x08 +#define MATERIAL_FLAG_X_RAY 0x10 +#define MATERIAL_FLAG_OUTLINE 0x20 +#define MATERIAL_FLAG_VEGETATION 0x40 +#define MATERIAL_FLAG_WIREFRAME 0x80000000 + +#endif // ANTKEEPER_MATERIAL_FLAGS_HPP + diff --git a/src/antkeeper/renderer/material-property.cpp b/src/antkeeper/renderer/material-property.cpp new file mode 100644 index 0000000..816e31c --- /dev/null +++ b/src/antkeeper/renderer/material-property.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 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 "renderer/material-property.hpp" +#include "rasterizer/shader-input.hpp" + +material_property_base::material_property_base(): + input(nullptr) +{} + +bool material_property_base::connect(const shader_input* input) +{ + if (!input || input->get_data_type() != get_data_type()) + { + return false; + } + + this->input = input; + + return true; +} + +void material_property_base::disconnect() +{ + this->input = nullptr; +} + diff --git a/src/antkeeper/renderer/material-property.hpp b/src/antkeeper/renderer/material-property.hpp new file mode 100644 index 0000000..6fdefe7 --- /dev/null +++ b/src/antkeeper/renderer/material-property.hpp @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2020 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_MATERIAL_PROPERTY_HPP +#define ANTKEEPER_MATERIAL_PROPERTY_HPP + +#include "rasterizer/shader-variable-type.hpp" +#include "rasterizer/shader-input.hpp" +#include +#include + +using namespace vmq::types; + +class material; +class shader_program; +class texture_2d; +class texture_cube; + +/** + * 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 shader_input* input); + + /** + * Disconnects the material property from its shader input. + */ + void disconnect(); + + /** + * Uploads the material property to its shader program. + * + * @return `true` if the property was uploaded successfully, `false` otherwise. + */ + virtual bool upload() const = 0; + + /** + * Returns the type of data which the property contains. + */ + virtual 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 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: + /** + * 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::upload() const + virtual bool upload() 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); + + /// @copydoc material_property_base::get_data_type() const + virtual 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; + T* values; +}; + +template +material_property::material_property(std::size_t element_count): + element_count(element_count), + values(nullptr) +{ + values = new T[element_count]; +} + +template +material_property::~material_property() +{ + delete[] values; +} + +template +bool material_property::upload() const +{ + if (!is_connected()) + { + return false; + } + + if (element_count > 1) + { + return input->upload(0, values, element_count); + } + else + { + return input->upload(values[0]); + } +} + +template +void material_property::set_value(const T& value) +{ + values[0] = value; +} + +template +void material_property::set_value(std::size_t index, const T& value) +{ + values[index] = 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] = values[i]; + } +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::bool1; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::bool2; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::bool3; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::bool4; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::int1; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::int2; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::int3; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::int4; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::uint1; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::uint2; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::uint3; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::uint4; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::float1; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::float2; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::float3; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::float4; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::float2x2; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::float3x3; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::float4x4; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return shader_variable_type::texture_2d; +} + +template <> +inline shader_variable_type material_property::get_data_type() const +{ + return 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] = values[i]; + } + property->input = input; + + return property; +} + +#endif // ANTKEEPER_MATERIAL_PROPERTY_HPP + diff --git a/src/antkeeper/renderer/material.cpp b/src/antkeeper/renderer/material.cpp new file mode 100644 index 0000000..a65aaa0 --- /dev/null +++ b/src/antkeeper/renderer/material.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2020 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 "renderer/material.hpp" +#include "rasterizer/shader-program.hpp" + +material::material(shader_program* program): + program(program), + flags(0) +{} + +material::material(): + material(nullptr) +{} + +material::material(const material& other) +{ + *this = other; +} + +material::~material() +{ + for (material_property_base* property: properties) + { + delete property; + } +} + +material& material::operator=(const material& other) +{ + // Remove all properties + for (material_property_base* property: properties) + { + delete property; + } + properties.clear(); + property_map.clear(); + + this->program = other.program; + this->flags = other.flags; + 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; + } + + return *this; +} + +std::size_t material::upload() const +{ + if (!program) + { + return false; + } + + std::size_t failed_upload_count = 0; + + for (material_property_base* property: properties) + { + if (!property->upload()) + { + ++failed_upload_count; + } + } + + return failed_upload_count; +} + +void material::set_shader_program(shader_program* program) +{ + this->program = program; + reconnect_properties(); +} + +void material::set_flags(std::uint32_t flags) +{ + this->flags = flags; +} + +std::size_t material::reconnect_properties() +{ + std::size_t disconnected_property_count = properties.size(); + + for (auto it = property_map.begin(); it != property_map.end(); ++it) + { + material_property_base* property = it->second; + + property->disconnect(); + + if (program != nullptr) + { + if (property->connect(program->get_input(it->first))) + { + --disconnected_property_count; + } + } + } + + return disconnected_property_count; +} + diff --git a/src/antkeeper/renderer/material.hpp b/src/antkeeper/renderer/material.hpp new file mode 100644 index 0000000..6eaae30 --- /dev/null +++ b/src/antkeeper/renderer/material.hpp @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2020 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_MATERIAL_HPP +#define ANTKEEPER_MATERIAL_HPP + +#include "renderer/material-property.hpp" +#include "rasterizer/shader-program.hpp" +#include +#include +#include +#include +#include + +/** + * A material is associated with exactly one shader program and contains a set of material properties which can be uploaded to that shader program via shader inputs. + */ +class material +{ +public: + /** + * Creates a material. + * + * @param program Shader program with which to associate this material. + */ + explicit material(shader_program* program); + + /** + * Creates a material. + */ + material(); + + /** + * Creates a copy of another material. + * + * @param other Material to copy. + */ + material(const material& other); + + /** + * Destroys a material. + */ + ~material(); + + /** + * Makes this material a copy of aother material. + * + * @param other Material to copy. + * @return Reference to this material. + */ + material& operator=(const material& other); + + /** + * Uploads each material property to the material's shader program. + * + * @return Number of material property uploads which failed. + */ + std::size_t upload() const; + + /** + * Sets the material's shader program and reconnects all shader properties to their corresponding shader inputs. + * + * @param program Shader program with which to associate the material. + */ + void set_shader_program(shader_program* program); + + /** + * Sets the material flags. + * + * @param flags Material flags. + */ + void set_flags(std::uint32_t flags); + + /** + * Adds a material array property to the material. + * + * @param name Name of the material array property. + * @param element_count Number of elements in the array. + * @return Pointer to the added material property. + */ + template + material_property* add_property(const std::string& name, std::size_t element_count = 1); + + /** + * Returns the shader program with which this material is associated. + */ + shader_program* get_shader_program() const; + + /** + * Returns the material flags. + */ + std::uint32_t get_flags() const; + + /** + * 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. + */ + const std::list* get_properties() const; + +private: + /** + * Attempts to reconnect all material properties to their corresponding shader inputs. + * + * @return Number of disconnected properties. + */ + std::size_t reconnect_properties(); + + shader_program* program; + std::uint32_t flags; + std::list properties; + std::map property_map; +}; + +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 shader_program* material::get_shader_program() const +{ + return program; +} + +inline std::uint32_t material::get_flags() const +{ + return flags; +} + +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; +} + +#endif // ANTKEEPER_MATERIAL_HPP + diff --git a/src/antkeeper/renderer/model.cpp b/src/antkeeper/renderer/model.cpp new file mode 100644 index 0000000..cef7d27 --- /dev/null +++ b/src/antkeeper/renderer/model.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2020 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 "renderer/model.hpp" +#include "rasterizer/drawing-mode.hpp" + +model::model(): + bounds({0, 0, 0}, {0, 0, 0}) +{} + +model::~model() +{ + for (model_group* group: groups) + { + delete group; + } +} + +model_group* model::add_group(const std::string& name) +{ + if (!name.empty()) + { + if (auto it = group_map.find(name); it != group_map.end()) + { + return it->second; + } + } + + model_group* group = new model_group(); + group->index = groups.size(); + group->name = name; + group->material = nullptr; + group->drawing_mode = drawing_mode::triangles; + group->start_index = 0; + group->index_count = 0; + + groups.push_back(group); + + if (!name.empty()) + { + group_map[name] = group; + } + + return group; +} + +bool model::remove_group(const std::string& name) +{ + if (auto it = group_map.find(name); it != group_map.end()) + { + return remove_group(it->second); + } + + return false; +} + +bool model::remove_group(model_group* group) +{ + // Remove from group map + if (!group->name.empty()) + { + if (auto it = group_map.find(group->name); it != group_map.end()) + { + group_map.erase(it); + } + } + + // Adjust indices of groups after this group + for (std::size_t i = group->index + 1; i < groups.size(); ++i) + { + --groups[i]->index; + } + + // 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()) + { + return it->second; + } + + return nullptr; +} + +model_group* model::get_group(const std::string& name) +{ + if (auto it = group_map.find(name); it != group_map.end()) + { + return it->second; + } + + return nullptr; +} + + diff --git a/src/antkeeper/renderer/model.hpp b/src/antkeeper/renderer/model.hpp new file mode 100644 index 0000000..286deaa --- /dev/null +++ b/src/antkeeper/renderer/model.hpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2020 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_MODEL_HPP +#define ANTKEEPER_MODEL_HPP + +#include "rasterizer/vertex-array.hpp" +#include "rasterizer/vertex-buffer.hpp" +#include "geometry/aabb.hpp" +#include +#include +#include + +class material; +class skeleton; +class vertex_array; +class vertex_buffer; +enum class drawing_mode; + +/** + * Part of a model which is associated with exactly one material. + */ +class model_group +{ +public: + void set_material(material* material); + void set_drawing_mode(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(); + 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; + drawing_mode drawing_mode; + std::size_t start_index; + std::size_t index_count; +}; + +inline void model_group::set_material(::material* material) +{ + this->material = material; +} + +inline void model_group::set_drawing_mode(::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 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: + model(); + ~model(); + + void set_bounds(const aabb& bounds); + + model_group* add_group(const std::string& name = std::string()); + + bool remove_group(const std::string& name); + bool remove_group(model_group* group); + + const aabb& 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 vertex_array* get_vertex_array() const; + vertex_array* get_vertex_array(); + + const vertex_buffer* get_vertex_buffer() const; + vertex_buffer* get_vertex_buffer(); + +private: + aabb bounds; + std::vector groups; + std::map group_map; + vertex_array vao; + vertex_buffer vbo; + skeleton* skeleton; +}; + +inline void model::set_bounds(const aabb& bounds) +{ + this->bounds = bounds; +} + +inline const aabb& model::get_bounds() const +{ + return bounds; +} + +inline const std::vector* model::get_groups() const +{ + return &groups; +} + +inline const vertex_array* model::get_vertex_array() const +{ + return &vao; +} + +inline vertex_array* model::get_vertex_array() +{ + return &vao; +} + +inline const vertex_buffer* model::get_vertex_buffer() const +{ + return &vbo; +} + +inline vertex_buffer* model::get_vertex_buffer() +{ + return &vbo; +} + +#endif // ANTKEEPER_MODEL_HPP + diff --git a/src/antkeeper/renderer/passes/bloom-pass.cpp b/src/antkeeper/renderer/passes/bloom-pass.cpp new file mode 100644 index 0000000..630f99a --- /dev/null +++ b/src/antkeeper/renderer/passes/bloom-pass.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2020 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 "renderer/passes/bloom-pass.hpp" +#include "resources/resource-manager.hpp" +#include "rasterizer/rasterizer.hpp" +#include "rasterizer/framebuffer.hpp" +#include "rasterizer/shader-program.hpp" +#include "rasterizer/shader-input.hpp" +#include "rasterizer/vertex-buffer.hpp" +#include "rasterizer/vertex-array.hpp" +#include "rasterizer/vertex-attribute-type.hpp" +#include "rasterizer/drawing-mode.hpp" +#include "rasterizer/texture-2d.hpp" +#include "rasterizer/texture-wrapping.hpp" +#include "rasterizer/texture-filter.hpp" +#include "renderer/vertex-attributes.hpp" +#include "renderer/render-context.hpp" +#include +#include +#include + +using namespace vmq; + +bloom_pass::bloom_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager): + render_pass(rasterizer, framebuffer), + source_texture(nullptr), + brightness_threshold(1.0f), + blur_iterations(1) +{ + // Create clone of framebuffer texture + const texture_2d* framebuffer_texture = framebuffer->get_color_attachment(); + auto dimensions = framebuffer_texture->get_dimensions(); + auto pixel_type = framebuffer_texture->get_pixel_type(); + auto pixel_format = framebuffer_texture->get_pixel_format(); + auto wrapping = framebuffer_texture->get_wrapping(); + auto filters = framebuffer_texture->get_filters(); + float max_anisotropy = framebuffer_texture->get_max_anisotropy(); + cloned_framebuffer_texture = new texture_2d(std::get<0>(dimensions), std::get<1>(dimensions), pixel_type, pixel_format); + cloned_framebuffer_texture->set_wrapping(std::get<0>(wrapping), std::get<1>(wrapping)); + cloned_framebuffer_texture->set_filters(std::get<0>(filters), std::get<1>(filters)); + cloned_framebuffer_texture->set_max_anisotropy(max_anisotropy); + + // Create clone of framebuffer + cloned_framebuffer = new ::framebuffer(std::get<0>(dimensions), std::get<1>(dimensions)); + cloned_framebuffer->attach(framebuffer_attachment_type::color, cloned_framebuffer_texture); + + // Setup pingponging + pingpong_textures[0] = framebuffer_texture; + pingpong_textures[1] = cloned_framebuffer_texture; + pingpong_framebuffers[0] = framebuffer; + pingpong_framebuffers[1] = cloned_framebuffer; + + // Load brightness threshold shader + threshold_shader = resource_manager->load("brightness-threshold.glsl"); + threshold_shader_image_input = threshold_shader->get_input("image"); + threshold_shader_resolution_input = threshold_shader->get_input("resolution"); + threshold_shader_threshold_input = threshold_shader->get_input("threshold"); + + // Load blur shader + blur_shader = resource_manager->load("blur.glsl"); + blur_shader_image_input = blur_shader->get_input("image"); + blur_shader_resolution_input = blur_shader->get_input("resolution"); + blur_shader_direction_input = blur_shader->get_input("direction"); + + const float 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 + }; + + std::size_t vertex_size = 3; + std::size_t vertex_stride = sizeof(float) * vertex_size; + std::size_t vertex_count = 6; + + quad_vbo = new vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); + quad_vao = new vertex_array(); + quad_vao->bind_attribute(VERTEX_POSITION_LOCATION, *quad_vbo, 3, vertex_attribute_type::float_32, vertex_stride, 0); +} + +bloom_pass::~bloom_pass() +{ + delete cloned_framebuffer; + delete cloned_framebuffer_texture; + delete quad_vao; + delete quad_vbo; +} + +void bloom_pass::render(render_context* context) const +{ + rasterizer->use_framebuffer(*framebuffer); + + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + // Determine viewport based on framebuffer resolution + auto viewport = framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); + float2 resolution = {std::get<0>(viewport), std::get<1>(viewport)}; + + // Perform brightness threshold subpass, rendering to the first pingpong fbo + rasterizer->use_framebuffer(*pingpong_framebuffers[0]); + rasterizer->use_program(*threshold_shader); + threshold_shader_image_input->upload(source_texture); + threshold_shader_resolution_input->upload(resolution); + threshold_shader_threshold_input->upload(brightness_threshold); + rasterizer->draw_arrays(*quad_vao, drawing_mode::triangles, 0, 6); + + // Perform iterative blur subpass + const float2 direction_horizontal = {1, 0}; + const float2 direction_vertical = {0, 1}; + rasterizer->use_program(*blur_shader); + blur_shader_resolution_input->upload(resolution); + for (int i = 0; i < blur_iterations; ++i) + { + // Perform horizontal blur + rasterizer->use_framebuffer(*pingpong_framebuffers[1]); + blur_shader_image_input->upload(pingpong_textures[0]); + blur_shader_direction_input->upload(direction_horizontal); + rasterizer->draw_arrays(*quad_vao, drawing_mode::triangles, 0, 6); + + // Perform vertical blur + rasterizer->use_framebuffer(*pingpong_framebuffers[0]); + blur_shader_image_input->upload(pingpong_textures[1]); + blur_shader_direction_input->upload(direction_vertical); + rasterizer->draw_arrays(*quad_vao, drawing_mode::triangles, 0, 6); + } +} + +void bloom_pass::set_source_texture(const texture_2d* texture) +{ + this->source_texture = texture; +} + +void bloom_pass::set_brightness_threshold(float threshold) +{ + this->brightness_threshold = threshold; +} + +void bloom_pass::set_blur_iterations(int iterations) +{ + this->blur_iterations = iterations; +} + diff --git a/src/antkeeper/renderer/passes/bloom-pass.hpp b/src/antkeeper/renderer/passes/bloom-pass.hpp new file mode 100644 index 0000000..46d9034 --- /dev/null +++ b/src/antkeeper/renderer/passes/bloom-pass.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 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_BLOOM_PASS_HPP +#define ANTKEEPER_BLOOM_PASS_HPP + +#include "renderer/render-pass.hpp" +#include + +using namespace vmq::types; + +class shader_program; +class shader_input; +class vertex_buffer; +class vertex_array; +class texture_2d; +class resource_manager; + +/** + * + */ +class bloom_pass: public render_pass +{ +public: + bloom_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager); + virtual ~bloom_pass(); + virtual void render(render_context* context) const final; + + void set_source_texture(const texture_2d* texture); + void set_brightness_threshold(float threshold); + void set_blur_iterations(int iterations); + +private: + vertex_buffer* quad_vbo; + vertex_array* quad_vao; + + const ::framebuffer* pingpong_framebuffers[2]; + const texture_2d* pingpong_textures[2]; + texture_2d* cloned_framebuffer_texture; + ::framebuffer* cloned_framebuffer; + + shader_program* threshold_shader; + const shader_input* threshold_shader_image_input; + const shader_input* threshold_shader_resolution_input; + const shader_input* threshold_shader_threshold_input; + + shader_program* blur_shader; + const shader_input* blur_shader_image_input; + const shader_input* blur_shader_resolution_input; + const shader_input* blur_shader_direction_input; + + const texture_2d* source_texture; + float brightness_threshold; + int blur_iterations; +}; + +#endif // ANTKEEPER_BLOOM_PASS_HPP + diff --git a/src/antkeeper/renderer/passes/clear-pass.cpp b/src/antkeeper/renderer/passes/clear-pass.cpp new file mode 100644 index 0000000..ce7411e --- /dev/null +++ b/src/antkeeper/renderer/passes/clear-pass.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 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 "renderer/passes/clear-pass.hpp" +#include "rasterizer/rasterizer.hpp" +#include "rasterizer/framebuffer.hpp" +#include + +clear_pass::clear_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer): + render_pass(rasterizer, framebuffer), + clear_color_buffer(false), + clear_depth_buffer(false), + clear_stencil_buffer(false), + clear_color({0.0f, 0.0f, 0.0f, 0.0f}), + clear_depth(1.0f), + clear_stencil(0) +{} + +clear_pass::~clear_pass() +{} + +void clear_pass::render(render_context* context) const +{ + 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(GL_TRUE); + + 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); +} + +void clear_pass::set_cleared_buffers(bool color, bool depth, bool stencil) +{ + clear_color_buffer = color; + clear_depth_buffer = depth; + clear_stencil_buffer = stencil; +} + +void clear_pass::set_clear_color(const float4& color) +{ + clear_color = color; +} + +void clear_pass::set_clear_depth(float depth) +{ + clear_depth = depth; +} + +void clear_pass::set_clear_stencil(int stencil) +{ + clear_stencil = stencil; +} diff --git a/src/antkeeper/renderer/passes/clear-pass.hpp b/src/antkeeper/renderer/passes/clear-pass.hpp new file mode 100644 index 0000000..fa8d496 --- /dev/null +++ b/src/antkeeper/renderer/passes/clear-pass.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 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_CLEAR_PASS_HPP +#define ANTKEEPER_CLEAR_PASS_HPP + +#include "renderer/render-pass.hpp" +#include + +using namespace vmq::types; + +/** + * Clears the color, depth, or stencil buffer of a render target. + */ +class clear_pass: public render_pass +{ +public: + clear_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer); + virtual ~clear_pass(); + virtual void render(render_context* context) const final; + + void set_cleared_buffers(bool color, bool depth, bool stencil); + + void set_clear_color(const float4& color); + void set_clear_depth(float depth); + void set_clear_stencil(int stencil); + +private: + bool clear_color_buffer; + bool clear_depth_buffer; + bool clear_stencil_buffer; + float4 clear_color; + float clear_depth; + int clear_stencil; +}; + +#endif // ANTKEEPER_CLEAR_PASS_HPP + diff --git a/src/antkeeper/renderer/passes/final-pass.cpp b/src/antkeeper/renderer/passes/final-pass.cpp new file mode 100644 index 0000000..65a409d --- /dev/null +++ b/src/antkeeper/renderer/passes/final-pass.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 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 "renderer/passes/final-pass.hpp" +#include "resources/resource-manager.hpp" +#include "rasterizer/rasterizer.hpp" +#include "rasterizer/framebuffer.hpp" +#include "rasterizer/shader-program.hpp" +#include "rasterizer/shader-input.hpp" +#include "rasterizer/vertex-buffer.hpp" +#include "rasterizer/vertex-array.hpp" +#include "rasterizer/vertex-attribute-type.hpp" +#include "rasterizer/drawing-mode.hpp" +#include "rasterizer/texture-2d.hpp" +#include "rasterizer/texture-wrapping.hpp" +#include "rasterizer/texture-filter.hpp" +#include "renderer/vertex-attributes.hpp" +#include "renderer/render-context.hpp" +#include +#include +#include + +using namespace vmq; + +final_pass::final_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager): + render_pass(rasterizer, framebuffer), + color_texture(nullptr), + bloom_texture(nullptr) +{ + shader_program = resource_manager->load<::shader_program>("final.glsl"); + color_texture_input = shader_program->get_input("color_texture"); + bloom_texture_input = shader_program->get_input("bloom_texture"); + resolution_input = shader_program->get_input("resolution"); + + const float 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 + }; + + std::size_t vertex_size = 3; + std::size_t vertex_stride = sizeof(float) * vertex_size; + std::size_t vertex_count = 6; + + quad_vbo = new vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); + quad_vao = new vertex_array(); + quad_vao->bind_attribute(VERTEX_POSITION_LOCATION, *quad_vbo, 3, vertex_attribute_type::float_32, vertex_stride, 0); +} + +final_pass::~final_pass() +{ + delete quad_vao; + delete quad_vbo; +} + +void final_pass::render(render_context* context) const +{ + rasterizer->use_framebuffer(*framebuffer); + + 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)); + + 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); + bloom_texture_input->upload(bloom_texture); + resolution_input->upload(resolution); + + // Draw quad + rasterizer->draw_arrays(*quad_vao, drawing_mode::triangles, 0, 6); +} + +void final_pass::set_color_texture(const texture_2d* texture) +{ + this->color_texture = texture; +} + +void final_pass::set_bloom_texture(const texture_2d* texture) +{ + this->bloom_texture = texture; +} + diff --git a/src/antkeeper/renderer/passes/final-pass.hpp b/src/antkeeper/renderer/passes/final-pass.hpp new file mode 100644 index 0000000..2bdefd5 --- /dev/null +++ b/src/antkeeper/renderer/passes/final-pass.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 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_FINAL_PASS_HPP +#define ANTKEEPER_FINAL_PASS_HPP + +#include "renderer/render-pass.hpp" +#include + +using namespace vmq::types; + +class shader_program; +class shader_input; +class vertex_buffer; +class vertex_array; +class texture_2d; +class resource_manager; + +/** + * + */ +class final_pass: public render_pass +{ +public: + final_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager); + virtual ~final_pass(); + virtual void render(render_context* context) const final; + + void set_color_texture(const texture_2d* texture); + void set_bloom_texture(const texture_2d* texture); + +private: + shader_program* shader_program; + const shader_input* color_texture_input; + const shader_input* bloom_texture_input; + const shader_input* resolution_input; + vertex_buffer* quad_vbo; + vertex_array* quad_vao; + + const texture_2d* color_texture; + const texture_2d* bloom_texture; +}; + +#endif // ANTKEEPER_FINAL_PASS_HPP + diff --git a/src/antkeeper/renderer/passes/material-pass.cpp b/src/antkeeper/renderer/passes/material-pass.cpp new file mode 100644 index 0000000..6694db6 --- /dev/null +++ b/src/antkeeper/renderer/passes/material-pass.cpp @@ -0,0 +1,545 @@ +/* + * Copyright (C) 2020 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 "renderer/passes/material-pass.hpp" +#include "configuration.hpp" +#include "resources/resource-manager.hpp" +#include "rasterizer/rasterizer.hpp" +#include "rasterizer/framebuffer.hpp" +#include "rasterizer/shader.hpp" +#include "rasterizer/shader-type.hpp" +#include "rasterizer/shader-program.hpp" +#include "rasterizer/shader-input.hpp" +#include "rasterizer/vertex-buffer.hpp" +#include "rasterizer/vertex-array.hpp" +#include "rasterizer/vertex-attribute-type.hpp" +#include "rasterizer/drawing-mode.hpp" +#include "rasterizer/texture-2d.hpp" +#include "rasterizer/texture-wrapping.hpp" +#include "rasterizer/texture-filter.hpp" +#include "renderer/vertex-attributes.hpp" +#include "renderer/material-flags.hpp" +#include "renderer/model.hpp" +#include "renderer/render-context.hpp" +#include "scene/camera.hpp" +#include "scene/scene.hpp" +#include "scene/ambient-light.hpp" +#include "scene/directional-light.hpp" +#include "scene/point-light.hpp" +#include "scene/spotlight.hpp" +#include "scene/scene.hpp" +#include "configuration.hpp" +#include +#include +#include +#include + +#include "shadow-map-pass.hpp" + +using namespace vmq; + +static bool operation_compare(const render_operation& a, const render_operation& b); + +material_pass::material_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager): + render_pass(rasterizer, framebuffer), + fallback_material(nullptr), + time_tween(nullptr), + focal_point_tween(nullptr), + shadow_map_pass(nullptr), + shadow_map(nullptr) +{ + soft_shadows_texture = resource_manager->load("tree-shadow.png"); + soft_shadows_texture->set_wrapping(texture_wrapping::clamp, texture_wrapping::clamp); + soft_shadows_texture->set_filters(texture_min_filter::linear_mipmap_linear, texture_mag_filter::linear); + + 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_spotlight_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]; + spotlight_colors = new float3[max_spotlight_count]; + spotlight_positions = new float3[max_spotlight_count]; + spotlight_directions = new float3[max_spotlight_count]; + spotlight_attenuations = new float3[max_spotlight_count]; + spotlight_cutoffs = new float2[max_spotlight_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[] spotlight_colors; + delete[] spotlight_positions; + delete[] spotlight_directions; + delete[] spotlight_attenuations; + delete[] spotlight_cutoffs; +} + +void material_pass::render(render_context* context) const +{ + rasterizer->use_framebuffer(*framebuffer); + + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); + glDepthFunc(GL_LESS); + 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)); + + float time = (time_tween) ? time_tween->interpolate(context->alpha) : 0.0f; + float3 focal_point = (focal_point_tween) ? focal_point_tween->interpolate(context->alpha) : float3{0, 0, 0}; + float4x4 view = context->camera->get_view_tween().interpolate(context->alpha); + float4x4 projection = context->camera->get_projection_tween().interpolate(context->alpha); + float4x4 view_projection = projection * view; + float4x4 model_view_projection; + float4x4 model; + float4x4 model_view; + float3x3 normal_model_view; + + + + + + int active_material_flags = 0; + const ::shader_program* active_shader_program = nullptr; + const ::material* active_material = nullptr; + const parameter_set* parameters = nullptr; + + // Reset light counts + ambient_light_count = 0; + point_light_count = 0; + directional_light_count = 0; + spotlight_count = 0; + + // Collect lights + const std::list* lights = context->scene->get_objects(light::object_type_id); + for (const scene_object_base* object: *lights) + { + // Skip inactive lights + if (!object->is_active()) + continue; + + const ::light* light = static_cast(object); + switch (light->get_light_type()) + { + // Add ambient light + case light_type::ambient: + { + if (ambient_light_count < max_ambient_light_count) + { + ambient_light_colors[ambient_light_count] = light->get_scaled_color_tween().interpolate(context->alpha); + ++ambient_light_count; + } + break; + } + + // Add point light + case light_type::point: + { + if (point_light_count < max_point_light_count) + { + point_light_colors[point_light_count] = light->get_scaled_color_tween().interpolate(context->alpha); + + // Transform position into view-space + float3 position = light->get_transform_tween().interpolate(context->alpha).translation; + float3 view_space_position = vmq::resize<3>(view * float4{position.x, position.y, position.z, 1.0f}); + point_light_positions[point_light_count] = view_space_position; + + point_light_attenuations[point_light_count] = static_cast(light)->get_attenuation_tween().interpolate(context->alpha); + ++point_light_count; + } + break; + } + + // Add directional light + case light_type::directional: + { + if (directional_light_count < max_directional_light_count) + { + directional_light_colors[directional_light_count] = light->get_scaled_color_tween().interpolate(context->alpha); + + // Transform direction into view-space + float3 direction = static_cast(light)->get_direction_tween().interpolate(context->alpha); + float3 view_space_direction = vmq::normalize(vmq::resize<3>(view * vmq::resize<4>(-direction))); + directional_light_directions[directional_light_count] = view_space_direction; + + ++directional_light_count; + } + break; + } + + // Add spotlight + case light_type::spot: + { + if (spotlight_count < max_spotlight_count) + { + spotlight_colors[spotlight_count] = light->get_scaled_color_tween().interpolate(context->alpha); + + // Transform position into view-space + float3 position = light->get_transform_tween().interpolate(context->alpha).translation; + float3 view_space_position = vmq::resize<3>(view * float4{position.x, position.y, position.z, 1.0f}); + spotlight_positions[spotlight_count] = view_space_position; + + const ::spotlight* spotlight = static_cast(light); + + // Transform direction into view-space + float3 direction = spotlight->get_direction_tween().interpolate(context->alpha); + float3 view_space_direction = vmq::normalize(vmq::resize<3>(view * vmq::resize<4>(-direction))); + spotlight_directions[spotlight_count] = view_space_direction; + + spotlight_attenuations[spotlight_count] = spotlight->get_attenuation_tween().interpolate(context->alpha); + spotlight_cutoffs[spotlight_count] = spotlight->get_cosine_cutoff_tween().interpolate(context->alpha); + + ++spotlight_count; + } + break; + } + + default: + break; + } + } + + float4x4 shadow_map_matrices[4]; + float4 shadow_map_split_distances; + + if (shadow_map_pass) + { + for (int i = 0; i < 4; ++i) + shadow_map_matrices[i] = shadow_map_pass->get_shadow_matrices()[i]; + + // Calculate shadow map split distances + for (int i = 0; i < 4; ++i) + shadow_map_split_distances[i] = shadow_map_pass->get_split_distances()[i + 1]; + } + + // Sort render operations + context->operations.sort(operation_compare); + + for (const render_operation& operation: context->operations) + { + // Get operation material + const ::material* material = operation.material; + if (!material) + { + if (fallback_material) + { + // No material specified, use fallback material + material = fallback_material; + } + else + { + // No material specified and no fallback material, skip operation + continue; + } + } + + // Switch materials if necessary + if (active_material != material) + { + active_material = material; + + // Change rasterizer state according to material flags + std::uint32_t material_flags = active_material->get_flags(); + if (active_material_flags != material_flags) + { + if ((material_flags & MATERIAL_FLAG_TRANSLUCENT) != (active_material_flags & MATERIAL_FLAG_TRANSLUCENT)) + { + if (material_flags & MATERIAL_FLAG_TRANSLUCENT) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + } + else + { + glDisable(GL_BLEND); + } + } + + if ((material_flags & MATERIAL_FLAG_BACK_FACES) != (active_material_flags & MATERIAL_FLAG_BACK_FACES)) + { + if (material_flags & MATERIAL_FLAG_BACK_FACES) + { + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + } + else + { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + } + } + else if ((material_flags & MATERIAL_FLAG_FRONT_AND_BACK_FACES) != (active_material_flags & MATERIAL_FLAG_FRONT_AND_BACK_FACES)) + { + if (material_flags & MATERIAL_FLAG_FRONT_AND_BACK_FACES) + { + glDisable(GL_CULL_FACE); + } + else + { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + } + } + + if ((material_flags & MATERIAL_FLAG_X_RAY) != (active_material_flags & MATERIAL_FLAG_X_RAY)) + { + if (material_flags & MATERIAL_FLAG_X_RAY) + { + glDisable(GL_DEPTH_TEST); + + } + else + { + glEnable(GL_DEPTH_TEST); + } + } + + + active_material_flags = material_flags; + } + + // Switch shaders if necessary + const ::shader_program* shader_program = active_material->get_shader_program(); + if (active_shader_program != shader_program) + { + active_shader_program = shader_program; + if (!active_shader_program) + { + continue; + } + + // 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()) + { + parameters = it->second; + } + else + { + parameters = load_parameter_set(active_shader_program); + } + + // Upload context-dependent shader parameters + if (parameters->time) + parameters->time->upload(time); + 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->spotlight_count) + parameters->spotlight_count->upload(spotlight_count); + if (parameters->spotlight_colors) + parameters->spotlight_colors->upload(0, spotlight_colors, spotlight_count); + if (parameters->spotlight_positions) + parameters->spotlight_positions->upload(0, spotlight_positions, spotlight_count); + if (parameters->spotlight_directions) + parameters->spotlight_directions->upload(0, spotlight_directions, spotlight_count); + if (parameters->spotlight_attenuations) + parameters->spotlight_attenuations->upload(0, spotlight_attenuations, spotlight_count); + if (parameters->spotlight_cutoffs) + parameters->spotlight_cutoffs->upload(0, spotlight_cutoffs, spotlight_count); + if (parameters->soft_shadows) + parameters->soft_shadows->upload(soft_shadows_texture); + if (parameters->focal_point) + parameters->focal_point->upload(focal_point); + if (parameters->shadow_map_matrices) + parameters->shadow_map_matrices->upload(0, shadow_map_matrices, 4); + if (parameters->shadow_map_split_distances) + parameters->shadow_map_split_distances->upload(shadow_map_split_distances); + if (parameters->shadow_map && shadow_map) + parameters->shadow_map->upload(shadow_map); + } + + // Upload material properties to shader + active_material->upload(); + } + + // Calculate operation-dependent parameters + model = operation.transform; + model_view_projection = view_projection * model; + model_view = view * model; + normal_model_view = vmq::transpose(vmq::inverse(vmq::resize<3, 3>(model_view))); + + // 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_view) + parameters->normal_model_view->upload(normal_model_view); + + // 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; +} + +void material_pass::set_time_tween(const tween* time) +{ + this->time_tween = time; +} + +void material_pass::set_focal_point_tween(const tween* focal_point) +{ + this->focal_point_tween = focal_point; +} + +const material_pass::parameter_set* material_pass::load_parameter_set(const 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 = 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_view = program->get_input("normal_model_view"); + 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->spotlight_count = program->get_input("spotlight_count"); + parameters->spotlight_colors = program->get_input("spotlight_colors"); + parameters->spotlight_positions = program->get_input("spotlight_positions"); + parameters->spotlight_directions = program->get_input("spotlight_directions"); + parameters->spotlight_attenuations = program->get_input("spotlight_attenuations"); + parameters->spotlight_cutoffs = program->get_input("spotlight_cutoffs"); + parameters->soft_shadows = program->get_input("soft_shadows"); + parameters->focal_point = program->get_input("focal_point"); + parameters->shadow_map_matrices = program->get_input("shadow_map_matrices"); + parameters->shadow_map_split_distances = program->get_input("shadow_map_split_distances"); + parameters->shadow_map = program->get_input("shadow_map"); + + // 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) +{ + if (!a.material) + return false; + else if (!b.material) + return true; + + // Determine transparency + bool transparent_a = a.material->get_flags() & MATERIAL_FLAG_TRANSLUCENT; + bool transparent_b = b.material->get_flags() & MATERIAL_FLAG_TRANSLUCENT; + + if (transparent_a) + { + if (transparent_b) + { + // A and B are both transparent, render back to front + return (a.depth >= b.depth); + } + else + { + // A is transparent, B is opaque. Render B first + return false; + } + } + 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 + { + // 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()); + } + } + } +} + diff --git a/src/antkeeper/renderer/passes/material-pass.hpp b/src/antkeeper/renderer/passes/material-pass.hpp new file mode 100644 index 0000000..18641db --- /dev/null +++ b/src/antkeeper/renderer/passes/material-pass.hpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2020 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_MATERIAL_PASS_HPP +#define ANTKEEPER_MATERIAL_PASS_HPP + +#include "renderer/render-pass.hpp" +#include "renderer/material.hpp" +#include "animation/tween.hpp" +#include + +class camera; +class shader_program; +class shader_input; +class resource_manager; +class texture_2d; +class shadow_map_pass; + +/** + * Renders scene objects using their material-specified shaders and properties. + */ +class material_pass: public render_pass +{ +public: + material_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager); + virtual ~material_pass(); + virtual void render(render_context* context) const final; + + /// Sets the material to be used when a render operation is missing a material. If no fallback material is specified, render operations without materials will not be processed. + void set_fallback_material(const material* fallback); + + /// Sets the time tween, which is interpolated between updates + void set_time_tween(const tween* time); + + void set_focal_point_tween(const tween* focal_point); + + const ::shadow_map_pass* shadow_map_pass; + const texture_2d* shadow_map; + +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 shader_input* time; + const shader_input* model; + const shader_input* view; + const shader_input* projection; + const shader_input* model_view; + const shader_input* view_projection; + const shader_input* model_view_projection; + const shader_input* normal_model_view; + + const shader_input* ambient_light_count; + const shader_input* ambient_light_colors; + const shader_input* point_light_count; + const shader_input* point_light_colors; + const shader_input* point_light_positions; + const shader_input* point_light_attenuations; + const shader_input* directional_light_count; + const shader_input* directional_light_colors; + const shader_input* directional_light_directions; + const shader_input* spotlight_count; + const shader_input* spotlight_colors; + const shader_input* spotlight_positions; + const shader_input* spotlight_directions; + const shader_input* spotlight_attenuations; + const shader_input* spotlight_cutoffs; + + const shader_input* soft_shadows; + const shader_input* focal_point; + + const shader_input* shadow_map_matrices; + const shader_input* shadow_map_split_distances; + const shader_input* shadow_map; + }; + + const parameter_set* load_parameter_set(const shader_program* program) const; + + mutable std::unordered_map parameter_sets; + const material* fallback_material; + const tween* time_tween; + const tween* focal_point_tween; + texture_2d* soft_shadows_texture; + + int max_ambient_light_count; + int max_point_light_count; + int max_directional_light_count; + int max_spotlight_count; + + mutable int ambient_light_count; + mutable int point_light_count; + mutable int directional_light_count; + mutable int spotlight_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; + float3* spotlight_colors; + float3* spotlight_positions; + float3* spotlight_directions; + float3* spotlight_attenuations; + float2* spotlight_cutoffs; +}; + +#endif // ANTKEEPER_MATERIAL_PASS_HPP + diff --git a/src/antkeeper/renderer/passes/shadow-map-pass.cpp b/src/antkeeper/renderer/passes/shadow-map-pass.cpp new file mode 100644 index 0000000..acb5108 --- /dev/null +++ b/src/antkeeper/renderer/passes/shadow-map-pass.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2020 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 "renderer/passes/shadow-map-pass.hpp" +#include "resources/resource-manager.hpp" +#include "rasterizer/rasterizer.hpp" +#include "rasterizer/framebuffer.hpp" +#include "rasterizer/shader-program.hpp" +#include "rasterizer/shader-input.hpp" +#include "rasterizer/drawing-mode.hpp" +#include "renderer/render-context.hpp" +#include "renderer/material.hpp" +#include "renderer/material-flags.hpp" +#include "scene/camera.hpp" +#include "scene/scene.hpp" +#include "scene/light.hpp" +#include "scene/directional-light.hpp" +#include "geometry/view-frustum.hpp" +#include "geometry/aabb.hpp" +#include "configuration.hpp" +#include +#include +#include +#include + +using namespace vmq; + +static bool operation_compare(const render_operation& a, const render_operation& b); + +void shadow_map_pass::distribute_frustum_splits(float* split_distances, std::size_t split_count, float split_scheme, float near, float far) +{ + // Calculate split distances + for (std::size_t i = 0; i < split_count; ++i) + { + float part = static_cast(i + 1) / static_cast(split_count + 1); + + // Calculate uniform split distance + float uniform_split_distance = near + (far - near) * part; + + // Calculate logarithmic split distance + float log_split_distance = near * std::pow(far / near, part); + + // Interpolate between uniform and logarithmic split distances + split_distances[i] = log_split_distance * split_scheme + uniform_split_distance * (1.0f - split_scheme); + } +} + +shadow_map_pass::shadow_map_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager): + render_pass(rasterizer, framebuffer), + split_scheme_weight(0.5f) +{ + // Load skinned shader program + unskinned_shader_program = resource_manager->load<::shader_program>("depth-unskinned.glsl"); + unskinned_model_view_projection_input = unskinned_shader_program->get_input("model_view_projection"); + + // Load unskinned shader program + skinned_shader_program = resource_manager->load<::shader_program>("depth-skinned.glsl"); + skinned_model_view_projection_input = skinned_shader_program->get_input("model_view_projection"); + + // Calculate bias-tile matrices + float4x4 bias_matrix = vmq::translate(vmq::identity4x4, float3{0.5f, 0.5f, 0.5f}) * vmq::scale(vmq::identity4x4, float3{0.5f, 0.5f, 0.5f}); + float4x4 tile_scale = vmq::scale(vmq::identity4x4, float3{0.5f, 0.5f, 1.0f}); + for (int i = 0; i < 4; ++i) + { + float x = static_cast(i % 2) * 0.5f; + float y = static_cast(i / 2) * 0.5f; + float4x4 tile_matrix = vmq::translate(vmq::identity4x4, float3{x, y, 0.0f}) * tile_scale; + bias_tile_matrices[i] = tile_matrix * bias_matrix; + } +} + +shadow_map_pass::~shadow_map_pass() +{} + +void shadow_map_pass::render(render_context* context) const +{ + rasterizer->use_framebuffer(*framebuffer); + + // Disable blending + glDisable(GL_BLEND); + + // Enable depth testing + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); + + // Disable face culling + glDisable(GL_CULL_FACE); + + // Get camera + const ::camera& camera = *context->camera; + + // Find the first active directional light + const std::list* lights = context->scene->get_objects(light::object_type_id); + const ::directional_light* light = nullptr; + for (const scene_object_base* object: *lights) + { + // Skip inactive lights + if (!object->is_active()) + continue; + + if (static_cast(object)->get_light_type() == light_type::directional) + { + light = static_cast(object); + break; + } + } + + // Abort if no directional light was found + if (!light) + { + return; + } + + // Calculate distances to the depth clipping planes of each frustum split + float clip_near = camera.get_clip_near_tween().interpolate(context->alpha); + float clip_far = camera.get_clip_far_tween().interpolate(context->alpha); + split_distances[0] = clip_near; + split_distances[4] = clip_far; + distribute_frustum_splits(&split_distances[1], 3, split_scheme_weight, clip_near, clip_far); + + // Calculate viewports for each shadow map + const int shadow_map_resolution = std::get<0>(framebuffer->get_dimensions()) / 2; + float4 shadow_map_viewports[4]; + for (int i = 0; i < 4; ++i) + { + int x = i % 2; + int y = i / 2; + + float4& viewport = shadow_map_viewports[i]; + viewport[0] = static_cast(x * shadow_map_resolution); + viewport[1] = static_cast(y * shadow_map_resolution); + viewport[2] = static_cast(shadow_map_resolution); + viewport[3] = static_cast(shadow_map_resolution); + } + + // Calculate a view-projection matrix from the directional light's transform + transform light_transform = light->get_transform_tween().interpolate(context->alpha); + float3 forward = light_transform.rotation * global_forward; + float3 up = light_transform.rotation * global_up; + float4x4 light_view = vmq::look_at(light_transform.translation, light_transform.translation + forward, up); + float4x4 light_projection = vmq::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); + float4x4 light_view_projection = light_projection * light_view; + + // Get the camera's view matrix + float4x4 camera_view = camera.get_view_tween().interpolate(context->alpha); + + float4x4 crop_matrix; + float4x4 cropped_view_projection; + float4x4 model_view_projection; + + // Sort render operations + context->operations.sort(operation_compare); + + shader_program* active_shader_program = nullptr; + + for (int i = 0; i < 4; ++i) + { + // Set viewport for this shadow map + const float4& viewport = shadow_map_viewports[i]; + rasterizer->set_viewport(viewport[0], viewport[1], viewport[2], viewport[3]); + + // Calculate projection matrix for view camera subfrustum + const float subfrustum_near = split_distances[i]; + const float subfrustum_far = split_distances[i + 1]; + float4x4 subfrustum_projection = vmq::perspective(camera.get_fov(), camera.get_aspect_ratio(), subfrustum_near, subfrustum_far); + + // Calculate view camera subfrustum + view_frustum subfrustum(subfrustum_projection * camera_view); + + // Create AABB containing the view camera subfrustum corners + const std::array, 8>& subfrustum_corners = subfrustum.get_corners(); + aabb subfrustum_aabb = {subfrustum_corners[0], subfrustum_corners[0]}; + for (int j = 1; j < 8; ++j) + { + for (int k = 0; k < 3; ++k) + { + subfrustum_aabb.min_point[k] = std::min(subfrustum_aabb.min_point[k], subfrustum_corners[j][k]); + subfrustum_aabb.max_point[k] = std::max(subfrustum_aabb.max_point[k], subfrustum_corners[j][k]); + } + } + + // Transform subfrustum AABB into the light clip space + aabb cropping_bounds = aabb::transform(subfrustum_aabb, light_view_projection); + + // Calculate scale + float3 scale; + scale.x = 2.0f / (cropping_bounds.max_point.x - cropping_bounds.min_point.x); + scale.y = 2.0f / (cropping_bounds.max_point.y - cropping_bounds.min_point.y); + scale.z = 1.0f / (cropping_bounds.max_point.z - cropping_bounds.min_point.z); + + // Quantize scale + float scale_quantizer = 64.0f; + scale.x = 1.0f / std::ceil(1.0f / scale.x * scale_quantizer) * scale_quantizer; + scale.y = 1.0f / std::ceil(1.0f / scale.y * scale_quantizer) * scale_quantizer; + + // Calculate offset + float3 offset; + offset.x = (cropping_bounds.max_point.x + cropping_bounds.min_point.x) * scale.x * -0.5f; + offset.y = (cropping_bounds.max_point.y + cropping_bounds.min_point.y) * scale.y * -0.5f; + offset.z = -cropping_bounds.min_point.z * scale.z; + + // Quantize offset + float half_shadow_map_resolution = static_cast(shadow_map_resolution) * 0.5f; + offset.x = std::ceil(offset.x * half_shadow_map_resolution) / half_shadow_map_resolution; + offset.y = std::ceil(offset.y * half_shadow_map_resolution) / half_shadow_map_resolution; + + // Crop the light view-projection matrix + crop_matrix = vmq::translate(vmq::identity4x4, offset) * vmq::scale(vmq::identity4x4, scale); + cropped_view_projection = crop_matrix * light_view_projection; + + // Calculate shadow matrix + shadow_matrices[i] = bias_tile_matrices[i] * cropped_view_projection; + + for (const render_operation& operation: context->operations) + { + // Skip materials which don't cast shadows + const ::material* material = operation.material; + if (material && (material->get_flags() & MATERIAL_FLAG_NOT_SHADOW_CASTER)) + { + continue; + } + + // Switch shader programs if necessary + ::shader_program* shader_program = (operation.pose != nullptr) ? skinned_shader_program : unskinned_shader_program; + if (active_shader_program != shader_program) + { + active_shader_program = shader_program; + rasterizer->use_program(*active_shader_program); + } + + // Calculate model-view-projection matrix + model_view_projection = cropped_view_projection * operation.transform; + + // Upload operation-dependent parameters to shader program + if (active_shader_program == unskinned_shader_program) + { + unskinned_model_view_projection_input->upload(model_view_projection); + } + else if (active_shader_program == skinned_shader_program) + { + skinned_model_view_projection_input->upload(model_view_projection); + } + + // Draw geometry + rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); + } + } +} + +void shadow_map_pass::set_split_scheme_weight(float weight) +{ + split_scheme_weight = weight; +} + +bool operation_compare(const render_operation& a, const render_operation& b) +{ + // Determine transparency + bool skinned_a = (a.pose != nullptr); + bool skinned_b = (b.pose != nullptr); + + if (skinned_a) + { + if (skinned_b) + { + // A and B are both skinned, sort by VAO + return (a.vertex_array < b.vertex_array); + } + else + { + // A is skinned, B is unskinned. Render B first + return false; + } + } + else + { + if (skinned_b) + { + // A is unskinned, B is skinned. Render A first + return true; + } + else + { + // A and B are both unskinned, sort by VAO + return (a.vertex_array < b.vertex_array); + } + } +} diff --git a/src/antkeeper/renderer/passes/shadow-map-pass.hpp b/src/antkeeper/renderer/passes/shadow-map-pass.hpp new file mode 100644 index 0000000..5b03379 --- /dev/null +++ b/src/antkeeper/renderer/passes/shadow-map-pass.hpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 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_SHADOW_MAP_PASS_HPP +#define ANTKEEPER_SHADOW_MAP_PASS_HPP + +#include "renderer/render-pass.hpp" + +#include + +using namespace vmq::types; + +class shader_program; +class shader_input; +class camera; +class resource_manager; + +/** + * + */ +class shadow_map_pass: public render_pass +{ +public: + shadow_map_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager); + virtual ~shadow_map_pass(); + virtual void render(render_context* context) const final; + + /** + * Sets the linear interpolation weight between uniform and logarithmic frustum-splitting schemes. + * + * @param weight Linear interpolation weight between uniform and logarithmic frustum-splitting schemes. A value of `0.0` indicates a uniform split scheme, while `1.0` indicates a logarithmic split scheme. + */ + void set_split_scheme_weight(float weight); + + const float4x4* get_shadow_matrices() const; + const float* get_split_distances() const; + +private: + /** + * Calculates the distances along the depth axis at which a view-frustum should be split, given a frustum-splitting scheme. + * + * @param[out] split_distances Array containing the distances to each split. + * @param split_count Number of times the frustum should be split. + * @param split_scheme Linear interpolation weight between uniform and logarithmic frustum-splitting schemes. A value of `0.0` indicates a uniform split scheme, while `1.0` indicates a logarithmic split scheme. + * @param near Distance to the near clipping plane of the frustum to be split. + * @param far Distance to the far clipping plane of the frustum to be split. + */ + static void distribute_frustum_splits(float* split_distances, std::size_t split_count, float split_scheme, float near, float far); + + shader_program* unskinned_shader_program; + const shader_input* unskinned_model_view_projection_input; + + shader_program* skinned_shader_program; + const shader_input* skinned_model_view_projection_input; + + mutable float split_distances[5]; + mutable float4x4 shadow_matrices[4]; + float4x4 bias_tile_matrices[4]; + float split_scheme_weight; +}; + +inline const float4x4* shadow_map_pass::get_shadow_matrices() const +{ + return shadow_matrices; +} + +inline const float* shadow_map_pass::get_split_distances() const +{ + return split_distances; +} + +#endif // ANTKEEPER_SHADOW_MAP_PASS_HPP + diff --git a/src/antkeeper/renderer/passes/sky-pass.cpp b/src/antkeeper/renderer/passes/sky-pass.cpp new file mode 100644 index 0000000..eae7838 --- /dev/null +++ b/src/antkeeper/renderer/passes/sky-pass.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2020 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 "renderer/passes/sky-pass.hpp" +#include "resources/resource-manager.hpp" +#include "rasterizer/rasterizer.hpp" +#include "rasterizer/framebuffer.hpp" +#include "rasterizer/shader-program.hpp" +#include "rasterizer/shader-input.hpp" +#include "rasterizer/vertex-buffer.hpp" +#include "rasterizer/vertex-array.hpp" +#include "rasterizer/vertex-attribute-type.hpp" +#include "rasterizer/drawing-mode.hpp" +#include "rasterizer/texture-2d.hpp" +#include "rasterizer/texture-wrapping.hpp" +#include "rasterizer/texture-filter.hpp" +#include "renderer/vertex-attributes.hpp" +#include "renderer/render-context.hpp" +#include "scene/camera.hpp" +#include "scene/scene.hpp" +#include "scene/ambient-light.hpp" +#include "scene/directional-light.hpp" +#include "scene/scene.hpp" +#include +#include +#include + +using namespace vmq; + +sky_pass::sky_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager): + render_pass(rasterizer, framebuffer) +{ + shader_program = resource_manager->load<::shader_program>("sky.glsl"); + matrix_input = shader_program->get_input("matrix"); + sun_direction_input = shader_program->get_input("sun_direction"); + sun_angular_radius_input = shader_program->get_input("sun_angular_radius"); + sky_gradient_input = shader_program->get_input("sky_gradient"); + bayer_matrix_input = shader_program->get_input("bayer_matrix"); + + const float 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 + }; + + std::size_t vertex_size = 3; + std::size_t vertex_stride = sizeof(float) * vertex_size; + std::size_t vertex_count = 6; + + quad_vbo = new vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); + quad_vao = new vertex_array(); + quad_vao->bind_attribute(VERTEX_POSITION_LOCATION, *quad_vbo, 3, vertex_attribute_type::float_32, vertex_stride, 0); + + sky_gradient = resource_manager->load("grassland-sky-gradient.png"); + sky_gradient->set_wrapping(texture_wrapping::clamp, texture_wrapping::clamp); + sky_gradient->set_filters(texture_min_filter::linear, texture_mag_filter::linear); + + // Generate Bayer matrix texture + static const char bayer_matrix_data[] = + { + 0, 32, 8, 40, 2, 34, 10, 42, + 48, 16, 56, 24, 50, 18, 58, 26, + 12, 44, 4, 36, 14, 46, 6, 38, + 60, 28, 52, 20, 62, 30, 54, 22, + 3, 35, 11, 43, 1, 33, 9, 41, + 51, 19, 59, 27, 49, 17, 57, 25, + 15, 47, 7, 39, 13, 45, 5, 37, + 63, 31, 55, 23, 61, 29, 53, 21 + }; + bayer_matrix = new texture_2d(8, 8, pixel_type::int_8, pixel_format::r, bayer_matrix_data); + bayer_matrix->set_wrapping(texture_wrapping::repeat, texture_wrapping::repeat); + bayer_matrix->set_filters(texture_min_filter::nearest, texture_mag_filter::nearest); + bayer_matrix->set_max_anisotropy(0.0f); +} + +sky_pass::~sky_pass() +{ + delete bayer_matrix; + delete quad_vao; + delete quad_vbo; +} + +void sky_pass::render(render_context* context) const +{ + rasterizer->use_framebuffer(*framebuffer); + + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDisable(GL_CULL_FACE); + + auto viewport = framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); + + float3 sun_direction = {0, 0, -1}; + float sun_angular_radius = 3.0f * vmq::pi / 180.0f; + + // Find sun direction + const std::list* lights = context->scene->get_objects(light::object_type_id); + for (const scene_object_base* object: *lights) + { + const ::light* light = static_cast(object); + + if (light->get_light_type() == light_type::directional) + { + sun_direction = static_cast(light)->get_direction_tween().interpolate(context->alpha); + break; + } + } + + // Calculate matrix + float4x4 model_view = vmq::resize<4, 4>(vmq::resize<3, 3>(context->camera->get_view_tween().interpolate(context->alpha))); + float4x4 inverse_projection = vmq::inverse(context->camera->get_projection_tween().interpolate(context->alpha)); + float4x4 matrix = vmq::inverse(model_view) * inverse_projection; + + // Change shader program + rasterizer->use_program(*shader_program); + + // Upload shader parameters + matrix_input->upload(matrix); + sun_direction_input->upload(sun_direction); + sun_angular_radius_input->upload(sun_angular_radius); + sky_gradient_input->upload(sky_gradient); + bayer_matrix_input->upload(bayer_matrix); + + // Draw quad + rasterizer->draw_arrays(*quad_vao, drawing_mode::triangles, 0, 6); +} + diff --git a/src/antkeeper/renderer/passes/sky-pass.hpp b/src/antkeeper/renderer/passes/sky-pass.hpp new file mode 100644 index 0000000..1043a72 --- /dev/null +++ b/src/antkeeper/renderer/passes/sky-pass.hpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 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_SKY_PASS_HPP +#define ANTKEEPER_SKY_PASS_HPP + +#include "renderer/render-pass.hpp" + +class shader_program; +class shader_input; +class vertex_buffer; +class vertex_array; +class texture_2d; +class resource_manager; + +/** + * + */ +class sky_pass: public render_pass +{ +public: + sky_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager); + virtual ~sky_pass(); + virtual void render(render_context* context) const final; + +private: + shader_program* shader_program; + const shader_input* matrix_input; + const shader_input* sun_direction_input; + const shader_input* sun_angular_radius_input; + const shader_input* sky_gradient_input; + const shader_input* bayer_matrix_input; + + vertex_buffer* quad_vbo; + vertex_array* quad_vao; + texture_2d* sky_gradient; + texture_2d* bayer_matrix; +}; + +#endif // ANTKEEPER_SKY_PASS_HPP + diff --git a/src/antkeeper/renderer/passes/ui-pass.cpp b/src/antkeeper/renderer/passes/ui-pass.cpp new file mode 100644 index 0000000..79c26b5 --- /dev/null +++ b/src/antkeeper/renderer/passes/ui-pass.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2020 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 "renderer/passes/ui-pass.hpp" +#include "resources/resource-manager.hpp" +#include "rasterizer/rasterizer.hpp" +#include "rasterizer/framebuffer.hpp" +#include "rasterizer/shader.hpp" +#include "rasterizer/shader-type.hpp" +#include "rasterizer/shader-program.hpp" +#include "rasterizer/shader-input.hpp" +#include "rasterizer/vertex-buffer.hpp" +#include "rasterizer/vertex-array.hpp" +#include "rasterizer/vertex-attribute-type.hpp" +#include "rasterizer/drawing-mode.hpp" +#include "rasterizer/texture-2d.hpp" +#include "rasterizer/texture-wrapping.hpp" +#include "rasterizer/texture-filter.hpp" +#include "renderer/vertex-attributes.hpp" +#include "renderer/material-flags.hpp" +#include "renderer/render-context.hpp" +#include "scene/camera.hpp" +#include "scene/scene.hpp" +#include "scene/ambient-light.hpp" +#include "scene/directional-light.hpp" +#include "scene/scene.hpp" +#include "scene/billboard.hpp" +#include +#include +#include + +using namespace vmq; + +ui_pass::ui_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager): + render_pass(rasterizer, framebuffer), + time(0.0f) +{} + +ui_pass::~ui_pass() +{} + +void ui_pass::render(render_context* context) 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 = context->camera->get_view_tween().interpolate(context->alpha); + float4x4 projection = context->camera->get_projection_tween().interpolate(context->alpha); + float4x4 view_projection = projection * view; + float4x4 model_view_projection; + + // Collect billboards + std::list billboards = *context->scene->get_objects(billboard::object_type_id); + + // Sort billboards + + // Rebuild vertex buffer +} + +void ui_pass::set_time(float time) +{ + this->time = time; +} + +const ui_pass::parameter_set* ui_pass::load_parameter_set(const 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; +} diff --git a/src/antkeeper/renderer/passes/ui-pass.hpp b/src/antkeeper/renderer/passes/ui-pass.hpp new file mode 100644 index 0000000..1a65993 --- /dev/null +++ b/src/antkeeper/renderer/passes/ui-pass.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 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_UI_PASS_HPP +#define ANTKEEPER_UI_PASS_HPP + +#include "renderer/render-pass.hpp" +#include "renderer/material.hpp" +#include + +class shader_program; +class shader_input; +class resource_manager; +class texture_2d; + +/** + * + */ +class ui_pass: public render_pass +{ +public: + ui_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer, resource_manager* resource_manager); + virtual ~ui_pass(); + virtual void render(render_context* context) const final; + + void set_time(float time); + +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 shader_input* time; + const shader_input* model_view_projection; + }; + + const parameter_set* load_parameter_set(const shader_program* program) const; + + mutable std::unordered_map parameter_sets; + float time; +}; + +#endif // ANTKEEPER_UI_PASS_HPP + diff --git a/src/antkeeper/renderer/render-context.hpp b/src/antkeeper/renderer/render-context.hpp new file mode 100644 index 0000000..418aa60 --- /dev/null +++ b/src/antkeeper/renderer/render-context.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 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_CONTEXT_HPP +#define ANTKEEPER_RENDER_CONTEXT_HPP + +#include "renderer/render-operation.hpp" +#include "geometry/plane.hpp" +#include "geometry/bounding-volume.hpp" +#include +#include + +class camera; +class scene; + +using namespace vmq::types; +using vmq::transform; + +struct render_context +{ + const camera* camera; + transform camera_transform; + float3 camera_forward; + float3 camera_up; + const bounding_volume* camera_culling_volume; + plane clip_near; + + const scene* scene; + std::list operations; + float alpha; +}; + +#endif // ANTKEEPER_RENDER_CONTEXT_HPP + diff --git a/src/antkeeper/renderer/render-operation.hpp b/src/antkeeper/renderer/render-operation.hpp new file mode 100644 index 0000000..83592e0 --- /dev/null +++ b/src/antkeeper/renderer/render-operation.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 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_OPERATION_HPP +#define ANTKEEPER_RENDER_OPERATION_HPP + +#include +#include + +class pose; +class material; +class vertex_array; +enum class drawing_mode; +using namespace vmq::types; + +/** + * Describes a render operation with a single mesh and single material. + */ +struct render_operation +{ + const pose* pose; + const material* material; + const vertex_array* vertex_array; + drawing_mode drawing_mode; + std::size_t start_index; + std::size_t index_count; + float4x4 transform; + float depth; + std::size_t instance_count; +}; + +#endif // ANTKEEPER_RENDER_OPERATION_HPP + diff --git a/src/antkeeper/renderer/render-pass.cpp b/src/antkeeper/renderer/render-pass.cpp new file mode 100644 index 0000000..dd92528 --- /dev/null +++ b/src/antkeeper/renderer/render-pass.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 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 "renderer/render-pass.hpp" + +render_pass::render_pass(::rasterizer* rasterizer, const ::framebuffer* framebuffer): + rasterizer(rasterizer), + framebuffer(framebuffer), + enabled(true) +{} + +render_pass::~render_pass() +{} + +void render_pass::set_enabled(bool enabled) +{ + this->enabled = enabled; +} + diff --git a/src/antkeeper/renderer/render-pass.hpp b/src/antkeeper/renderer/render-pass.hpp new file mode 100644 index 0000000..61d9d9e --- /dev/null +++ b/src/antkeeper/renderer/render-pass.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 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_PASS_HPP +#define ANTKEEPER_RENDER_PASS_HPP + +class rasterizer; +class framebuffer; +struct render_context; + +/** + * + */ +class render_pass +{ +public: + render_pass(rasterizer* rasterizer, const framebuffer* framebuffer); + virtual ~render_pass(); + + virtual void render(render_context* context) const = 0; + + void set_enabled(bool enabled); + bool is_enabled() const; + +protected: + rasterizer* rasterizer; + const ::framebuffer* framebuffer; + +private: + bool enabled; +}; + +inline bool render_pass::is_enabled() const +{ + return enabled; +} + +#endif // ANTKEEPER_RENDER_PASS_HPP + diff --git a/src/antkeeper/renderer/renderer.cpp b/src/antkeeper/renderer/renderer.cpp new file mode 100644 index 0000000..311545d --- /dev/null +++ b/src/antkeeper/renderer/renderer.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2020 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 "renderer/renderer.hpp" +#include "renderer/render-context.hpp" +#include "renderer/compositor.hpp" +#include "scene/scene.hpp" +#include "scene/camera.hpp" +#include "scene/model-instance.hpp" +#include "scene/billboard.hpp" +#include "scene/lod-group.hpp" +#include "renderer/model.hpp" +#include "rasterizer/drawing-mode.hpp" +#include "configuration.hpp" +#include "math.hpp" +#include +#include + +using namespace vmq::operators; + +renderer::renderer() +{ + // Setup billboard render operation + billboard_op.pose = nullptr; + billboard_op.drawing_mode = drawing_mode::triangles; + billboard_op.vertex_array = nullptr; + billboard_op.start_index = 0; + billboard_op.index_count = 6; + billboard_op.instance_count = 0; +} + +void renderer::render(float alpha, const scene& scene) const +{ + // Get list of all objects in the scene + const std::list* objects = scene.get_objects(); + + // Build list of cameras to be sorted + const std::list* cameras = scene.get_objects(camera::object_type_id); + std::list sorted_cameras; + for (scene_object_base* object: *cameras) + { + sorted_cameras.push_back(static_cast(object)); + } + + // Sort cameras according to their respective compositing indices + sorted_cameras.sort + ( + [](const camera* a, const camera* b) -> bool + { + return a->get_composite_index() < b->get_composite_index(); + } + ); + + // Process cameras in order + for (const camera* camera: sorted_cameras) + { + // Skip cameras with no compositors + const compositor* compositor = camera->get_compositor(); + if (!compositor) + { + continue; + } + + // Setup render context + render_context context; + context.camera = camera; + context.camera_transform = camera->get_transform_tween().interpolate(alpha); + context.camera_forward = context.camera_transform.rotation * global_forward; + context.camera_up = context.camera_transform.rotation * global_up; + context.clip_near = camera->get_view_frustum().get_near(); ///< TODO: tween this + context.scene = &scene; + context.alpha = alpha; + + // Get camera culling volume + context.camera_culling_volume = camera->get_culling_mask(); + if (!context.camera_culling_volume) + context.camera_culling_volume = &camera->get_bounds(); + + // Generate render operations for each visible scene object + for (const scene_object_base* object: *objects) + { + // Skip inactive objects + if (!object->is_active()) + continue; + + // Process object + process_object(context, object); + } + + // Pass render context to the camera's compositor + compositor->composite(&context); + } +} + +void renderer::set_billboard_vao(vertex_array* vao) +{ + billboard_op.vertex_array = vao; +} + +void renderer::process_object(render_context& context, const scene_object_base* object) const +{ + std::size_t type = object->get_object_type_id(); + + if (type == model_instance::object_type_id) + process_model_instance(context, static_cast(object)); + else if (type == billboard::object_type_id) + process_billboard(context, static_cast(object)); + else if (type == lod_group::object_type_id) + process_lod_group(context, static_cast(object)); +} + +void renderer::process_model_instance(render_context& context, const ::model_instance* model_instance) const +{ + const model* model = model_instance->get_model(); + if (!model) + return; + + // Get object culling volume + const bounding_volume* object_culling_volume = model_instance->get_culling_mask(); + if (!object_culling_volume) + object_culling_volume = &model_instance->get_bounds(); + + // Perform view-frustum culling + if (!context.camera_culling_volume->intersects(*object_culling_volume)) + return; + + const std::vector* instance_materials = model_instance->get_materials(); + const std::vector* groups = model->get_groups(); + + for (model_group* group: *groups) + { + render_operation operation; + + // Determine operation material + operation.material = group->get_material(); + if ((*instance_materials)[group->get_index()]) + { + // Override model group material with the instance's material + operation.material = (*instance_materials)[group->get_index()]; + } + + operation.pose = model_instance->get_pose(); + operation.vertex_array = model->get_vertex_array(); + operation.drawing_mode = group->get_drawing_mode(); + operation.start_index = group->get_start_index(); + operation.index_count = group->get_index_count(); + operation.transform = vmq::matrix_cast(model_instance->get_transform_tween().interpolate(context.alpha)); + operation.depth = signed_distance(context.clip_near, vmq::resize<3>(operation.transform[3])); + operation.instance_count = model_instance->get_instance_count(); + + context.operations.push_back(operation); + } +} + +void renderer::process_billboard(render_context& context, const ::billboard* billboard) const +{ + // Get object culling volume + const bounding_volume* object_culling_volume = billboard->get_culling_mask(); + if (!object_culling_volume) + object_culling_volume = &billboard->get_bounds(); + + // Perform view-frustum culling + if (!context.camera_culling_volume->intersects(*object_culling_volume)) + return; + + transform billboard_transform = billboard->get_transform_tween().interpolate(context.alpha); + billboard_op.material = billboard->get_material(); + billboard_op.depth = signed_distance(context.clip_near, vmq::resize<3>(billboard_transform.translation)); + + // Align billboard + if (billboard->get_billboard_type() == billboard_type::spherical) + { + billboard_transform.rotation = vmq::normalize(vmq::look_rotation(context.camera_forward, context.camera_up) * billboard_transform.rotation); + } + else if (billboard->get_billboard_type() == billboard_type::cylindrical) + { + const float3& alignment_axis = billboard->get_alignment_axis(); + float3 look = vmq::normalize(math::project_on_plane(billboard_transform.translation - context.camera_transform.translation, {0.0f, 0.0f, 0.0f}, alignment_axis)); + float3 right = vmq::normalize(vmq::cross(alignment_axis, look)); + look = vmq::cross(right, alignment_axis); + float3 up = vmq::cross(look, right); + billboard_transform.rotation = vmq::normalize(vmq::look_rotation(look, up) * billboard_transform.rotation); + } + + billboard_op.transform = vmq::matrix_cast(billboard_transform); + + context.operations.push_back(billboard_op); +} + +void renderer::process_lod_group(render_context& context, const ::lod_group* lod_group) const +{ + // Select level of detail + std::size_t level = lod_group->select_lod(*context.camera); + + // Process all objects in the group with the selected level of detail + const std::list& objects = lod_group->get_objects(level); + for (const scene_object_base* object: objects) + { + process_object(context, object); + } +} diff --git a/src/antkeeper/renderer/renderer.hpp b/src/antkeeper/renderer/renderer.hpp new file mode 100644 index 0000000..1724b20 --- /dev/null +++ b/src/antkeeper/renderer/renderer.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 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_RENDERER_HPP +#define ANTKEEPER_RENDERER_HPP + +#include "render-operation.hpp" + +struct render_context; +class scene; +class scene_object_base; +class vertex_array; +class model_instance; +class billboard; +class lod_group; + +/* +# Pipeline + +1. A scene containing meshes, lights, and cameras is passed to renderer::render(). +2. Each camera is processed in order of priority. +3. Scene objects are tested for visibility against camera's view frustum. +4. List of visible scene objects are passed to the camera's compositor. +5. Compositor passes the visible scene objects to each render pass +6. Render pass sorts scene objects according to its own rules, then rasterizes to its render target. +*/ + +/** + * + */ +class renderer +{ +public: + renderer(); + + void render(float alpha, const scene& scene) const; + + /** + * Sets the VAO to be used when generating render operations for billboards. + */ + void set_billboard_vao(vertex_array* vao); + +private: + void process_object(render_context& context, const scene_object_base* object) const; + void process_model_instance(render_context& context, const ::model_instance* model_instance) const; + void process_billboard(render_context& context, const ::billboard* billboard) const; + void process_lod_group(render_context& context, const ::lod_group* lod_group) const; + + mutable render_operation billboard_op; +}; + +#endif // ANTKEEPER_RENDERER_HPP + diff --git a/src/antkeeper/renderer/vertex-attributes.hpp b/src/antkeeper/renderer/vertex-attributes.hpp new file mode 100644 index 0000000..d653baf --- /dev/null +++ b/src/antkeeper/renderer/vertex-attributes.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 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_VERTEX_ATTRIBUTES_HPP +#define ANTKEEPER_VERTEX_ATTRIBUTES_HPP + +/// Vertex position (vec3) +#define VERTEX_POSITION_LOCATION 0 + +/// Vertex normal (vec3) +#define VERTEX_NORMAL_LOCATION 1 + +/// Vertex texture coordinates (vec2) +#define VERTEX_TEXCOORD_LOCATION 2 + +/// Vertex tangent (vec4) +#define VERTEX_TANGENT_LOCATION 3 + +/// Vertex bitangent (vec4) +#define VERTEX_BITANGENT_LOCATION 4 + +/// Vertex bone indices (vec4) +#define VERTEX_BONE_INDICES_LOCATION 5 + +/// Vertex bone weights (vec4) +#define VERTEX_BONE_WEIGHTS_LOCATION 6 + +// Vertex color (vec4) +#define VERTEX_COLOR_LOCATION 7 + +/// Vertex barycentric coordinates (vec3) +#define VERTEX_BARYCENTRIC_LOCATION 8 + +#endif // ANTKEEPER_VERTEX_ATTRIBUTES_HPP + diff --git a/src/antkeeper/resources/behavior-tree-loader.cpp b/src/antkeeper/resources/behavior-tree-loader.cpp new file mode 100644 index 0000000..456e602 --- /dev/null +++ b/src/antkeeper/resources/behavior-tree-loader.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2020 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 "resource-loader.hpp" +#include "resource-manager.hpp" +#include "behavior/ebt.hpp" +#include "nlohmann/json.hpp" +#include +#include +#include +#include +#include +#include + +template +void parse_argument(T& value, const std::string& string) +{ + std::istringstream stream(string); + stream >> value; +} + +template <> +void parse_argument(std::string& value, const std::string& string) +{ + value = string; +} + +template +std::function pack_function(T (*function)(ebt::context&, Args...), std::list argv) +{ + //if (argv.size() != sizeof...(Args)) + + // Parse list of strings into tuple of arguments + std::tuple...> arguments; + if constexpr (sizeof...(Args) > 0) + { + std::apply([&argv](auto& element, auto&... elements) + { + parse_argument(element, argv.front()); + argv.pop_front(); + }, + arguments); + } + + return std::bind( + [function, arguments](ebt::context& context) -> ebt::status + { + return std::apply(function, std::tuple_cat(std::make_tuple(context), arguments)); + }, + std::placeholders::_1); +} + +static ebt::node* load_node(const nlohmann::json::const_iterator& json, resource_manager* resource_manager); +static void load_node_child(ebt::decorator_node* node, const nlohmann::json& json, resource_manager* resource_manager); +static void load_node_children(ebt::composite_node* node, const nlohmann::json& json, resource_manager* resource_manager); + +static ebt::node* load_action_node(const nlohmann::json& json, resource_manager* resource_manager) +{ + // Get function name + auto function_it = json.find("function"); + if (function_it == json.end()) + throw std::runtime_error("load_action_node(): Action node has no function."); + std::string function_name = function_it.value().get(); + + // Get argument vector + std::list arguments; + auto arguments_it = json.find("arguments"); + for (auto it = arguments_it.value().cbegin(); it != arguments_it.value().cend(); ++it) + arguments.push_back(it.value().get()); + + ebt::action* action_node = new ebt::action(); + if (function_name == "print") action_node->function = pack_function(ebt::print, arguments); + else if (function_name == "print_eid") action_node->function = pack_function(ebt::print_eid, arguments); + else if (function_name == "warp_to") action_node->function = pack_function(ebt::warp_to, arguments); + + return action_node; +} + +static ebt::node* load_selector_node(const nlohmann::json& json, resource_manager* resource_manager) +{ + ebt::selector* selector_node = new ebt::selector(); + load_node_children(selector_node, json, resource_manager); + return selector_node; +} + +static ebt::node* load_sequence_node(const nlohmann::json& json, resource_manager* resource_manager) +{ + ebt::sequence* sequence_node = new ebt::sequence(); + load_node_children(sequence_node, json, resource_manager); + return sequence_node; +} + +static ebt::node* load_node(const nlohmann::json::const_iterator& json, resource_manager* resource_manager) +{ + static const std::map> node_loaders = + { + {"action", &load_action_node}, + {"selector", &load_selector_node}, + {"sequence", &load_sequence_node} + }; + + auto node_loader = node_loaders.find(json.key()); + if (node_loader == node_loaders.end()) + { + throw std::runtime_error("load_node(): Unknown behavior tree node type \"" + json.key() + "\""); + } + + return node_loader->second(json.value(), resource_manager); +} + +static void load_node_child(ebt::decorator_node* node, const nlohmann::json& json, resource_manager* resource_manager) +{ + auto it = json.find("child"); + node->child = load_node(it.value().cbegin(), resource_manager); +} + +static void load_node_children(ebt::composite_node* node, const nlohmann::json& json, resource_manager* resource_manager) +{ + auto children_it = json.find("children"); + for (auto it = children_it.value().cbegin(); it != children_it.value().cend(); ++it) + { + ebt::node* child = load_node(it.value().begin(), resource_manager); + node->children.push_back(child); + } +} + +template <> +ebt::node* resource_loader::load(resource_manager* resource_manager, std::istream* is) +{ + nlohmann::json json; + (*is) >> json; + + if (json.size() != 1) + { + throw std::runtime_error("resource_loader::load(): Behavior tree must have exactly one root node."); + } + + return load_node(json.cbegin(), resource_manager); +} + diff --git a/src/antkeeper/resources/entity-archetype-loader.cpp b/src/antkeeper/resources/entity-archetype-loader.cpp new file mode 100644 index 0000000..b75fdc7 --- /dev/null +++ b/src/antkeeper/resources/entity-archetype-loader.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2020 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 "resource-loader.hpp" +#include "resource-manager.hpp" +#include "string-table.hpp" +#include "renderer/model.hpp" +#include "entity/components/behavior-component.hpp" +#include "entity/components/collision-component.hpp" +#include "entity/components/terrain-component.hpp" +#include "entity/components/transform-component.hpp" +#include "entity/components/model-component.hpp" +#include "entity/components/nest-component.hpp" +#include "entity/archetype.hpp" +#include "behavior/ebt.hpp" +#include +#include + +using namespace ecs; + +static bool load_behavior_component(archetype& archetype, resource_manager& resource_manager, const std::vector& parameters) +{ + if (parameters.size() != 2) + { + throw std::runtime_error("load_behavior_component(): Invalid parameter count."); + } + + std::string filename = parameters[1]; + behavior_component component; + component.behavior_tree = resource_manager.load(filename); + if (!component.behavior_tree) + { + std::string message = std::string("load_behavior_component(): Failed to load behavior tree \"") + filename + std::string("\""); + throw std::runtime_error(message); + } + + archetype.set(component); + + return true; +} + +static bool load_collision_component(archetype& archetype, resource_manager& resource_manager, const std::vector& parameters) +{ + if (parameters.size() != 2) + { + throw std::runtime_error("load_collision_component(): Invalid parameter count."); + } + + std::string filename = parameters[1]; + collision_component component; + component.mesh = resource_manager.load(filename); + if (!component.mesh) + { + std::string message = std::string("load_collision_component(): Failed to load model \"") + filename + std::string("\""); + throw std::runtime_error(message); + } + + archetype.set(component); + + return true; +} + +static bool load_model_component(archetype& archetype, resource_manager& resource_manager, const std::vector& parameters) +{ + if (parameters.size() != 2) + { + throw std::runtime_error("load_model_component(): Invalid parameter count."); + } + + std::string filename = parameters[1]; + model_component component; + component.model = resource_manager.load(filename); + component.instance_count = 0; + if (!component.model) + { + std::string message = std::string("load_model_component(): Failed to load model \"") + filename + std::string("\""); + throw std::runtime_error(message); + } + + archetype.set(component); + + return true; +} + +static bool load_nest_component(archetype& archetype, const std::vector& parameters) +{ + nest_component component; + archetype.set(component); + + return true; +} + +static bool load_terrain_component(archetype& archetype, const std::vector& parameters) +{ + if (parameters.size() != 4) + { + throw std::runtime_error("load_terrain_component(): Invalid parameter count."); + } + + terrain_component component; + component.subdivisions = std::stoi(parameters[1]); + component.x = std::stoi(parameters[2]); + component.z = std::stoi(parameters[3]); + archetype.set(component); + + return true; +} + +static bool load_transform_component(archetype& archetype, const std::vector& parameters) +{ + if (parameters.size() != 11) + { + throw std::runtime_error("load_transform_component(): Invalid parameter count."); + } + + std::stringstream stream; + for (std::size_t i = 1; i < parameters.size(); ++i) + { + stream << parameters[i]; + if (i < parameters.size() - 1) + stream << ' '; + } + + transform_component component; + stream >> component.transform.translation.x; + stream >> component.transform.translation.y; + stream >> component.transform.translation.z; + stream >> component.transform.rotation.w; + stream >> component.transform.rotation.x; + stream >> component.transform.rotation.y; + stream >> component.transform.rotation.z; + stream >> component.transform.scale.x; + stream >> component.transform.scale.y; + stream >> component.transform.scale.z; + component.warp = true; + + archetype.set(component); + + return true; +} + +static bool load_component(archetype& archetype, resource_manager& resource_manager, const std::vector& parameters) +{ + if (parameters[0] == "behavior") return load_behavior_component(archetype, resource_manager, parameters); + if (parameters[0] == "collision") return load_collision_component(archetype, resource_manager, parameters); + if (parameters[0] == "model") return load_model_component(archetype, resource_manager, parameters); + if (parameters[0] == "nest") return load_nest_component(archetype, parameters); + if (parameters[0] == "terrain") return load_terrain_component(archetype, parameters); + if (parameters[0] == "transform") return load_transform_component(archetype, parameters); + + std::string message = std::string("load_component(): Unknown component type \"") + parameters[0] + std::string("\""); + throw std::runtime_error(message); +} + +template <> +archetype* resource_loader::load(resource_manager* resource_manager, std::istream* is) +{ + ecs::archetype* archetype = new ecs::archetype(resource_manager->get_archetype_registry()); + + // Load string table from input stream + string_table* table = resource_loader::load(resource_manager, is); + + // Ensure table is not empty. + if (!table || table->empty()) + { + delete table; + return nullptr; + } + + // Load components from table rows + for (const string_table_row& row: *table) + { + // Skip empty rows and comments + if (row.empty() || row[0].empty() || row[0][0] == '#') + { + continue; + } + + + load_component(*archetype, *resource_manager, row); + } + + return archetype; +} + diff --git a/src/resources/image-loader.cpp b/src/antkeeper/resources/image-loader.cpp similarity index 73% rename from src/resources/image-loader.cpp rename to src/antkeeper/resources/image-loader.cpp index 9a48a15..c9382f7 100644 --- a/src/resources/image-loader.cpp +++ b/src/antkeeper/resources/image-loader.cpp @@ -1,32 +1,32 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ #include "resource-loader.hpp" #include "stb/stb_image.h" -#include "image.hpp" +#include "resources/image.hpp" #include template <> -Image* ResourceLoader::load(ResourceManager* resourceManager, std::istream* is) +image* resource_loader::load(resource_manager* resource_manager, std::istream* is) { unsigned char* buffer; - std::size_t size; + int size; int width; int height; int channels; @@ -35,7 +35,7 @@ Image* ResourceLoader::load(ResourceManager* resourceManager, std::istrea // Read input stream into buffer is->seekg(0, is->end); - size = is->tellg(); + size = static_cast(is->tellg()); buffer = new unsigned char[size]; is->seekg(0, is->beg); is->read(reinterpret_cast(&buffer[0]), size); @@ -65,10 +65,10 @@ Image* ResourceLoader::load(ResourceManager* resourceManager, std::istrea throw std::runtime_error("STBI failed to load image from memory."); } - Image* image = new Image(); + ::image* image = new ::image(); image->format(static_cast(channels), hdr); image->resize(static_cast(width), static_cast(height)); - std::memcpy(image->getPixels(), pixels, width * height * channels * ((hdr) ? sizeof(float) : sizeof(unsigned char))); + std::memcpy(image->get_pixels(), pixels, width * height * channels * ((hdr) ? sizeof(float) : sizeof(unsigned char))); // Free loaded image data stbi_image_free(pixels); diff --git a/src/resources/image.cpp b/src/antkeeper/resources/image.cpp similarity index 67% rename from src/resources/image.cpp rename to src/antkeeper/resources/image.cpp index 7bd6331..39f3f59 100644 --- a/src/resources/image.cpp +++ b/src/antkeeper/resources/image.cpp @@ -1,25 +1,25 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ #include "image.hpp" -Image::Image(): +image::image(): hdr(false), width(0), height(0), @@ -27,38 +27,38 @@ Image::Image(): pixels(nullptr) {} -Image::~Image() +image::~image() { - freePixels(); + free_pixels(); } -void Image::format(unsigned int channels, bool hdr) +void image::format(unsigned int channels, bool hdr) { if (this->channels == channels && this->hdr == hdr) { return; } - freePixels(); + free_pixels(); this->channels = channels; this->hdr = hdr; - allocatePixels(); + allocate_pixels(); } -void Image::resize(unsigned int width, unsigned int height) +void image::resize(unsigned int width, unsigned int height) { if (this->width == width && this->height == height) { return; } - freePixels(); + free_pixels(); this->width = width; this->height = height; - allocatePixels(); + allocate_pixels(); } -void Image::allocatePixels() +void image::allocate_pixels() { if (hdr) { @@ -70,7 +70,7 @@ void Image::allocatePixels() } } -void Image::freePixels() +void image::free_pixels() { if (pixels != nullptr) { diff --git a/src/resources/image.hpp b/src/antkeeper/resources/image.hpp similarity index 64% rename from src/resources/image.hpp rename to src/antkeeper/resources/image.hpp index b1968b5..bc06d56 100644 --- a/src/resources/image.hpp +++ b/src/antkeeper/resources/image.hpp @@ -1,36 +1,36 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#ifndef IMAGE_HPP -#define IMAGE_HPP +#ifndef ANTKEEPER_IMAGE_HPP +#define ANTKEEPER_IMAGE_HPP /** * Stores basic image data. */ -class Image +class image { public: /// Creates an image. - Image(); + image(); /// Destroys an image. - ~Image(); + ~image(); /** * Changes the format of the image. Existing pixel data will be erased if the format has changed. @@ -49,26 +49,26 @@ public: void resize(unsigned int width, unsigned int height); /// Returns whether or not the image contains HDR data. - bool isHDR() const; + bool is_hdr() const; /// Returns the width of the image, in pixels. - unsigned int getWidth() const; + unsigned int get_width() const; /// Returns the height of the image, in pixels. - unsigned int getHeight() const; + unsigned int get_height() const; /// Returns the number of color channels in the image. - unsigned int getChannels() const; + unsigned int get_channels() const; /// Returns a pointer to the pixel data. - const void* getPixels() const; + const void* get_pixels() const; - /// @copydoc Image::getPixels() const - void* getPixels(); + /// @copydoc image::get_pixels() const + void* get_pixels(); private: - void allocatePixels(); - void freePixels(); + void allocate_pixels(); + void free_pixels(); bool hdr; unsigned int width; @@ -77,35 +77,35 @@ private: void* pixels; }; -inline bool Image::isHDR() const +inline bool image::is_hdr() const { return hdr; } -inline unsigned int Image::getWidth() const +inline unsigned int image::get_width() const { return width; } -inline unsigned int Image::getHeight() const +inline unsigned int image::get_height() const { return height; } -inline unsigned int Image::getChannels() const +inline unsigned int image::get_channels() const { return channels; } -inline const void* Image::getPixels() const +inline const void* image::get_pixels() const { return pixels; } -inline void* Image::getPixels() +inline void* image::get_pixels() { return pixels; } -#endif // IMAGE_HPP +#endif // ANTKEEPER_IMAGE_HPP diff --git a/src/antkeeper/resources/material-loader.cpp b/src/antkeeper/resources/material-loader.cpp new file mode 100644 index 0000000..27ec3c5 --- /dev/null +++ b/src/antkeeper/resources/material-loader.cpp @@ -0,0 +1,423 @@ +/* + * Copyright (C) 2020 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 "renderer/material.hpp" +#include "resource-loader.hpp" +#include "resource-manager.hpp" +#include "rasterizer/shader-variable-type.hpp" +#include "rasterizer/texture-wrapping.hpp" +#include "rasterizer/texture-filter.hpp" +#include "rasterizer/texture-2d.hpp" +#include "string-table.hpp" +#include +#include +#include + +static const std::map texture_wrapping_map = +{ + {"clamp", texture_wrapping::clamp}, + {"repeat", texture_wrapping::repeat}, + {"mirrored_repeat", texture_wrapping::mirrored_repeat}, +}; + +static const std::map texture_min_filter_map = +{ + {"nearest", texture_min_filter::nearest}, + {"linear", texture_min_filter::linear}, + {"nearest_mipmap_nearest", texture_min_filter::nearest_mipmap_nearest}, + {"linear_mipmap_nearest", texture_min_filter::linear_mipmap_nearest}, + {"nearest_mipmap_linear", texture_min_filter::nearest_mipmap_linear}, + {"linear_mipmap_linear", texture_min_filter::linear_mipmap_linear} +}; + +static const std::map texture_mag_filter_map = +{ + {"nearest", texture_mag_filter::nearest}, + {"linear", texture_mag_filter::linear}, +}; + +static bool load_bool_property(material* material, const string_table_row& row, int vector_size, int array_size) +{ + if (row.size() - 4 != vector_size * array_size || vector_size < 1 || vector_size > 4) + { + return false; + } + + std::size_t size = array_size * vector_size; + bool* values = new bool[size]; + for (std::size_t i = 0; i < size; ++i) + { + values[i] = (std::stoi(row[4 + i]) != 0); + } + + if (vector_size == 1) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, values, array_size); + } + else if (vector_size == 2) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + else if (vector_size == 3) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + else if (vector_size == 4) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + + delete[] values; + + return true; +} + +static bool load_int_property(material* material, const string_table_row& row, int vector_size, int array_size) +{ + if (row.size() - 4 != vector_size * array_size || vector_size < 1 || vector_size > 4) + { + return false; + } + + std::size_t size = array_size * vector_size; + int* values = new int[size]; + for (std::size_t i = 0; i < size; ++i) + { + values[i] = std::stoi(row[4 + i]); + } + + if (vector_size == 1) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, values, array_size); + } + else if (vector_size == 2) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + else if (vector_size == 3) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + else if (vector_size == 4) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + + delete[] values; + + return true; +} + +static bool load_uint_property(material* material, const string_table_row& row, int vector_size, int array_size) +{ + if (row.size() - 4 != vector_size * array_size || vector_size < 1 || vector_size > 4) + { + return false; + } + + std::size_t size = array_size * vector_size; + unsigned int* values = new unsigned int[size]; + for (std::size_t i = 0; i < size; ++i) + { + values[i] = static_cast(std::stoul(row[4 + i])); + } + + if (vector_size == 1) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, values, array_size); + } + else if (vector_size == 2) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + else if (vector_size == 3) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + else if (vector_size == 4) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + + delete[] values; + + return true; +} + +static bool load_float_property(material* material, const string_table_row& row, int vector_size, int array_size) +{ + if (row.size() - 4 != vector_size * array_size || vector_size < 1 || vector_size > 4) + { + return false; + } + + std::size_t size = array_size * vector_size; + float* values = new float[size]; + for (std::size_t i = 0; i < size; ++i) + { + values[i] = static_cast(std::stod(row[4 + i])); + } + + if (vector_size == 1) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, values, array_size); + } + else if (vector_size == 2) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + else if (vector_size == 3) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + else if (vector_size == 4) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + + delete[] values; + + return true; +} + +static bool load_float_matrix_property(material* material, const string_table_row& row, int matrix_columns, int matrix_rows, int array_size) +{ + int matrix_size = matrix_columns * matrix_rows; + + if (row.size() - 4 != matrix_size * array_size) + { + return false; + } + + std::size_t size = array_size * matrix_size; + float* values = new float[size]; + for (std::size_t i = 0; i < size; ++i) + { + values[i] = static_cast(std::stod(row[4 + i])); + } + + if (matrix_size == 2*2) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + else if (matrix_size == 3*3) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + else if (matrix_size == 4*4) + { + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, reinterpret_cast(values), array_size); + } + + delete[] values; + + return true; +} + +static bool load_texture_2d_property(material* material, const string_table_row& row, resource_manager* resource_manager, int array_size) +{ + if (row.size() - 4 != array_size * 6) + { + return false; + } + + const texture_2d** values = new const texture_2d*[array_size]; + for (std::size_t i = 0; i < array_size; ++i) + { + std::size_t offset = 4 + i; + + const std::string& filename = row[offset]; + texture_wrapping wrap_s = texture_wrapping::clamp; + texture_wrapping wrap_t = texture_wrapping::clamp; + texture_min_filter min_filter = texture_min_filter::linear_mipmap_linear; + texture_mag_filter mag_filter = texture_mag_filter::linear; + if (auto it = texture_wrapping_map.find(row[offset + 1]); it != texture_wrapping_map.end()) + { + wrap_s = it->second; + } + if (auto it = texture_wrapping_map.find(row[offset + 2]); it != texture_wrapping_map.end()) + { + wrap_t = it->second; + } + if (auto it = texture_min_filter_map.find(row[offset + 3]); it != texture_min_filter_map.end()) + { + min_filter = it->second; + } + if (auto it = texture_mag_filter_map.find(row[offset + 4]); it != texture_mag_filter_map.end()) + { + mag_filter = it->second; + } + float max_anisotropy = static_cast(std::stod(row[offset + 5])); + + texture_2d* texture = resource_manager->load(row[4 + i]); + texture->set_wrapping(wrap_s, wrap_t); + texture->set_filters(min_filter, mag_filter); + texture->set_max_anisotropy(max_anisotropy); + + values[i] = texture; + } + + material_property* property = material->add_property(row[1], array_size); + property->set_values(0, values, array_size); + + delete[] values; + + return true; +} + +static bool load_texture_cube_property(material* material, const string_table_row& row, resource_manager* resource_manager, int array_size) +{ + return false; +} + +static bool load_material_property(material* material, const string_table_row& row, resource_manager* resource_manager) +{ + // Ensure row has at least five columns + if (row.size() < 5) + { + return false; + } + + const std::string& name = row[1]; + if (name.empty()) + { + return false; + } + + const std::string& type = row[2]; + if (type.empty()) + { + return false; + } + + int vector_size = 1; + if (std::isdigit(type.back())) + { + vector_size = std::stoi(type.substr(type.size() - 1, 1)); + } + + int matrix_columns = 0; + int matrix_rows = 0; + if (type[type.size() - 2] == 'x' && std::isdigit(type[type.size() - 3]) && std::isdigit(type.back())) + { + matrix_columns = std::stoi(type.substr(type.size() - 3, 1)); + matrix_rows = std::stoi(type.substr(type.size() - 1, 1)); + } + + int array_size = std::stoi(row[3]); + if (array_size <= 0) + { + return false; + } + + if (type == "bool" || type == "bool2" || type == "bool3" || type == "bool4") + { + return load_bool_property(material, row, vector_size, array_size); + } + else if (type == "int" || type == "int2" || type == "int3" || type == "int4") + { + return load_int_property(material, row, vector_size, array_size); + } + else if (type == "uint" || type == "uint2" || type == "uint3" || type == "uint4") + { + return load_uint_property(material, row, vector_size, array_size); + } + else if (type == "float" || type == "float2" || type == "float3" || type == "float4") + { + return load_float_property(material, row, vector_size, array_size); + } + else if (type == "float2x2" || type == "float3x3" || type == "float4x4") + { + return load_float_matrix_property(material, row, matrix_columns, matrix_rows, array_size); + } + else if (type == "texture_2d") + { + return load_texture_2d_property(material, row, resource_manager, array_size); + } + else if (type == "texture_cube") + { + return load_texture_cube_property(material, row, resource_manager, array_size); + } + + return false; +} + +template <> +material* resource_loader::load(resource_manager* resource_manager, std::istream* is) +{ + // Load string table from input stream + string_table* table = resource_loader::load(resource_manager, is); + + // Ensure table is not empty. + if (!table || table->empty()) + { + delete table; + return nullptr; + } + + // Allocate material + ::material* material = new ::material(); + + // Parse table rows + for (const string_table_row& row: *table) + { + // Skip empty rows and comments + if (row.empty() || row[0].empty() || row[0][0] == '#') + { + continue; + } + + if (row[0] == "shader" && row.size() == 2) + { + shader_program* program = resource_manager->load(row[1]); + material->set_shader_program(program); + } + else if (row[0] == "flags" && row.size() == 2) + { + std::uint32_t flags = std::stoi(row[1]); + material->set_flags(flags); + } + else if (row[0] == "property") + { + load_material_property(material, row, resource_manager); + } + } + + return material; +} + diff --git a/src/resources/triangle-mesh-loader.cpp b/src/antkeeper/resources/mesh-loader.cpp similarity index 55% rename from src/resources/triangle-mesh-loader.cpp rename to src/antkeeper/resources/mesh-loader.cpp index 04cd92c..c2675f2 100644 --- a/src/resources/triangle-mesh-loader.cpp +++ b/src/antkeeper/resources/mesh-loader.cpp @@ -1,35 +1,34 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#include "resource-loader.hpp" +#include "resources/resource-loader.hpp" +#include "geometry/mesh.hpp" +#include "geometry/mesh-functions.hpp" #include #include -#include -using namespace Emergent; - template <> -TriangleMesh* ResourceLoader::load(ResourceManager* resourceManager, std::istream* is) +mesh* resource_loader::load(resource_manager* resource_manager, std::istream* is) { std::string line; - std::vector vertices; - std::vector indices; + std::vector vertices; + std::vector> triangles; while (is->good() && std::getline(*is, line)) { @@ -49,15 +48,15 @@ TriangleMesh* ResourceLoader::load(ResourceManager* resourceManage if (tokens.size() != 4) { std::stringstream stream; - stream << "ResourceLoader::load(): Invalid line \"" << line << "\"" << std::endl; + stream << "resource_loader::load(): Invalid line \"" << line << "\"" << std::endl; throw std::runtime_error(stream.str()); } - Vector3 vertex; + float3 vertex; - std::stringstream(tokens[1]) >> vertex.x; - std::stringstream(tokens[2]) >> vertex.y; - std::stringstream(tokens[3]) >> vertex.z; + std::stringstream(tokens[1]) >> vertex[0]; + std::stringstream(tokens[2]) >> vertex[1]; + std::stringstream(tokens[3]) >> vertex[2]; vertices.push_back(vertex); } @@ -66,26 +65,22 @@ TriangleMesh* ResourceLoader::load(ResourceManager* resourceManage if (tokens.size() != 4) { std::stringstream stream; - stream << "ResourceLoader::load(): Invalid line \"" << line << "\"" << std::endl; + stream << "resource_loader::load(): Invalid line \"" << line << "\"" << std::endl; throw std::runtime_error(stream.str()); } - std::size_t a, b, c; + std::uint_fast32_t a, b, c; std::stringstream(tokens[1]) >> a; std::stringstream(tokens[2]) >> b; std::stringstream(tokens[3]) >> c; - - a -= 1; - b -= 1; - c -= 1; - - indices.push_back(a); - indices.push_back(b); - indices.push_back(c); + triangles.push_back({a - 1, b - 1, c - 1}); } } - return new TriangleMesh(vertices, indices); + mesh* mesh = new ::mesh(); + create_triangle_mesh(*mesh, vertices, triangles); + + return mesh; } diff --git a/src/antkeeper/resources/model-loader.cpp b/src/antkeeper/resources/model-loader.cpp new file mode 100644 index 0000000..7b39dfa --- /dev/null +++ b/src/antkeeper/resources/model-loader.cpp @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2020 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 "resources/resource-loader.hpp" +#include "resources/resource-manager.hpp" +#include "renderer/model.hpp" +#include "renderer/vertex-attributes.hpp" +#include "rasterizer/vertex-attribute-type.hpp" +#include "rasterizer/drawing-mode.hpp" +#include +#include +#include +#include + +using namespace vmq::types; + +struct material_group +{ + std::string name; + material* material; + std::size_t start_index; + std::size_t index_count; +}; + +static const float3 barycentric_coords[3] = +{ + float3{1, 0, 0}, + float3{0, 1, 0}, + float3{0, 0, 1} +}; + +template <> +model* resource_loader::load(resource_manager* resource_manager, std::istream* is) +{ + std::string line; + std::vector positions; + std::vector uvs; + std::vector normals; + std::vector> faces; + std::vector material_groups; + material_group* current_material_group = nullptr; + aabb bounds = + { + {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}, + {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()} + }; + + while (is->good() && std::getline(*is, 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 position; + + std::stringstream(tokens[1]) >> position[0]; + std::stringstream(tokens[2]) >> position[1]; + std::stringstream(tokens[3]) >> position[2]; + + positions.push_back(position); + + // Add position to bounds + for (int i = 0; i < 3; ++i) + { + bounds.min_point[i] = std::min(bounds.min_point[i], position[i]); + bounds.max_point[i] = std::max(bounds.max_point[i], position[i]); + } + } + else if (tokens[0] == "vt") + { + if (tokens.size() != 3) + { + std::stringstream stream; + stream << "resource_loader::load(): Invalid line \"" << line << "\"" << std::endl; + throw std::runtime_error(stream.str()); + } + + float2 uv; + + std::stringstream(tokens[1]) >> uv[0]; + std::stringstream(tokens[2]) >> uv[1]; + + uvs.push_back(uv); + } + else if (tokens[0] == "vn") + { + if (tokens.size() != 4) + { + std::stringstream stream; + stream << "resource_loader::load(): Invalid line \"" << line << "\"" << std::endl; + throw std::runtime_error(stream.str()); + } + + float3 normal; + + std::stringstream(tokens[1]) >> normal[0]; + std::stringstream(tokens[2]) >> normal[1]; + std::stringstream(tokens[3]) >> normal[2]; + + normals.push_back(normal); + } + 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::vector face; + + for (std::size_t i = 0; i < 3; ++i) + { + std::stringstream ss(tokens[i + 1]); + while (ss.good()) + { + std::string substring; + std::getline(ss, substring, '/'); + + if (!substring.empty()) + { + std::size_t index = std::stoul(substring) - 1; + face.push_back(index); + } + } + } + + faces.push_back(face); + } + else if (tokens[0] == "usemtl") + { + if (tokens.size() != 2) + { + std::stringstream stream; + stream << "resource_loader::load(): Invalid line \"" << line << "\"" << std::endl; + throw std::runtime_error(stream.str()); + } + + if (current_material_group) + { + current_material_group->index_count = faces.size() * 3 - current_material_group->start_index; + } + + material_groups.push_back(material_group()); + current_material_group = &material_groups.back(); + current_material_group->name = tokens[1]; + current_material_group->start_index = faces.size() * 3; + current_material_group->index_count = 0; + } + } + + if (current_material_group) + { + current_material_group->index_count = faces.size() * 3 - current_material_group->start_index; + } + + // Load material group materials + for (material_group& material_group: material_groups) + { + material_group.material = resource_manager->load(material_group.name + ".mtl"); + } + + bool has_uvs = (!uvs.empty()); + bool has_normals = (!normals.empty()); + bool has_barycentric = true; + + std::size_t vertex_size = 3; + if (has_uvs) + vertex_size += 2; + if (has_normals) + vertex_size += 3; + if (has_barycentric) + vertex_size += 3; + + std::size_t vertex_stride = sizeof(float) * vertex_size; + + // Generate vertex buffer + float* vertex_data = new float[vertex_size * faces.size() * 3]; + float* v = &vertex_data[0]; + for (std::size_t i = 0; i < faces.size(); ++i) + { + const std::vector& face = faces[i]; + + std::size_t k = 0; + for (std::size_t j = 0; j < 3; ++j) + { + const float3& position = positions[face[k++]]; + *(v++) = position[0]; + *(v++) = position[1]; + *(v++) = position[2]; + + if (has_uvs) + { + const float2& uv = uvs[face[k++]]; + *(v++) = uv[0]; + *(v++) = uv[1]; + } + + if (has_normals) + { + const float3& normal = normals[face[k++]]; + *(v++) = normal[0]; + *(v++) = normal[1]; + *(v++) = normal[2]; + } + + if (has_barycentric) + { + *(v++) = barycentric_coords[j][0]; + *(v++) = barycentric_coords[j][1]; + *(v++) = barycentric_coords[j][2]; + } + } + } + + // Allocate a model + model* model = new ::model(); + model->set_bounds(bounds); + vertex_buffer* vbo = model->get_vertex_buffer(); + vertex_array* vao = model->get_vertex_array(); + vbo->resize(sizeof(float) * vertex_size * faces.size() * 3, vertex_data); + std::size_t offset = 0; + vao->bind_attribute(VERTEX_POSITION_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, 0); + offset += 3; + if (has_uvs) + { + vao->bind_attribute(VERTEX_TEXCOORD_LOCATION, *vbo, 2, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset); + offset += 2; + } + if (has_normals) + { + vao->bind_attribute(VERTEX_NORMAL_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset); + offset += 3; + } + if (has_barycentric) + { + vao->bind_attribute(VERTEX_BARYCENTRIC_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset); + offset += 3; + } + + // Add model groups for each material + for (const material_group& material_group: material_groups) + { + model_group* model_group = model->add_group(material_group.name); + model_group->set_material(material_group.material); + model_group->set_drawing_mode(drawing_mode::triangles); + model_group->set_start_index(material_group.start_index); + model_group->set_index_count(material_group.index_count); + } + + // Deallocate vertex data + delete[] vertex_data; + + return model; +} + diff --git a/src/entity/system.cpp b/src/antkeeper/resources/resource-handle.cpp similarity index 55% rename from src/entity/system.cpp rename to src/antkeeper/resources/resource-handle.cpp index 1688afb..46f9bf1 100644 --- a/src/entity/system.cpp +++ b/src/antkeeper/resources/resource-handle.cpp @@ -1,25 +1,25 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#include "system.hpp" +#include "resources/resource-handle.hpp" -System::System(ComponentManager* componentManager): - componentManager(componentManager) +resource_handle_base::resource_handle_base(): + reference_count(0) {} diff --git a/src/resources/resource-handle.hpp b/src/antkeeper/resources/resource-handle.hpp similarity index 58% rename from src/resources/resource-handle.hpp rename to src/antkeeper/resources/resource-handle.hpp index bff0445..162f761 100644 --- a/src/resources/resource-handle.hpp +++ b/src/antkeeper/resources/resource-handle.hpp @@ -1,70 +1,70 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#ifndef RESOURCE_HANDLE_HPP -#define RESOURCE_HANDLE_HPP +#ifndef ANTKEEPER_RESOURCE_HANDLE_HPP +#define ANTKEEPER_RESOURCE_HANDLE_HPP #include /** * Base class for resource handles. */ -class ResourceHandleBase +class resource_handle_base { public: /// Creates a resource handle base. - ResourceHandleBase(); + resource_handle_base(); /// Destroys a resource handle base. - virtual ~ResourceHandleBase() = default; + virtual ~resource_handle_base() = default; /// Number of times the handle is referenced. - std::size_t referenceCount; + std::size_t reference_count; }; /** * Templated resource handle class. */ template -class ResourceHandle: public ResourceHandleBase +class resource_handle: public resource_handle_base { public: /// Creates a resource handle - ResourceHandle(); + resource_handle(); /// Destroys a resource handle and deletes its data. - virtual ~ResourceHandle(); + virtual ~resource_handle(); /// Pointer to resource data T* data; }; template -ResourceHandle::ResourceHandle(): +resource_handle::resource_handle(): data(nullptr) {} template -ResourceHandle::~ResourceHandle() +resource_handle::~resource_handle() { delete data; } -#endif // RESOURCE_HANDLE_HPP +#endif // ANTKEEPER_RESOURCE_HANDLE_HPP diff --git a/src/resources/resource-loader.hpp b/src/antkeeper/resources/resource-loader.hpp similarity index 70% rename from src/resources/resource-loader.hpp rename to src/antkeeper/resources/resource-loader.hpp index 0bb5ea7..0c9fb78 100644 --- a/src/resources/resource-loader.hpp +++ b/src/antkeeper/resources/resource-loader.hpp @@ -1,20 +1,20 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ #ifndef RESOURCE_LOADER_HPP @@ -23,7 +23,7 @@ #include #include -class ResourceManager; +class resource_manager; /** * Templated resource loader. @@ -31,7 +31,7 @@ class ResourceManager; * @tparam Type of resource which this loader handles. */ template -class ResourceLoader +class resource_loader { public: /** @@ -41,7 +41,7 @@ public: * @param is Input stream containing the resource data. * @return Pointer to the loaded resource. */ - static T* load(ResourceManager* resourceManager, std::istream* is); + static T* load(resource_manager* resourceManager, std::istream* is); /** * Saves resource data. @@ -50,7 +50,7 @@ public: * @param os Output stream which will contain the resource data. * @param resource Pointer to the resource data. */ - static void save(ResourceManager* resourceManager, std::ostream* os, const T* resource); + static void save(resource_manager* resourceManager, std::ostream* os, const T* resource); }; #endif // RESOURCE_LOADER_HPP diff --git a/src/resources/resource-manager.cpp b/src/antkeeper/resources/resource-manager.cpp similarity index 51% rename from src/resources/resource-manager.cpp rename to src/antkeeper/resources/resource-manager.cpp index 87d03c0..11fed76 100644 --- a/src/resources/resource-manager.cpp +++ b/src/antkeeper/resources/resource-manager.cpp @@ -1,56 +1,56 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#include "resource-manager.hpp" +#include "resources/resource-manager.hpp" -ResourceManager::ResourceManager() +resource_manager::resource_manager() {} -ResourceManager::~ResourceManager() +resource_manager::~resource_manager() { - for (auto it = resourceCache.begin(); it != resourceCache.end(); ++it) + for (auto it = resource_cache.begin(); it != resource_cache.end(); ++it) { delete it->second; } } -void ResourceManager::unload(const std::string& path) +void resource_manager::unload(const std::string& path) { // Check if resource is in the cache - auto it = resourceCache.find(path); - if (it != resourceCache.end()) + auto it = resource_cache.find(path); + if (it != resource_cache.end()) { // Decrement the resource handle reference count - --it->second->referenceCount; + --it->second->reference_count; // Free the resource if the resource handle is unreferenced - if (it->second->referenceCount <= 0) + if (it->second->reference_count <= 0) { delete it->second; } // Remove resource from the cache - resourceCache.erase(it); + resource_cache.erase(it); } } -void ResourceManager::include(const std::string& path) +void resource_manager::include(const std::string& path) { paths.push_back(path); } diff --git a/src/resources/resource-manager.hpp b/src/antkeeper/resources/resource-manager.hpp similarity index 63% rename from src/resources/resource-manager.hpp rename to src/antkeeper/resources/resource-manager.hpp index 33083c8..9357f3a 100644 --- a/src/resources/resource-manager.hpp +++ b/src/antkeeper/resources/resource-manager.hpp @@ -1,24 +1,24 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#ifndef RESOURCE_MANAGER_HPP -#define RESOURCE_MANAGER_HPP +#ifndef ANTKEEPER_RESOURCE_MANAGER_HPP +#define ANTKEEPER_RESOURCE_MANAGER_HPP #include "resource-handle.hpp" #include "resource-loader.hpp" @@ -27,22 +27,23 @@ #include #include #include +#include /** * Loads resources. */ -class ResourceManager +class resource_manager { public: /** * Creates a resource manager. */ - ResourceManager(); + resource_manager(); /** * Destroys a resource manager and frees all of its resources. */ - ~ResourceManager(); + ~resource_manager(); /** * Adds a path to be searched when a resource is requested. @@ -78,23 +79,26 @@ public: template void save(const T* resource, const std::string& path); + entt::registry& get_archetype_registry(); + private: - std::map resourceCache; + std::map resource_cache; std::list paths; + entt::registry archetype_registry; }; template -T* ResourceManager::load(const std::string& path) +T* resource_manager::load(const std::string& path) { // Check if resource is in the cache - auto it = resourceCache.find(path); - if (it != resourceCache.end()) + auto it = resource_cache.find(path); + if (it != resource_cache.end()) { // Resource found - ResourceHandle* resource = static_cast*>(it->second); + resource_handle* resource = static_cast*>(it->second); // Increment resource handle reference count - ++resource->referenceCount; + ++resource->reference_count; // Return resource data return resource->data; @@ -109,9 +113,9 @@ T* ResourceManager::load(const std::string& path) for (const std::string& directory: paths) { // Attempt to open file - std::string fullPath = directory + path; + std::string full_path = directory + path; std::ifstream fs; - fs.open(fullPath.c_str(), std::ios::in | std::ios::binary); + fs.open(full_path.c_str(), std::ios::in | std::ios::binary); // If unable to open file if (!fs.is_open() || !fs.good()) @@ -127,35 +131,35 @@ T* ResourceManager::load(const std::string& path) // File opened, load it opened = true; - data = ResourceLoader::load(this, &fs); + data = resource_loader::load(this, &fs); fs.close(); break; } if (!opened) { - throw std::runtime_error("ResourceManager::load(): Unable to open file \"" + path + "\""); + throw std::runtime_error("resource_manager::load(): Unable to open file \"" + path + "\""); } } catch (const std::exception& e) { - std::string error = std::string("ResourceManager::load(): Failed to load resource \"") + path + std::string("\": \"") + e.what() + std::string("\""); + std::string error = std::string("resource_manager::load(): Failed to load resource \"") + path + std::string("\": \"") + e.what() + std::string("\""); throw std::runtime_error(error.c_str()); } // Create a resource handle for the resource data - ResourceHandle* resource = new ResourceHandle(); + resource_handle* resource = new resource_handle(); resource->data = data; - resource->referenceCount = 1; + resource->reference_count = 1; // Add resource to the cache - resourceCache[path] = resource; + resource_cache[path] = resource; return resource->data; } template -void ResourceManager::save(const T* resource, const std::string& path) +void resource_manager::save(const T* resource, const std::string& path) { // Attempt to open file std::ofstream fs; @@ -169,19 +173,24 @@ void ResourceManager::save(const T* resource, const std::string& path) fs.close(); } - throw std::runtime_error("ResourceManager::save(): Unable to open file \"" + path + "\""); + throw std::runtime_error("resource_manager::save(): Unable to open file \"" + path + "\""); } try { - ResourceLoader::save(this, &fs, resource); + resource_loader::save(this, &fs, resource); } catch (const std::exception& e) { - std::string error = std::string("ResourceManager::load(): Failed to save resource \"") + path + std::string("\": \"") + e.what() + std::string("\""); + std::string error = std::string("resource_manager::load(): Failed to save resource \"") + path + std::string("\": \"") + e.what() + std::string("\""); throw std::runtime_error(error.c_str()); } } -#endif // RESOURCE_MANAGER_HPP +inline entt::registry& resource_manager::get_archetype_registry() +{ + return archetype_registry; +} + +#endif // ANTKEEPER_RESOURCE_MANAGER_HPP diff --git a/src/antkeeper/resources/shader-program-loader.cpp b/src/antkeeper/resources/shader-program-loader.cpp new file mode 100644 index 0000000..63a8066 --- /dev/null +++ b/src/antkeeper/resources/shader-program-loader.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2020 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 "resources/resource-loader.hpp" +#include "resources/resource-manager.hpp" +#include "resources/text-file.hpp" +#include "rasterizer/shader-type.hpp" +#include "rasterizer/shader.hpp" +#include "rasterizer/shader-program.hpp" +#include +#include + +/** + * Tokenizes a line of shader source code. + */ +static std::vector tokenize(const std::string& line) +{ + std::vector tokens; + std::string token; + std::istringstream linestream(line); + while (linestream >> token) + tokens.push_back(token); + + return tokens; +} + +/** + * Handles `#pragma include` directives by loading the specified text files and inserting them in place. + */ +static void handle_includes(text_file* source, resource_manager* resource_manager) +{ + // For each line in the source + for (std::size_t i = 0; i < source->size(); ++i) + { + // Tokenize line + std::vector tokens = tokenize((*source)[i]); + + // Look for `#pragma include` directives + if (tokens.size() == 3 && tokens[0] == "#pragma" && tokens[1] == "include") + { + // Get path to include file + std::string path = tokens[2].substr(1, tokens[2].length() - 2); + + // Load include file + if (!resource_manager->load(path)) + { + std::string message = std::string("Failed to load shader include file \"") + path + std::string("\""); + throw std::runtime_error(message.c_str()); + } + text_file include_file = *(resource_manager->load(path)); + + // Handle `#pragma include` directives inside include file + handle_includes(&include_file, resource_manager); + + // Replace #pragma include directive with include file contents + source->erase(source->begin() + i); + source->insert(source->begin() + i, include_file.begin(), include_file.end()); + i += include_file.size() - 1; + } + } +} + +/** + * Injects the `DEBUG` or `NDEBUG` macros after the `#version` directive. + */ +static void inject_debug_macro(text_file* source) +{ + // For each line in the source + for (std::size_t i = 0; i < source->size(); ++i) + { + // Tokenize line + std::vector tokens = tokenize((*source)[i]); + + // Inject DEBUG and NDEBUG macros + if (!tokens.empty() && tokens[0] == "#version") + { + #if defined(NDEBUG) + source->insert(source->begin() + i + 1, "#define NDEBUG"); + #else + source->insert(source->begin() + i + 1, "#define DEBUG"); + #endif + + return; + } + } +} + +/** + * Injects a shader type macro definition after the `#version` directive. + */ +static void inject_shader_type_macro(text_file* source, shader_type type) +{ + const char* vertex_macro = "#define _VERTEX"; + const char* fragment_macro = "#define _FRAGMENT"; + const char* geometry_macro = "#define _GEOMETRY"; + + const char* macro = nullptr; + if (type == shader_type::vertex) + macro = vertex_macro; + else if (type == shader_type::fragment) + macro = fragment_macro; + else if (type == shader_type::geometry) + macro = geometry_macro; + + // For each line in the source + for (std::size_t i = 0; i < source->size(); ++i) + { + // Tokenize line + std::vector tokens = tokenize((*source)[i]); + + // Inject shader type macro + if (!tokens.empty() && tokens[0] == "#version") + { + source->insert(source->begin() + i + 1, macro); + return; + } + } +} + +static std::list detect_shader_types(text_file* source) +{ + std::list types; + + // For each line in the source + for (std::size_t i = 0; i < source->size(); ++i) + { + // Tokenize line + std::vector tokens = tokenize((*source)[i]); + + // Look for `#pragma include` directives + if (tokens.size() == 2 && tokens[0] == "#pragma") + { + if (tokens[1] == "vertex") + { + types.push_back(shader_type::vertex); + } + else if (tokens[1] == "fragment") + { + types.push_back(shader_type::fragment); + } + else if (tokens[1] == "geometry") + { + types.push_back(shader_type::geometry); + } + } + } + + return types; +} + +static std::string generate_source_buffer(const std::vector& source) +{ + std::ostringstream stream; + std::copy(source.begin(), source.end(), std::ostream_iterator(stream, "\n")); + return stream.str(); +} + +template <> +shader_program* resource_loader::load(resource_manager* resource_manager, std::istream* is) +{ + // Allocate shader source + text_file* source = new text_file(); + + // Read input stream into source + while (!is->eof()) + { + // For each line in input stream + std::string line; + std::getline(*is, line); + if (is->bad() || is->fail()) + { + break; + } + + // Add line to the source + source->push_back(line); + } + + // Handle `#pragma include` directives + handle_includes(source, resource_manager); + + // Inject `DEBUG` or `NDEBUG` macro definitions + inject_debug_macro(source); + + // Detect declared shader types via the `#pragma vertex`, `#pragma fragment` and `#pragma geometry` directives + std::list shader_types = detect_shader_types(source); + + // Load detected shaders + std::list shaders; + for (shader_type type: shader_types) + { + text_file type_source = *source; + inject_shader_type_macro(&type_source, type); + std::string source_buffer = generate_source_buffer(type_source); + shaders.push_back(new shader(type, source_buffer)); + } + + // Create shader program + shader_program* program = new shader_program(shaders); + + // Delete shaders + for (shader* shader: shaders) + { + delete shader; + } + + return program; +} + diff --git a/src/resources/string-table-loader.cpp b/src/antkeeper/resources/string-table-loader.cpp similarity index 72% rename from src/resources/string-table-loader.cpp rename to src/antkeeper/resources/string-table-loader.cpp index a74c5c0..e8302e2 100644 --- a/src/resources/string-table-loader.cpp +++ b/src/antkeeper/resources/string-table-loader.cpp @@ -1,26 +1,26 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ #include "resource-loader.hpp" #include "string-table.hpp" -static StringTableRow parseRow(const std::string& line) +static string_table_row parse_row(const std::string& line) { std::vector row; std::string column; @@ -92,9 +92,9 @@ static StringTableRow parseRow(const std::string& line) } template <> -StringTable* ResourceLoader::load(ResourceManager* resourceManager, std::istream* is) +string_table* resource_loader::load(resource_manager* resource_manager, std::istream* is) { - StringTable* table = new StringTable(); + string_table* table = new string_table(); std::string line; while (!is->eof()) @@ -105,18 +105,18 @@ StringTable* ResourceLoader::load(ResourceManager* resourceManager, break; } - table->push_back(parseRow(line)); + table->push_back(parse_row(line)); } return table; } template <> -void ResourceLoader::save(ResourceManager* resourceManager, std::ostream* os, const StringTable* table) +void resource_loader::save(resource_manager* resource_manager, std::ostream* os, const string_table* table) { for (std::size_t i = 0; i < table->size(); ++i) { - const StringTableRow& row = (*table)[i]; + const string_table_row& row = (*table)[i]; for (std::size_t j = 0; j < row.size(); ++j) { diff --git a/src/resources/string-table.cpp b/src/antkeeper/resources/string-table.cpp similarity index 59% rename from src/resources/string-table.cpp rename to src/antkeeper/resources/string-table.cpp index dc98334..a8f29fa 100644 --- a/src/resources/string-table.cpp +++ b/src/antkeeper/resources/string-table.cpp @@ -1,27 +1,27 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#include "string-table.hpp" +#include "resources/string-table.hpp" -StringTableIndex createIndex(const StringTable& table) +string_table_index createIndex(const string_table& table) { - StringTableIndex index; + string_table_index index; for (std::size_t i = 0; i < table.size(); ++i) { diff --git a/src/resources/string-table.hpp b/src/antkeeper/resources/string-table.hpp similarity index 57% rename from src/resources/string-table.hpp rename to src/antkeeper/resources/string-table.hpp index e857712..5acf06c 100644 --- a/src/resources/string-table.hpp +++ b/src/antkeeper/resources/string-table.hpp @@ -1,24 +1,24 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#ifndef STRING_TABLE_HPP -#define STRING_TABLE_HPP +#ifndef ANTKEEPER_STRING_TABLE_HPP +#define ANTKEEPER_STRING_TABLE_HPP #include #include @@ -27,24 +27,24 @@ /** * A single row in a string table. */ -typedef std::vector StringTableRow; +typedef std::vector string_table_row; /** * A table of strings. */ -typedef std::vector StringTable; +typedef std::vector string_table; /** * An index for finding elements in a string table. */ -typedef std::unordered_map StringTableIndex; +typedef std::unordered_map string_table_index; /** * Creates an index for a string table using strings in the first column as keys. * * @param table Table for which an index will be created. */ -StringTableIndex createIndex(const StringTable& table); +string_table_index createIndex(const string_table& table); -#endif // STRING_TABLE_HPP +#endif // ANTKEEPER_STRING_TABLE_HPP diff --git a/src/resources/text-file-loader.cpp b/src/antkeeper/resources/text-file-loader.cpp similarity index 57% rename from src/resources/text-file-loader.cpp rename to src/antkeeper/resources/text-file-loader.cpp index 5c2e916..71f788a 100644 --- a/src/resources/text-file-loader.cpp +++ b/src/antkeeper/resources/text-file-loader.cpp @@ -1,29 +1,29 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#include "resource-loader.hpp" -#include "text-file.hpp" +#include "resources/resource-loader.hpp" +#include "resources/text-file.hpp" template <> -TextFile* ResourceLoader::load(ResourceManager* resourceManager, std::istream* is) +text_file* resource_loader::load(resource_manager* resource_manager, std::istream* is) { - TextFile* file = new TextFile(); + text_file* file = new text_file(); std::string line; while (!is->eof()) diff --git a/src/resources/text-file.hpp b/src/antkeeper/resources/text-file.hpp similarity index 64% rename from src/resources/text-file.hpp rename to src/antkeeper/resources/text-file.hpp index f743924..6341574 100644 --- a/src/resources/text-file.hpp +++ b/src/antkeeper/resources/text-file.hpp @@ -1,20 +1,20 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ #ifndef TEXT_FILE_HPP @@ -23,7 +23,7 @@ #include #include -typedef std::vector TextFile; +typedef std::vector text_file; #endif // TEXT_FILE_HPP diff --git a/src/antkeeper/resources/texture-2d-loader.cpp b/src/antkeeper/resources/texture-2d-loader.cpp new file mode 100644 index 0000000..1ce613b --- /dev/null +++ b/src/antkeeper/resources/texture-2d-loader.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 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 "resources/resource-loader.hpp" +#include "resources/image.hpp" +#include "rasterizer/pixel-type.hpp" +#include "rasterizer/pixel-format.hpp" +#include "rasterizer/texture-2d.hpp" +#include + +template <> +texture_2d* resource_loader::load(resource_manager* resource_manager, std::istream* is) +{ + // Load image + ::image* image = resource_loader<::image>::load(resource_manager, is); + + // Determine pixel type + pixel_type type = (image->is_hdr()) ? pixel_type::float_32 : pixel_type::uint_8; + + // Determine pixel format + pixel_format format; + if (image->get_channels() == 1) + { + format = pixel_format::r; + } + else if (image->get_channels() == 2) + { + format = pixel_format::rg; + } + else if (image->get_channels() == 3) + { + format = pixel_format::rgb; + } + else if (image->get_channels() == 4) + { + format = pixel_format::rgba; + } + else + { + std::stringstream stream; + stream << std::string("Texture cannot be created from an image with an unsupported number of color channels (") << image->get_channels() << std::string(")."); + delete image; + throw std::runtime_error(stream.str().c_str()); + } + + // Create texture + texture_2d* texture = new texture_2d(image->get_width(), image->get_height(), type, format, image->get_pixels()); + + // Free loaded image + delete image; + + return texture; +} + diff --git a/src/antkeeper/scene/ambient-light.hpp b/src/antkeeper/scene/ambient-light.hpp new file mode 100644 index 0000000..c297bac --- /dev/null +++ b/src/antkeeper/scene/ambient-light.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 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_AMBIENT_LIGHT_HPP +#define ANTKEEPER_AMBIENT_LIGHT_HPP + +#include "scene/light.hpp" + +class ambient_light: public light +{ +public: + /// Returns light_type::ambient + virtual light_type get_light_type() const; +}; + +inline light_type ambient_light::get_light_type() const +{ + return light_type::ambient; +} + +#endif // ANTKEEPER_AMBIENT_LIGHT_HPP + diff --git a/src/antkeeper/scene/billboard.cpp b/src/antkeeper/scene/billboard.cpp new file mode 100644 index 0000000..5858335 --- /dev/null +++ b/src/antkeeper/scene/billboard.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 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 "scene/billboard.hpp" +#include "configuration.hpp" +#include + +const aabb billboard::untransformed_bounds = {{-1, -1, -1}, {1, 1, 1}}; + +billboard::billboard(): + bounds(untransformed_bounds), + material(nullptr), + type(billboard_type::flat), + alignment_axis(global_up) +{} + +billboard::billboard(const billboard& other) +{ + *this = other; +} + +billboard& billboard::operator=(const billboard& other) +{ + bounds = other.bounds; + material = other.material; + type = other.type; + alignment_axis = other.alignment_axis; + return *this; +} + +void billboard::set_material(::material* material) +{ + this->material = material; +} + +void billboard::set_billboard_type(billboard_type type) +{ + this->type = type; +} + +void billboard::set_alignment_axis(const float3& axis) +{ + this->alignment_axis = axis; +} + +void billboard::transformed() +{ + bounds = aabb::transform(untransformed_bounds, get_transform()); +} diff --git a/src/antkeeper/scene/billboard.hpp b/src/antkeeper/scene/billboard.hpp new file mode 100644 index 0000000..8004d4b --- /dev/null +++ b/src/antkeeper/scene/billboard.hpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 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_BILLBOARD_HPP +#define ANTKEEPER_BILLBOARD_HPP + +#include "scene/scene-object.hpp" +#include "geometry/aabb.hpp" + +class material; + +/// Enumerates billboard types. +enum class billboard_type +{ + // No alignment + flat, + + // Aligns to face camera + spherical, + + // Rotates about an alignment axis to face camera + cylindrical +}; + +/** + * A 2D unit quad with one material. + */ +class billboard: public scene_object +{ +public: + billboard(); + billboard(const billboard& other); + billboard& operator=(const billboard& other); + + void set_material(material* material); + + /// Sets the billboard alignment mode. + void set_billboard_type(billboard_type type); + + /// 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& get_bounds() const; + + material* get_material() const; + billboard_type get_billboard_type() const; + const float3& get_alignment_axis() const; + +private: + static const aabb untransformed_bounds; + + virtual void transformed(); + + aabb bounds; + material* material; + billboard_type type; + float3 alignment_axis; +}; + +inline const bounding_volume& billboard::get_bounds() const +{ + return bounds; +} + +inline 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; +} + +#endif // ANTKEEPER_BILLBOARD_HPP + diff --git a/src/antkeeper/scene/camera.cpp b/src/antkeeper/scene/camera.cpp new file mode 100644 index 0000000..58b5059 --- /dev/null +++ b/src/antkeeper/scene/camera.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2020 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 "scene/camera.hpp" +#include "configuration.hpp" + +using namespace vmq::operators; + +static float4x4 interpolate_view(const camera* camera, const float4x4& x, const float4x4& y, float a) +{ + transform transform = camera->get_transform_tween().interpolate(a); + float3 forward = transform.rotation * global_forward; + float3 up = transform.rotation * global_up; + return vmq::look_at(transform.translation, transform.translation + forward, up); +} + +static float4x4 interpolate_projection(const camera* camera, const float4x4& x, const float4x4& y, float a) +{ + if (camera->is_orthographic()) + { + return vmq::ortho( + camera->get_clip_left_tween().interpolate(a), + camera->get_clip_right_tween().interpolate(a), + camera->get_clip_bottom_tween().interpolate(a), + camera->get_clip_top_tween().interpolate(a), + camera->get_clip_near_tween().interpolate(a), + camera->get_clip_far_tween().interpolate(a)); + } + else + { + return vmq::perspective( + camera->get_fov_tween().interpolate(a), + camera->get_aspect_ratio_tween().interpolate(a), + camera->get_clip_near_tween().interpolate(a), + camera->get_clip_far_tween().interpolate(a)); + } +} + +static float4x4 interpolate_view_projection(const camera* camera, const float4x4& x, const float4x4& y, float a) +{ + return camera->get_projection_tween().interpolate(a) * camera->get_view_tween().interpolate(a); +} + +camera::camera(): + compositor(nullptr), + composite_index(0), + orthographic(true), + clip_left(-1.0f), + clip_right(1.0f), + clip_bottom(-1.0f), + clip_top(1.0f), + clip_near(-1.0f), + clip_far(1.0f), + fov(vmq::half_pi), + aspect_ratio(1.0f), + view(vmq::identity4x4, std::bind(&interpolate_view, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), + projection(vmq::identity4x4, std::bind(&interpolate_projection, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), + view_projection(vmq::identity4x4, std::bind(&interpolate_view_projection, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)) +{} + +float3 camera::project(const float3& object, const float4& viewport) const +{ + float4 result = view_projection[1] * float4{object[0], object[1], object[2], 1.0f}; + result[0] = (result[0] / result[3]) * 0.5f + 0.5f; + result[1] = (result[1] / result[3]) * 0.5f + 0.5f; + result[2] = (result[2] / result[3]) * 0.5f + 0.5f; + + result[0] = result[0] * viewport[2] + viewport[0]; + result[1] = result[1] * viewport[3] + viewport[1]; + + return vmq::resize<3>(result); +} + +float3 camera::unproject(const float3& window, const float4& viewport) const +{ + float4 result; + result[0] = ((window[0] - viewport[0]) / viewport[2]) * 2.0f - 1.0f; + result[1] = ((window[1] - viewport[1]) / viewport[3]) * 2.0f - 1.0f; + result[2] = window[2] * 2.0f - 1.0f; + result[3] = 1.0f; + + result = vmq::inverse(view_projection[1]) * result; + + return vmq::resize<3>(result) * (1.0f / result[3]); +} + +void camera::set_perspective(float fov, float aspect_ratio, float clip_near, float clip_far) +{ + orthographic = false; + + this->fov[1] = fov; + this->aspect_ratio[1] = aspect_ratio; + this->clip_near[1] = clip_near; + this->clip_far[1] = clip_far; + + projection[1] = vmq::perspective(fov, aspect_ratio, clip_near, clip_far); + + // Recalculate view-projection matrix + view_projection[1] = projection[1] * view[1]; + + // Recalculate view frustum + view_frustum.set_matrix(view_projection[1]); +} + +void camera::set_orthographic(float clip_left, float clip_right, float clip_bottom, float clip_top, float clip_near, float clip_far) +{ + orthographic = true; + + this->clip_left[1] = clip_left; + this->clip_right[1] = clip_right; + this->clip_bottom[1] = clip_bottom; + this->clip_top[1] = clip_top; + this->clip_near[1] = clip_near; + this->clip_far[1] = clip_far; + + projection[1] = vmq::ortho(clip_left, clip_right, clip_bottom, clip_top, clip_near, clip_far); + + // Recalculate view-projection matrix + view_projection[1] = projection[1] * view[1]; + + // Recalculate view frustum + view_frustum.set_matrix(view_projection[1]); +} + +void camera::set_compositor(::compositor* compositor) +{ + this->compositor = compositor; +} + +void camera::set_composite_index(int index) +{ + composite_index = index; +} + +void camera::update_tweens() +{ + scene_object_base::update_tweens(); + clip_left.update(); + clip_right.update(); + clip_bottom.update(); + clip_top.update(); + clip_near.update(); + clip_far.update(); + fov.update(); + aspect_ratio.update(); + view.update(); + projection.update(); + view_projection.update(); +} + +void camera::transformed() +{ + // Recalculate view and view-projection matrices + float3 forward = get_rotation() * global_forward; + float3 up = get_rotation() * global_up; + view[1] = vmq::look_at(get_translation(), get_translation() + forward, up); + view_projection[1] = projection[1] * view[1]; + + // Recalculate view frustum + view_frustum.set_matrix(view_projection[1]); +} + diff --git a/src/antkeeper/scene/camera.hpp b/src/antkeeper/scene/camera.hpp new file mode 100644 index 0000000..11173f4 --- /dev/null +++ b/src/antkeeper/scene/camera.hpp @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2020 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_CAMERA_HPP +#define ANTKEEPER_CAMERA_HPP + +#include "scene/scene-object.hpp" +#include "geometry/view-frustum.hpp" + +class compositor; + +/** + * + */ +class camera: public scene_object +{ +public: + camera(); + + /** + * Maps object coordinates to window coordinates. + * + * @param object Object coordinates. + * @param viewport Vector containing the `x`, `y`, `w`, and `h` of the viewport. + * @return Projected window coordinates. + */ + float3 project(const float3& object, const float4& viewport) const; + + /** + * Maps window coordinates to object coordinates. + * + * @param window Window coordinates. + * @param viewport Vector containing the `x`, `y`, `w`, and `h` of the viewport. + * @return Unprojected object coordinates. + */ + float3 unproject(const float3& window, const float4& viewport) const; + + /** + * Sets the camera's projection matrix using perspective projection. + * + * @param fov Vertical field of view. + * @param aspect_ratio Aspect ratio. + * @param clip_near Distance to near clipping plane. + * @param clip_far Distance to far clipping plane. + */ + void set_perspective(float fov, float aspect_ratio, float clip_near, float clip_far); + + /** + * Sets the camera's projection matrix using orthographic projection. + * + * @param clip_left Signed distance to left clipping plane. + * @param clip_right Signed distance to right clipping plane. + * @param clip_bottom Signed distance to bottom clipping plane. + * @param clip_top Signed distance to top clipping plane. + * @param clip_near Signed distance to near clipping plane. + * @param clip_far Signed distance to far clipping plane. + */ + void set_orthographic(float clip_left, float clip_right, float clip_bottom, float clip_top, float clip_near, float clip_far); + + void set_compositor(compositor* compositor); + void set_composite_index(int index); + + virtual const bounding_volume& get_bounds() const; + + float is_orthographic() const; + float get_clip_left() const; + float get_clip_right() const; + float get_clip_bottom() const; + float get_clip_top() const; + float get_clip_near() const; + float get_clip_far() const; + float get_fov() const; + float get_aspect_ratio() const; + + /// Returns the camera's view matrix. + const float4x4& get_view() const; + + /// Returns the camera's projection matrix. + const float4x4& get_projection() const; + + /// Returns the camera's view-projection matrix. + const float4x4& get_view_projection() const; + + /// Returns the camera's view frustum. + const view_frustum& get_view_frustum() const; + + const compositor* get_compositor() const; + compositor* get_compositor(); + int get_composite_index() const; + + const tween& get_clip_left_tween() const; + const tween& get_clip_right_tween() const; + const tween& get_clip_bottom_tween() const; + const tween& get_clip_top_tween() const; + const tween& get_clip_near_tween() const; + const tween& get_clip_far_tween() const; + const tween& get_fov_tween() const; + const tween& get_aspect_ratio_tween() const; + const tween& get_view_tween() const; + const tween& get_projection_tween() const; + const tween& get_view_projection_tween() const; + + /// @copydoc scene_object_base::update_tweens(); + virtual void update_tweens(); + +private: + virtual void transformed(); + + compositor* compositor; + int composite_index; + bool orthographic; + tween clip_left; + tween clip_right; + tween clip_bottom; + tween clip_top; + tween clip_near; + tween clip_far; + tween fov; + tween aspect_ratio; + tween view; + tween projection; + tween view_projection; + view_frustum view_frustum; +}; + +inline const bounding_volume& camera::get_bounds() const +{ + return view_frustum.get_bounds(); +} + +inline float camera::is_orthographic() const +{ + return orthographic; +} + +inline float camera::get_clip_left() const +{ + return clip_left[1]; +} + +inline float camera::get_clip_right() const +{ + return clip_right[1]; +} + +inline float camera::get_clip_bottom() const +{ + return clip_bottom[1]; +} + +inline float camera::get_clip_top() const +{ + return clip_top[1]; +} + +inline float camera::get_clip_near() const +{ + return clip_near[1]; +} + +inline float camera::get_clip_far() const +{ + return clip_far[1]; +} + +inline float camera::get_fov() const +{ + return fov[1]; +} + +inline float camera::get_aspect_ratio() const +{ + return aspect_ratio[1]; +} + +inline const float4x4& camera::get_view() const +{ + return view[1]; +} + +inline const float4x4& camera::get_projection() const +{ + return projection[1]; +} + +inline const float4x4& camera::get_view_projection() const +{ + return view_projection[1]; +} + +inline const view_frustum& camera::get_view_frustum() const +{ + return view_frustum; +} + +inline const compositor* camera::get_compositor() const +{ + return compositor; +} + +inline compositor* camera::get_compositor() +{ + return compositor; +} + +inline int camera::get_composite_index() const +{ + return composite_index; +} + +inline const tween& camera::get_clip_left_tween() const +{ + return clip_left; +} + +inline const tween& camera::get_clip_right_tween() const +{ + return clip_right; +} + +inline const tween& camera::get_clip_bottom_tween() const +{ + return clip_bottom; +} + +inline const tween& camera::get_clip_top_tween() const +{ + return clip_top; +} + +inline const tween& camera::get_clip_near_tween() const +{ + return clip_near; +} + +inline const tween& camera::get_clip_far_tween() const +{ + return clip_far; +} + +inline const tween& camera::get_fov_tween() const +{ + return fov; +} + +inline const tween& camera::get_aspect_ratio_tween() const +{ + return aspect_ratio; +} + +inline const tween& camera::get_view_tween() const +{ + return view; +} + +inline const tween& camera::get_projection_tween() const +{ + return projection; +} + +inline const tween& camera::get_view_projection_tween() const +{ + return view_projection; +} + +#endif // ANTKEEPER_CAMERA_HPP + diff --git a/src/antkeeper/scene/directional-light.cpp b/src/antkeeper/scene/directional-light.cpp new file mode 100644 index 0000000..4123888 --- /dev/null +++ b/src/antkeeper/scene/directional-light.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 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 "directional-light.hpp" +#include "configuration.hpp" + +using namespace vmq::operators; + +static float3 interpolate_direction(const float3& x, const float3& y, float a) +{ + quaternion q0 = vmq::rotation(global_forward, x); + quaternion q1 = vmq::rotation(global_forward, y); + return vmq::normalize(vmq::slerp(q0, q1, a) * global_forward); +} + +directional_light::directional_light(): + direction(global_forward, interpolate_direction) +{} + +void directional_light::update_tweens() +{ + light::update_tweens(); + direction.update(); +} + +void directional_light::transformed() +{ + direction[1] = vmq::normalize(get_transform().rotation * global_forward); +} + diff --git a/src/antkeeper/scene/directional-light.hpp b/src/antkeeper/scene/directional-light.hpp new file mode 100644 index 0000000..c7cddaf --- /dev/null +++ b/src/antkeeper/scene/directional-light.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 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_DIRECTIONAL_LIGHT_HPP +#define ANTKEEPER_DIRECTIONAL_LIGHT_HPP + +#include "scene/light.hpp" +#include + +using namespace vmq::types; + +class directional_light: public light +{ +public: + directional_light(); + + /// Returns light_type::directional + virtual light_type get_light_type() const; + + const float3& get_direction() const; + + const tween& get_direction_tween() const; + + /// @copydoc scene_object_base::update_tweens(); + virtual void update_tweens(); + +private: + virtual void transformed(); + + tween direction; +}; + +inline light_type directional_light::get_light_type() const +{ + return light_type::directional; +} + +inline const float3& directional_light::get_direction() const +{ + return direction[1]; +} + +inline const tween& directional_light::get_direction_tween() const +{ + return direction; +} + +#endif // ANTKEEPER_DIRECTIONAL_LIGHT_HPP + diff --git a/src/antkeeper/scene/light.cpp b/src/antkeeper/scene/light.cpp new file mode 100644 index 0000000..aa979c2 --- /dev/null +++ b/src/antkeeper/scene/light.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 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 "scene/light.hpp" + +using namespace vmq::operators; + +light::light(): + bounds(get_translation(), 0.0f), + color(float3{1.0f, 1.0f, 1.0f}), + intensity(1.0f), + scaled_color(float3{1.0f, 1.0f, 1.0f}) +{} + +void light::set_color(const float3& color) +{ + this->color[1] = color; + scaled_color[1] = color * intensity[1]; +} + +void light::set_intensity(float intensity) +{ + this->intensity[1] = intensity; + scaled_color[1] = color[1] * intensity; +} + +void light::update_tweens() +{ + scene_object_base::update_tweens(); + color.update(); + intensity.update(); + scaled_color.update(); +} + +void light::transformed() +{ + bounds = sphere(get_translation(), 0.0f); +} diff --git a/src/antkeeper/scene/light.hpp b/src/antkeeper/scene/light.hpp new file mode 100644 index 0000000..06a71ca --- /dev/null +++ b/src/antkeeper/scene/light.hpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 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_LIGHT_HPP +#define ANTKEEPER_LIGHT_HPP + +#include "scene/scene-object.hpp" +#include "geometry/sphere.hpp" + +enum class light_type +{ + ambient, + directional, + point, + spot +}; + +class light: public scene_object +{ +public: + light(); + + virtual light_type get_light_type() const = 0; + + void set_color(const float3& color); + void set_intensity(float intensity); + + virtual const bounding_volume& get_bounds() const; + + const float3& get_color() const; + float get_intensity() const; + const float3& get_scaled_color() const; + + const tween& get_color_tween() const; + const tween& get_intensity_tween() const; + const tween& get_scaled_color_tween() const; + + /// @copydoc scene_object_base::update_tweens(); + virtual void update_tweens(); + +private: + virtual void transformed(); + + tween color; + tween intensity; + tween scaled_color; + sphere bounds; +}; + +inline const bounding_volume& light::get_bounds() const +{ + return bounds; +} + +inline const float3& light::get_color() const +{ + return color[1]; +} + +inline float light::get_intensity() const +{ + return intensity[1]; +} + +inline const float3& light::get_scaled_color() const +{ + return scaled_color[1]; +} + +inline const tween& light::get_color_tween() const +{ + return color; +} + +inline const tween& light::get_intensity_tween() const +{ + return intensity; +} + +inline const tween& light::get_scaled_color_tween() const +{ + return scaled_color; +} + +#endif // ANTKEEPER_LIGHT_HPP + diff --git a/src/antkeeper/scene/lod-group.cpp b/src/antkeeper/scene/lod-group.cpp new file mode 100644 index 0000000..cc3e584 --- /dev/null +++ b/src/antkeeper/scene/lod-group.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 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 "scene/lod-group.hpp" +#include "scene/camera.hpp" + +lod_group::lod_group(std::size_t level_count): + bounds(get_translation(), get_translation()) +{ + resize(level_count); +} + +lod_group::lod_group(): + lod_group(1) +{} + +void lod_group::resize(std::size_t level_count) +{ + levels.resize(level_count); +} + +std::size_t lod_group::select_lod(const ::camera& camera) const +{ + float distance = signed_distance(camera.get_view_frustum().get_near(), get_translation()); + + if (distance < 300.0f) + return 0; + else if (distance < 500.0f) + return 1; + else if (distance < 600.0f) + return 2; + + return 3; +} + +void lod_group::add_object(std::size_t level, scene_object_base* object) +{ + levels[level].push_back(object); +} + +void lod_group::remove_object(std::size_t level, scene_object_base* object) +{ + levels[level].remove(object); +} + +void lod_group::remove_objects(std::size_t level) +{ + levels[level].clear(); +} + +void lod_group::update_bounds() +{ + bounds = {get_translation(), get_translation()}; +} + +void lod_group::transformed() +{ + update_bounds(); +} diff --git a/src/antkeeper/scene/lod-group.hpp b/src/antkeeper/scene/lod-group.hpp new file mode 100644 index 0000000..d9a5008 --- /dev/null +++ b/src/antkeeper/scene/lod-group.hpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2020 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_LOD_GROUP_HPP +#define ANTKEEPER_LOD_GROUP_HPP + +#include "scene/scene-object.hpp" +#include "geometry/aabb.hpp" +#include +#include + +class camera; + +class lod_group: public scene_object +{ +public: + /** + * Creates a LOD group. + * + * @param level_count Number of detail levels in the group. + */ + lod_group(std::size_t level_count); + + /// Creates a LOD group with one level of detail. + lod_group(); + + /** + * Resizes the LOD group to accommodate the specified number of detail levels. + * + * @param level_count Number of desired detail levels in the group. + */ + void resize(std::size_t level_count); + + /** + * Selects the appropriate level of detail for a camera. + * + * @param camera Camera for which the LOD should be selected. + * @return Selected level of detail. + */ + std::size_t select_lod(const ::camera& camera) const; + + /** + * Adds an object to the LOD group. + * + * @param level Level of detail of the object to add. + * @param object Object to add. + */ + void add_object(std::size_t level, scene_object_base* object); + + /** + * Removes an object from the LOD group. + * + * @param level Level of detail of the object to remove. + * @param object Object to remove. + */ + void remove_object(std::size_t level, scene_object_base* object); + + /** + * Removes all objects with the specified level of detail. + * + * @param level Level of detail of the objects to remove. + */ + void remove_objects(std::size_t level); + + virtual const bounding_volume& get_bounds() const; + + /// Returns the number of detail levels in the group. + std::size_t get_level_count() const; + + /** + * Returns a list containing all objects in the LOD group with the specified detail level. + * + * @param level Level of detail. + * @return List of all objects in the group with the specified detail level. + */ + const std::list& get_objects(std::size_t level) const; + +private: + void update_bounds(); + virtual void transformed(); + + aabb bounds; + std::vector> levels; +}; + +inline const bounding_volume& lod_group::get_bounds() const +{ + return bounds; +} + +inline std::size_t lod_group::get_level_count() const +{ + return levels.size(); +} + +inline const std::list& lod_group::get_objects(std::size_t level) const +{ + return levels[level]; +} + +#endif // ANTKEEPER_LOD_GROUP_HPP + diff --git a/src/antkeeper/scene/model-instance.cpp b/src/antkeeper/scene/model-instance.cpp new file mode 100644 index 0000000..b6fcf96 --- /dev/null +++ b/src/antkeeper/scene/model-instance.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2020 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 "scene/model-instance.hpp" +#include "renderer/model.hpp" +#include + +model_instance::model_instance(::model* model): + model(nullptr), + pose(nullptr), + bounds(get_translation(), get_translation()), + instanced(false), + instance_count(0) +{ + 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) +{ + bounds = other.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(::model* model) +{ + this->model = model; + this->pose = nullptr; + + if (model) + { + materials.resize(model->get_groups()->size()); + reset_materials(); + } + + update_bounds(); +} + +void model_instance::set_pose(::pose* pose) +{ + this->pose = pose; +} + +void model_instance::set_material(std::size_t group_index, material* material) +{ + materials[group_index] = material; +} + +void model_instance::set_instanced(bool instanced, std::size_t instance_count) +{ + this->instanced = instanced; + this->instance_count = (instanced) ? instance_count : 0; +} + +void model_instance::reset_materials() +{ + std::fill(materials.begin(), materials.end(), nullptr); +} + +void model_instance::update_bounds() +{ + if (model) + bounds = aabb::transform(model->get_bounds(), get_transform()); + else + bounds = {get_translation(), get_translation()}; +} + +void model_instance::transformed() +{ + update_bounds(); +} diff --git a/src/antkeeper/scene/model-instance.hpp b/src/antkeeper/scene/model-instance.hpp new file mode 100644 index 0000000..136fcbe --- /dev/null +++ b/src/antkeeper/scene/model-instance.hpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2020 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_MODEL_INSTANCE_HPP +#define ANTKEEPER_MODEL_INSTANCE_HPP + +#include "scene/scene-object.hpp" +#include "geometry/aabb.hpp" +#include + +class material; +class model; +class pose; + +class model_instance: public scene_object +{ +public: + explicit model_instance(model* model); + model_instance(); + model_instance(const model_instance& other); + model_instance& operator=(const model_instance& other); + + /** + * Sets the model with which this model instance is associated. This will reset the pose and all overwritten materials. + */ + void set_model(model* model); + + /** + * Sets the + */ + void set_pose(pose* pose); + + /** + * Overwrites the material of a model group for this model instance. + * + * @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, material* material); + + void set_instanced(bool instanced, std::size_t instance_count = 1); + + /** + * Resets all overwritten materials. + */ + void reset_materials(); + + virtual const bounding_volume& get_bounds() const; + + const model* get_model() const; + model* get_model(); + + const pose* get_pose() const; + pose* get_pose(); + + const std::vector* get_materials() const; + + bool is_instanced() const; + std::size_t get_instance_count() const; + +private: + void update_bounds(); + virtual void transformed(); + + model* model; + pose* pose; + std::vector materials; + aabb bounds; + bool instanced; + std::size_t instance_count; +}; + +inline const bounding_volume& model_instance::get_bounds() const +{ + return bounds; +} + +inline const model* model_instance::get_model() const +{ + return model; +} + +inline 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; +} + +#endif // ANTKEEPER_MODEL_INSTANCE_HPP + diff --git a/src/antkeeper/scene/point-light.cpp b/src/antkeeper/scene/point-light.cpp new file mode 100644 index 0000000..86ebe05 --- /dev/null +++ b/src/antkeeper/scene/point-light.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 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 "point-light.hpp" + +using namespace vmq::operators; + +point_light::point_light(): + attenuation(float3{1, 0, 0}) +{} + +void point_light::set_attenuation(const float3& attenuation) +{ + this->attenuation[1] = attenuation; +} + +void point_light::update_tweens() +{ + light::update_tweens(); + attenuation.update(); +} + diff --git a/src/antkeeper/scene/point-light.hpp b/src/antkeeper/scene/point-light.hpp new file mode 100644 index 0000000..daec171 --- /dev/null +++ b/src/antkeeper/scene/point-light.hpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 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_POINT_LIGHT_HPP +#define ANTKEEPER_POINT_LIGHT_HPP + +#include "scene/light.hpp" + +class point_light: public light +{ +public: + point_light(); + + /// Returns light_type::point + virtual light_type get_light_type() const; + + /** + * Sets the attenuation factors of the light. + * + * @param attenuation Vector containing the constant, linear, and quadratic attenuation factors, as x, y, and z, respectively. + */ + void set_attenuation(const float3& attenuation); + + /// Returns the attenuation factors of the light. + const float3& get_attenuation() const; + + /// Returns the attenuation tween. + const tween& get_attenuation_tween() const; + + /// @copydoc scene_object_base::update_tweens(); + virtual void update_tweens(); + +private: + tween attenuation; +}; + +inline light_type point_light::get_light_type() const +{ + return light_type::point; +} + +inline const float3& point_light::get_attenuation() const +{ + return attenuation[1]; +} + +inline const tween& point_light::get_attenuation_tween() const +{ + return attenuation; +} + +#endif // ANTKEEPER_POINT_LIGHT_HPP + diff --git a/src/antkeeper/scene/scene-object.cpp b/src/antkeeper/scene/scene-object.cpp new file mode 100644 index 0000000..fd2f0e8 --- /dev/null +++ b/src/antkeeper/scene/scene-object.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 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 "scene/scene-object.hpp" + +static transform transform_interpolate(const transform& x, const transform& y, float a) +{ + return + { + vmq::lerp(x.translation, y.translation, a), + vmq::slerp(x.rotation, y.rotation, a), + vmq::lerp(x.scale, y.scale, a), + }; +} + +scene_object_base::scene_object_base(): + active(true), + transform(vmq::identity_transform, transform_interpolate), + culling_mask(nullptr) +{} + +void scene_object_base::set_culling_mask(const bounding_volume* culling_mask) +{ + this->culling_mask = culling_mask; +} + +std::size_t scene_object_base::next_object_type_id() +{ + static std::atomic id{0}; + return id++; +} + +void scene_object_base::update_tweens() +{ + transform.update(); +} + +void scene_object_base::look_at(const float3& position, const float3& target, const float3& up) +{ + transform[1].translation = position; + transform[1].rotation = vmq::look_rotation(vmq::normalize(vmq::sub(target, position)), up); + transformed(); +} + +void scene_object_base::transformed() +{} + diff --git a/src/antkeeper/scene/scene-object.hpp b/src/antkeeper/scene/scene-object.hpp new file mode 100644 index 0000000..b1ca26a --- /dev/null +++ b/src/antkeeper/scene/scene-object.hpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2020 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_SCENE_OBJECT_HPP +#define ANTKEEPER_SCENE_OBJECT_HPP + +#include +#include +#include +#include "animation/tween.hpp" +#include "geometry/bounding-volume.hpp" + +using namespace vmq::types; +using vmq::quaternion; +using vmq::transform; + +/** + * Internal base class for scene objects. + */ +class scene_object_base +{ +public: + /// Returns the type ID for this scene_object type. + virtual const std::size_t get_object_type_id() const = 0; + + /** + * Creates a scene object base. + */ + scene_object_base(); + + /** + * Destroys a scene object base. + */ + virtual ~scene_object_base() = default; + + /** + * Updates all tweens in the scene object. + */ + virtual void update_tweens(); + + /** + * Activates or deactivates the scene object. + */ + void set_active(bool active); + + /** + * + */ + void look_at(const float3& position, const float3& target, const float3& up); + + /** + * Sets the scene object's transform. + */ + void set_transform(const transform& transform); + + /** + * Sets the scene object's translation. + */ + void set_translation(const float3& translation); + + /** + * Sets the scene object's rotation. + */ + void set_rotation(const quaternion& rotation); + + /** + * Sets the scene object's scale. + */ + void set_scale(const float3& scale); + + /** + * Sets a culling mask for the object, which will be used for view-frustum culling instead of the object's bounds. + */ + void set_culling_mask(const bounding_volume* culling_mask); + + /// Returns whether the scene object is active. + bool is_active() const; + + /** + * Returns the transform. + */ + const transform& get_transform() const; + + /** + * Returns the transform's translation vector. + */ + const float3& get_translation() const; + + /** + * Returns the transform's rotation quaternion. + */ + const quaternion& get_rotation() const; + + /** + * Returns the transform's scale vector. + */ + const float3& get_scale() const; + + /** + * Returns the transform tween. + */ + const tween>& get_transform_tween() const; + tween>& get_transform_tween(); + + /** + * Returns the bounds of the object. + */ + virtual const bounding_volume& get_bounds() const = 0; + + /** + * Returns the culling mask of the object. + */ + const bounding_volume* get_culling_mask() const; + +protected: + static std::size_t next_object_type_id(); + +private: + /** + * Called every time the scene object's tranform is changed. + */ + virtual void transformed(); + + bool active; + tween> transform; + const bounding_volume* culling_mask; +}; + +inline void scene_object_base::set_active(bool active) +{ + this->active = active; +} + +inline void scene_object_base::set_transform(const ::transform& transform) +{ + this->transform[1] = transform; + transformed(); +} + +inline void scene_object_base::set_translation(const float3& translation) +{ + transform[1].translation = translation; + transformed(); +} + +inline void scene_object_base::set_rotation(const quaternion& rotation) +{ + transform[1].rotation = rotation; + transformed(); +} + +inline void scene_object_base::set_scale(const float3& scale) +{ + transform[1].scale = scale; + transformed(); +} + +inline bool scene_object_base::is_active() const +{ + return active; +} + +inline const transform& scene_object_base::get_transform() const +{ + return transform[1]; +} + +inline const float3& scene_object_base::get_translation() const +{ + return get_transform().translation; +} + +inline const quaternion& scene_object_base::get_rotation() const +{ + return get_transform().rotation; +} + +inline const float3& scene_object_base::get_scale() const +{ + return get_transform().scale; +} + +inline const tween>& scene_object_base::get_transform_tween() const +{ + return transform; +} + +inline tween>& scene_object_base::get_transform_tween() +{ + return transform; +} + +inline const bounding_volume* scene_object_base::get_culling_mask() const +{ + return culling_mask; +} + +/** + * Abstract base class for lights, cameras, model instances, and other scene objects. + * + * @tparam T This should be the same class that's inheriting from the scene object, in order to give it a valid type-specific ID. + */ +template +class scene_object: public scene_object_base +{ +public: + /// Unique type ID for this scene_object type. + static const std::atomic object_type_id; + + virtual const std::size_t get_object_type_id() const final; +}; + +template +const std::atomic scene_object::object_type_id{scene_object_base::next_object_type_id()}; + +template +inline const std::size_t scene_object::get_object_type_id() const +{ + return object_type_id; +} + +#endif // ANTKEEPER_SCENE_OBJECT_HPP + diff --git a/src/antkeeper/scene/scene.cpp b/src/antkeeper/scene/scene.cpp new file mode 100644 index 0000000..82d8873 --- /dev/null +++ b/src/antkeeper/scene/scene.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 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 "scene/scene.hpp" +#include "scene/scene-object.hpp" +#include "scene/light.hpp" +#include "scene/camera.hpp" +#include "scene/model-instance.hpp" + +void scene::add_object(scene_object_base* object) +{ + objects.push_back(object); + object_map[object->get_object_type_id()].push_back(object); +} + +void scene::remove_object(scene_object_base* object) +{ + objects.remove(object); + object_map[object->get_object_type_id()].remove(object); +} + +void scene::remove_objects() +{ + objects.clear(); + object_map.clear(); +} + +void scene::update_tweens() +{ + for (scene_object_base* object: objects) + { + object->update_tweens(); + } +} diff --git a/src/antkeeper/scene/scene.hpp b/src/antkeeper/scene/scene.hpp new file mode 100644 index 0000000..6db7384 --- /dev/null +++ b/src/antkeeper/scene/scene.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2020 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_SCENE_HPP +#define ANTKEEPER_SCENE_HPP + +#include +#include + +class scene_object_base; + +/** + * Container for scene objects. + */ +class scene +{ +public: + /** + * Adds an object to the scene. + * + * @param object Object to add. + */ + void add_object(scene_object_base* object); + + /** + * Removes an object from the scene. + * + * @param object Object to remove. + */ + void remove_object(scene_object_base* object); + + /// Removes all objects from the scene. + void remove_objects(); + + /// Updates the tweens of all objects in the scene. + void update_tweens(); + + /// Returns a list of all objects in the scene. + const std::list* get_objects() const; + + /** + * Returns a list of all objects in the scene with the specified type ID. + * + * @param type_id Scene object type ID. + * @return List of scene ibjects with the specified type ID. + */ + const std::list* get_objects(std::size_t type_id) const; + +private: + std::list objects; + mutable std::unordered_map> object_map; +}; + +inline const std::list* scene::get_objects() const +{ + return &objects; +} + +inline const std::list* scene::get_objects(std::size_t type_id) const +{ + return &object_map[type_id]; +} + +#endif // ANTKEEPER_SCENE_HPP diff --git a/src/antkeeper/scene/spotlight.cpp b/src/antkeeper/scene/spotlight.cpp new file mode 100644 index 0000000..21d41ce --- /dev/null +++ b/src/antkeeper/scene/spotlight.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 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 "spotlight.hpp" +#include "configuration.hpp" +#include + +using namespace vmq::operators; + +static float3 interpolate_direction(const float3& x, const float3& y, float a) +{ + quaternion q0 = vmq::rotation(global_forward, x); + quaternion q1 = vmq::rotation(global_forward, y); + return vmq::normalize(vmq::slerp(q0, q1, a) * global_forward); +} + +spotlight::spotlight(): + direction(global_forward, interpolate_direction), + attenuation(float3{1, 0, 0}), + cutoff(float2{vmq::pi, vmq::pi}), + cosine_cutoff(float2{std::cos(vmq::pi), std::cos(vmq::pi)}) +{} + +void spotlight::set_attenuation(const float3& attenuation) +{ + this->attenuation[1] = attenuation; +} + +void spotlight::set_cutoff(const float2& cutoff) +{ + this->cutoff[1] = cutoff; + this->cosine_cutoff[1] = {std::cos(cutoff.x), std::cos(cutoff.y)}; +} + +void spotlight::update_tweens() +{ + light::update_tweens(); + direction.update(); + attenuation.update(); + cutoff.update(); + cosine_cutoff.update(); +} + +void spotlight::transformed() +{ + direction[1] = vmq::normalize(get_transform().rotation * global_forward); +} + diff --git a/src/antkeeper/scene/spotlight.hpp b/src/antkeeper/scene/spotlight.hpp new file mode 100644 index 0000000..38b1a0b --- /dev/null +++ b/src/antkeeper/scene/spotlight.hpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2020 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_SPOTLIGHT_HPP +#define ANTKEEPER_SPOTLIGHT_HPP + +#include "scene/light.hpp" +#include + +using namespace vmq::types; + +class spotlight: public light +{ +public: + spotlight(); + + /// Returns light_type::spot + virtual light_type get_light_type() const; + + /** + * Sets the attenuation factors of the light. + * + * @param attenuation Vector containing the constant, linear, and quadratic attenuation factors, as x, y, and z, respectively. + */ + void set_attenuation(const float3& attenuation); + + /** + * Sets the spotlight cutoff angles. + * + * @param cutoff Vector containing the inner and outer cutoff angles, as x and y, respectively. + */ + void set_cutoff(const float2& cutoff); + + /// Returns the direction vector. + const float3& get_direction() const; + + /// Returns the attenuation factors of the light. + const float3& get_attenuation() const; + + /// Returns the spotlight cutoff angles. + const float2& get_cutoff() const; + + /// Returns the cosine of the spotlight cutoff angles. + const float2& get_cosine_cutoff() const; + + /// Returns the direction tween. + const tween& get_direction_tween() const; + + /// Returns the attenuation tween. + const tween& get_attenuation_tween() const; + + /// Returns the cutoff tween. + const tween& get_cutoff_tween() const; + + /// Returns the cosine cutoff tween. + const tween& get_cosine_cutoff_tween() const; + + /// @copydoc scene_object_base::update_tweens(); + virtual void update_tweens(); + +private: + virtual void transformed(); + + tween direction; + tween attenuation; + tween cutoff; + tween cosine_cutoff; +}; + +inline light_type spotlight::get_light_type() const +{ + return light_type::spot; +} + +inline const float3& spotlight::get_direction() const +{ + return direction[1]; +} + +inline const float3& spotlight::get_attenuation() const +{ + return attenuation[1]; +} + +inline const float2& spotlight::get_cutoff() const +{ + return cutoff[1]; +} + +inline const float2& spotlight::get_cosine_cutoff() const +{ + return cosine_cutoff[1]; +} + +inline const tween& spotlight::get_direction_tween() const +{ + return direction; +} + +inline const tween& spotlight::get_attenuation_tween() const +{ + return attenuation; +} + +inline const tween& spotlight::get_cutoff_tween() const +{ + return cutoff; +} + +inline const tween& spotlight::get_cosine_cutoff_tween() const +{ + return cosine_cutoff; +} + +#endif // ANTKEEPER_SPOTLIGHT_HPP + diff --git a/src/antkeeper/sdf.hpp b/src/antkeeper/sdf.hpp new file mode 100644 index 0000000..3b1f09d --- /dev/null +++ b/src/antkeeper/sdf.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2020 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_SDF_HPP +#define ANTKEEPER_SDF_HPP + +#include +using namespace vmq::types; +using namespace vmq::operators; + +/** + * Contains signed distance functions. + */ +namespace sdf { + +inline float3 translate(const float3& sample, const float3& offset) +{ + return sample - offset; +} + +inline float sphere(const float3& p, float r) +{ + return vmq::length(p) - r; +} + +inline float cylinder(const float3& p, float r, float h) +{ + float dx = std::abs(vmq::length(vmq::swizzle<0, 2>(p))) - r; + float dy = std::abs(p[1]) - h; + return std::min(std::max(dx, dy), 0.0f) + vmq::length(float2{std::max(dx, 0.0f), std::max(dy, 0.0f)}); +} + +inline float op_union(float a, float b) +{ + return std::min(a, b); +} + +inline float op_difference(float a, float b) +{ + return std::max(-a, b); +} + +inline float op_intersection(float a, float b) +{ + return std::max(a, b); +} + +inline float op_round(float d, float r) +{ + return d - r; +} + +} // namespace sdf + +#endif // ANTKEEPER_SDF_HPP + diff --git a/src/antkeeper/state/application-states.hpp b/src/antkeeper/state/application-states.hpp new file mode 100644 index 0000000..80662f9 --- /dev/null +++ b/src/antkeeper/state/application-states.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 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_APPLICATION_STATES_HPP +#define ANTKEEPER_APPLICATION_STATES_HPP + +class application; + +void enter_loading_state(application* app); +void exit_loading_state(application* app); +void enter_language_select_state(application* app); +void exit_language_select_state(application* app); +void enter_splash_state(application* app); +void exit_splash_state(application* app); +void enter_title_state(application* app); +void exit_title_state(application* app); +void enter_play_state(application* app); +void exit_play_state(application* app); +void enter_pause_state(application* app); +void exit_pause_state(application* app); + +#endif // ANTKEEPER_APPLICATION_STATES_HPP + diff --git a/src/antkeeper/state/fsm.cpp b/src/antkeeper/state/fsm.cpp new file mode 100644 index 0000000..cc87709 --- /dev/null +++ b/src/antkeeper/state/fsm.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 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 "fsm.hpp" + +namespace fsm { + +machine::machine(): + previous_state{nullptr, nullptr}, + current_state{nullptr, nullptr} +{} + +void machine::change_state(const state& next_state) +{ + if (current_state.exit) + { + current_state.exit(); + } + + previous_state = current_state; + current_state = next_state; + + if (current_state.enter) + { + current_state.enter(); + } +} + +} // namespace fsm + diff --git a/src/antkeeper/state/fsm.hpp b/src/antkeeper/state/fsm.hpp new file mode 100644 index 0000000..83c8a02 --- /dev/null +++ b/src/antkeeper/state/fsm.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2020 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_STATE_MACHINE_HPP +#define ANTKEEPER_STATE_MACHINE_HPP + +#include + +namespace fsm { + +/** + * A finite state machine state consisting of a pair of `enter()` and `exit()` functions. + */ +struct state +{ + std::function enter; + std::function exit; +}; + +/** + * General purpose finite state machine. + */ +class machine +{ +public: + machine(); + + void change_state(const state& next_state); + + const state& get_previous_state() const; + const state& get_current_state() const; + +private: + state previous_state; + state current_state; +}; + +inline const state& machine::get_previous_state() const +{ + return previous_state; +} + +inline const state& machine::get_current_state() const +{ + return current_state; +} + +} // namespace fsm + +#endif // ANTKEEPER_STATE_MACHINE_HPP + diff --git a/src/entity/components/camera-component.cpp b/src/antkeeper/state/language-select-state.cpp similarity index 51% rename from src/entity/components/camera-component.cpp rename to src/antkeeper/state/language-select-state.cpp index 2f4f81f..84062cb 100644 --- a/src/entity/components/camera-component.cpp +++ b/src/antkeeper/state/language-select-state.cpp @@ -1,29 +1,31 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#include "camera-component.hpp" +#include "application-states.hpp" +#include "application.hpp" +#include -ComponentBase* CameraComponent::clone() const +void enter_language_select_state(application* app) +{ +} + +void exit_language_select_state(application* app) { - CameraComponent* component = new CameraComponent(); - component->camera = camera; - - return component; } diff --git a/src/antkeeper/state/loading-state.cpp b/src/antkeeper/state/loading-state.cpp new file mode 100644 index 0000000..50c7d38 --- /dev/null +++ b/src/antkeeper/state/loading-state.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 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 "application-states.hpp" +#include "application.hpp" +#include + +void enter_loading_state(application* app) +{ + logger* logger = app->get_logger(); + logger->log("Entering loading state...\n"); + + logger->success("Entering loading state... success\n"); + + app->get_state_machine()->change_state(app->get_title_state()); +} + +void exit_loading_state(application* app) +{ + logger* logger = app->get_logger(); + logger->log("Exiting loading state...\n"); + + logger->success("Exiting loading state... success\n"); +} + diff --git a/src/entity/components/steering-component.cpp b/src/antkeeper/state/pause-state.cpp similarity index 52% rename from src/entity/components/steering-component.cpp rename to src/antkeeper/state/pause-state.cpp index a1113c9..593d70c 100644 --- a/src/entity/components/steering-component.cpp +++ b/src/antkeeper/state/pause-state.cpp @@ -1,27 +1,31 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#include "steering-component.hpp" +#include "application-states.hpp" +#include "application.hpp" +#include -ComponentBase* SteeringComponent::clone() const +void enter_pause_state(application* app) +{ +} + +void exit_pause_state(application* app) { - SteeringComponent* component = new SteeringComponent(); - return component; } diff --git a/src/antkeeper/state/play-state.cpp b/src/antkeeper/state/play-state.cpp new file mode 100644 index 0000000..77ad665 --- /dev/null +++ b/src/antkeeper/state/play-state.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2020 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 "application-states.hpp" +#include "configuration.hpp" +#include "application.hpp" +#include "scene/model-instance.hpp" +#include "resources/resource-manager.hpp" +#include "renderer/model.hpp" +#include "renderer/material.hpp" +#include "systems/control-system.hpp" +#include "entity/components/model-component.hpp" +#include "entity/components/transform-component.hpp" +#include "entity/components/terrain-component.hpp" +#include "entity/components/samara-component.hpp" +#include "entity/components/cavity-component.hpp" +#include "entity/components/tool-component.hpp" +#include "entity/components/placement-component.hpp" +#include "entity/archetype.hpp" +#include "nest.hpp" +#include "math.hpp" +#include "geometry/mesh-accelerator.hpp" +#include "behavior/ebt.hpp" +#include + +using namespace vmq::operators; + +void enter_play_state(application* app) +{ + logger* logger = app->get_logger(); + logger->log("Entering play state...\n"); + + + resource_manager* resource_manager = app->get_resource_manager(); + entt::registry& ecs_registry = app->get_ecs_registry(); + + // Load entity archetypes + ecs::archetype* ant_hill_archetype = resource_manager->load("ant-hill.ent"); + ecs::archetype* maple_tree_archetype = resource_manager->load("maple-tree.ent"); + ecs::archetype* darkness_volume_archetype = resource_manager->load("darkness-volume.ent"); + ecs::archetype* nest_archetype = resource_manager->load("harvester-nest.ent"); + ecs::archetype* samara_archetype = resource_manager->load("samara.ent"); + ecs::archetype* forceps_archetype = resource_manager->load("forceps.ent"); + ecs::archetype* larva_archetype = resource_manager->load("larva.ent"); + ecs::archetype* pebble_archetype = resource_manager->load("pebble.ent"); + + + + ecs::placement_component placement; + + auto ant_hill_entity = ant_hill_archetype->create(ecs_registry); + placement.ray.origin = {0, 10000, 0}; + placement.ray.direction = {0, -1, 0}; + ecs_registry.assign(ant_hill_entity, placement); + + + + float pebble_radius = 300.0f; + int pebble_count = 100; + + for (int i = 0; i < pebble_count; ++i) + { + float x = frand(-pebble_radius, pebble_radius); + float z = frand(-pebble_radius, pebble_radius); + + auto pebble_entity = pebble_archetype->create(ecs_registry); + + auto& transform = ecs_registry.get(pebble_entity); + transform.transform = vmq::identity_transform; + transform.transform.rotation = vmq::angle_axis(frand(0.0f, vmq::two_pi), {0, 1, 0}); + transform.transform.scale = float3{1, 1, 1} * frand(0.75f, 1.25f); + + placement.ray.origin = {x, 10000, z}; + ecs_registry.assign(pebble_entity, placement); + } + + auto maple_tree_entity = maple_tree_archetype->create(ecs_registry); + placement.ray.origin = {300, 10000, 200}; + placement.ray.direction = {0, -1, 0}; + ecs_registry.assign(maple_tree_entity, placement); + + //auto darkness_volume_entity = darkness_volume_archetype->create(ecs_registry); + auto nest_entity = nest_archetype->create(ecs_registry); + + int terrain_radius = 2; + for (int x = -terrain_radius; x <= terrain_radius; ++x) + { + for (int z = -terrain_radius; z <= terrain_radius; ++z) + { + ecs::terrain_component terrain_component; + terrain_component.subdivisions = TERRAIN_PATCH_RESOLUTION; + terrain_component.x = x; + terrain_component.z = z; + auto terrain_entity = ecs_registry.create(); + ecs_registry.assign(terrain_entity, terrain_component); + } + } + + for (int i = 0; i < 15; ++i) + { + auto samara_entity = samara_archetype->create(ecs_registry); + + auto& transform = ecs_registry.get(samara_entity); + float zone = 200.0f; + transform.transform = vmq::identity_transform; + transform.transform.translation.x = frand(-zone, zone); + transform.transform.translation.y = frand(50.0f, 150.0f); + transform.transform.translation.z = frand(-zone, zone); + + ecs::samara_component samara_component; + samara_component.angle = frand(0.0f, vmq::radians(360.0f)); + samara_component.direction = vmq::normalize(float3{frand(-1, 1), frand(-1, -5), frand(-1, 1)}); + samara_component.chirality = (frand(0, 1) < 0.5f) ? -1.0f : 1.0f; + + ecs_registry.assign_or_replace(samara_entity, samara_component); + } + + /* + ecs::archetype* grass_archetype = resource_manager->load("grassland-grass.ent"); + auto grass_entity_1 = grass_archetype->create(ecs_registry); + auto grass_entity_2 = grass_archetype->create(ecs_registry); + ecs_registry.get(grass_entity_2).transform.rotation = vmq::angle_axis(vmq::radians(120.0f), float3{0, 1, 0}); + */ + + // Setup camera + camera* camera = app->get_camera(); + orbit_cam* orbit_cam = app->get_orbit_cam(); + orbit_cam->attach(camera); + orbit_cam->set_target_focal_point({0, 0, 0}); + orbit_cam->set_target_focal_distance(15.0f); + orbit_cam->set_target_elevation(vmq::radians(25.0f)); + orbit_cam->set_target_azimuth(0.0f); + orbit_cam->set_focal_point(orbit_cam->get_target_focal_point()); + orbit_cam->set_focal_distance(orbit_cam->get_target_focal_distance()); + orbit_cam->set_elevation(orbit_cam->get_target_elevation()); + orbit_cam->set_azimuth(orbit_cam->get_target_azimuth()); + + + + + // Create forceps tool + auto forceps_entity = forceps_archetype->create(ecs_registry); + ecs::tool_component forceps_tool_component; + forceps_tool_component.active = true; + ecs_registry.assign(forceps_entity, forceps_tool_component); + + app->get_scene().update_tweens(); + + // Allocate a nest + nest* nest = new ::nest(); + + // Setup initial nest parameters + float tunnel_radius = 1.15f; + nest->set_tunnel_radius(tunnel_radius); + nest::shaft* central_shaft = nest->get_central_shaft(); + central_shaft->chirality = -1.0f; + central_shaft->rotation = vmq::radians(0.0f); + central_shaft->depth = {0.0f, 200.0f}; + central_shaft->radius = {0.0f, 5.0f}; + central_shaft->pitch = {4.0f, 8.0f}; + central_shaft->translation = {{{0.0f, 0.0f}, {40.0f, 26.0f}}}; + central_shaft->current_depth = 0.0f; + for (std::size_t i = 0; i < 4; ++i) + { + nest::chamber chamber; + chamber.shaft = central_shaft; + chamber.depth = (i + 1) * 50.0f; + chamber.rotation = vmq::radians(0.0f); + chamber.inner_radius = 4.0f; + chamber.outer_radius = 10.0f; + central_shaft->chambers.push_back(chamber); + } + + // Dig nest shafts + float shift = 0.1f; + for (int i = 0; i < 400; ++i) + { + ecs::cavity_component cavity; + cavity.position = nest->extend_shaft(*nest->get_central_shaft()); + cavity.position += float3{frand(-shift, shift), frand(-shift, shift), frand(-shift, shift)}; + cavity.radius = tunnel_radius * frand(1.0f, 1.1f); + + ecs_registry.assign(ecs_registry.create(), cavity); + } + + // Dig nest chambers + for (int i = 0; i < central_shaft->chambers.size(); ++i) + { + for (int j = 0; j < 150; ++j) + { + ecs::cavity_component cavity; + cavity.position = nest->expand_chamber(central_shaft->chambers[i]); + cavity.position += float3{frand(-shift, shift), frand(-shift, shift), frand(-shift, shift)}; + cavity.radius = tunnel_radius * frand(1.0f, 1.1f); + + ecs_registry.assign(ecs_registry.create(), cavity); + } + } + + // Place larva in chamber + { + auto larva_entity = larva_archetype->create(ecs_registry); + auto& transform = ecs_registry.get(larva_entity); + transform.transform = vmq::identity_transform; + transform.transform.translation = nest->get_shaft_position(*central_shaft, central_shaft->depth[1]); + //transform.transform.translation.y -= 1.0f; + } + + control_system* control_system = app->get_control_system(); + control_system->update(0.0f); + control_system->set_nest(nest); + orbit_cam->update(0.0f); + + logger->success("Entering play state... success\n"); +} + +void exit_play_state(application* app) +{ + logger* logger = app->get_logger(); + logger->log("Exiting play state...\n"); + + logger->success("Exiting play state... success\n"); +} + diff --git a/src/antkeeper/state/splash-state.cpp b/src/antkeeper/state/splash-state.cpp new file mode 100644 index 0000000..a58cd58 --- /dev/null +++ b/src/antkeeper/state/splash-state.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 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 "application-states.hpp" +#include "application.hpp" +#include + +void enter_splash_state(application* app) +{ + logger* logger = app->get_logger(); + logger->log("Entering splash state...\n"); + + timeline* timeline = app->get_timeline(); + float t = timeline->get_position(); + + sequence splash_sequence = + { + {t + 0.0f, [logger](){ logger->log("cue logo fade-in\n"); }}, + {t + 3.0f, [logger](){ logger->log("cue logo fade-out\n"); }}, + {t + 8.0f, [app](){ app->get_state_machine()->change_state(app->get_play_state()); }} + }; + + timeline->add_sequence(splash_sequence); + + logger->success("Entering splash state... success\n"); +} + +void exit_splash_state(application* app) +{ + logger* logger = app->get_logger(); + logger->log("Exiting splash state...\n"); + logger->success("Exiting splash state... success\n"); +} + diff --git a/src/antkeeper/state/title-state.cpp b/src/antkeeper/state/title-state.cpp new file mode 100644 index 0000000..ae742dd --- /dev/null +++ b/src/antkeeper/state/title-state.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 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 "application-states.hpp" +#include "application.hpp" +#include + +void enter_title_state(application* app) +{ + logger* logger = app->get_logger(); + logger->log("Entering title state...\n"); + + timeline* timeline = app->get_timeline(); + float t = timeline->get_position(); + + sequence startup_sequence = + { + {t + 0.0f, [logger](){ logger->log("cue sound fade-in\n"); }}, + {t + 3.0f, [logger](){ logger->log("cue scene fade-in from black\n"); }}, + {t + 8.0f, [logger](){ logger->log("cue title fade-in\n"); }}, + {t + 10.0f, [logger](){ logger->log("cue menu fade-in\n"); }} + }; + + timeline->add_sequence(startup_sequence); + + logger->success("Entering title state... success\n"); +} + +void exit_title_state(application* app) +{ + logger* logger = app->get_logger(); + logger->log("Exiting title state...\n"); + + + logger->success("Exiting title state... success\n"); +} + diff --git a/src/antkeeper/systems/behavior-system.cpp b/src/antkeeper/systems/behavior-system.cpp new file mode 100644 index 0000000..a33fc73 --- /dev/null +++ b/src/antkeeper/systems/behavior-system.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 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 "behavior-system.hpp" +#include "entity/components/behavior-component.hpp" + +using namespace ecs; + +behavior_system::behavior_system(entt::registry& registry): + entity_system(registry) +{} + +void behavior_system::update(double t, double dt) +{ + ebt::context context; + context.registry = ®istry; + + registry.view().each( + [&](auto entity, auto& behavior) + { + if (behavior.behavior_tree) + { + context.entity = entity; + behavior.behavior_tree->execute(context); + } + }); +} + diff --git a/src/antkeeper/systems/behavior-system.hpp b/src/antkeeper/systems/behavior-system.hpp new file mode 100644 index 0000000..b2924bb --- /dev/null +++ b/src/antkeeper/systems/behavior-system.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 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_BEHAVIOR_SYSTEM_HPP +#define ANTKEEPER_BEHAVIOR_SYSTEM_HPP + +#include "entity-system.hpp" + +class behavior_system: + public entity_system +{ +public: + behavior_system(entt::registry& registry); + virtual void update(double t, double dt); +}; + +#endif // ANTKEEPER_BEHAVIOR_SYSTEM_HPP + diff --git a/src/antkeeper/systems/camera-system.cpp b/src/antkeeper/systems/camera-system.cpp new file mode 100644 index 0000000..403f3ee --- /dev/null +++ b/src/antkeeper/systems/camera-system.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2020 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 "camera-system.hpp" +#include "entity/components/collision-component.hpp" +#include "entity/components/tool-component.hpp" +#include "entity/components/transform-component.hpp" +#include "scene/camera.hpp" +#include "orbit-cam.hpp" +#include "geometry/mesh.hpp" +#include "geometry/intersection.hpp" +#include + +using namespace vmq::operators; +using namespace ecs; + +camera_system::camera_system(entt::registry& registry): + entity_system(registry), + orbit_cam(nullptr), + viewport{0, 0, 0, 0}, + mouse_position{0, 0} +{} + +void camera_system::update(double t, double dt) +{ + if (!orbit_cam) + return; + + const camera* camera = orbit_cam->get_camera(); + if (!camera) + return; + + /* + registry.view().each( + [&](auto entity, auto& transform, auto& tool) + { + if (!tool.active) + return; + + float3 screen_position = camera->project(transform.transform.translation, viewport); + std::cout << screen_position << std::endl; + + // Project tool position onto viewport + }); + */ + + // Cast a ray straight down from infinity at the focal point's lateral coordinates. + // std::numeric_limits::infinity() actually doesn't work here + float3 focal_point = orbit_cam->get_target_focal_point(); + float3 pick_origin = float3{focal_point.x, focal_point.y + 500.0f, focal_point.z}; + float3 pick_direction = float3{0, -1, 0}; + ray picking_ray = {pick_origin, pick_direction}; + + float a = std::numeric_limits::infinity(); + bool intersection = false; + float3 pick; + + registry.view().each( + [&](auto entity, auto& transform, auto& collision) + { + vmq::transform inverse_transform = vmq::inverse(transform.transform); + float3 origin = inverse_transform * pick_origin; + float3 direction = vmq::normalize(vmq::conjugate(transform.transform.rotation) * pick_direction); + ray transformed_ray = {origin, direction}; + + // Broad phase AABB test + auto aabb_result = ray_aabb_intersection(transformed_ray, collision.bounds); + if (!std::get<0>(aabb_result)) + { + return; + } + + // Narrow phase mesh test + auto mesh_result = ray_mesh_intersection(transformed_ray, *collision.mesh); + if (std::get<0>(mesh_result)) + { + intersection = true; + if (std::get<1>(mesh_result) < a) + { + a = std::get<1>(mesh_result); + pick = picking_ray.extrapolate(a); + } + } + }); + + + if (intersection) + { + //orbit_cam->set_target_focal_point(pick); + } +} + +void camera_system::set_orbit_cam(::orbit_cam* orbit_cam) +{ + this->orbit_cam = orbit_cam; +} + +void camera_system::set_viewport(const float4& viewport) +{ + this->viewport = viewport; +} + +void camera_system::handle_event(const mouse_moved_event& event) +{ + mouse_position[0] = event.x; + mouse_position[1] = event.y; +} + diff --git a/src/antkeeper/systems/camera-system.hpp b/src/antkeeper/systems/camera-system.hpp new file mode 100644 index 0000000..261a6f0 --- /dev/null +++ b/src/antkeeper/systems/camera-system.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 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_CAMERA_SYSTEM_HPP +#define ANTKEEPER_CAMERA_SYSTEM_HPP + +#include "entity-system.hpp" +#include "event/event-handler.hpp" +#include "input/input-events.hpp" +#include +using namespace vmq::types; + +class camera; +class orbit_cam; + +class camera_system: + public entity_system, + public event_handler +{ +public: + camera_system(entt::registry& registry); + virtual void update(double t, double dt); + + void set_orbit_cam(orbit_cam* orbit_cam); + void set_viewport(const float4& viewport); + +private: + virtual void handle_event(const mouse_moved_event& event); + + orbit_cam* orbit_cam; + float4 viewport; + float2 mouse_position; +}; + +#endif // ANTKEEPER_CAMERA_SYSTEM_HPP + diff --git a/src/antkeeper/systems/collision-system.cpp b/src/antkeeper/systems/collision-system.cpp new file mode 100644 index 0000000..91922c2 --- /dev/null +++ b/src/antkeeper/systems/collision-system.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 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 "collision-system.hpp" +#include "entity/components/transform-component.hpp" + +using namespace ecs; + +collision_system::collision_system(entt::registry& registry): + entity_system(registry) +{ + registry.on_construct().connect<&collision_system::on_collision_construct>(this); + registry.on_replace().connect<&collision_system::on_collision_replace>(this); + registry.on_destroy().connect<&collision_system::on_collision_destroy>(this); +} + +void collision_system::update(double t, double dt) +{} + +void collision_system::on_collision_construct(entt::registry& registry, entt::entity entity, collision_component& collision) +{} + +void collision_system::on_collision_replace(entt::registry& registry, entt::entity entity, collision_component& collision) +{} + +void collision_system::on_collision_destroy(entt::registry& registry, entt::entity entity) +{} + diff --git a/src/antkeeper/systems/collision-system.hpp b/src/antkeeper/systems/collision-system.hpp new file mode 100644 index 0000000..9bdc61b --- /dev/null +++ b/src/antkeeper/systems/collision-system.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 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_COLLISION_SYSTEM_HPP +#define ANTKEEPER_COLLISION_SYSTEM_HPP + +#include "entity-system.hpp" +#include "entity/components/collision-component.hpp" + +/** + * Maintains a spatially partitioned set of collision meshes. The set of collision meshes isnot owned by the collision system, so it can be accessed by other systems as well. + */ +class collision_system: public entity_system +{ +public: + collision_system(entt::registry& registry); + virtual void update(double t, double dt); + +private: + void on_collision_construct(entt::registry& registry, entt::entity entity, ecs::collision_component& collision); + void on_collision_replace(entt::registry& registry, entt::entity entity, ecs::collision_component& collision); + void on_collision_destroy(entt::registry& registry, entt::entity entity); +}; + +#endif // ANTKEEPER_COLLISION_SYSTEM_HPP + diff --git a/src/antkeeper/systems/control-system.cpp b/src/antkeeper/systems/control-system.cpp new file mode 100644 index 0000000..834208b --- /dev/null +++ b/src/antkeeper/systems/control-system.cpp @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2020 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 "control-system.hpp" +#include "input/control.hpp" +#include "orbit-cam.hpp" +#include "scene/camera.hpp" +#include "geometry/intersection.hpp" +#include "animation/easings.hpp" +#include "nest.hpp" +#include + +using namespace vmq::operators; + +template +static inline T lerp(const T& x, const T& y, float a) +{ + return x * (1.0f - a) + y * a; +} + +template +static inline T log_lerp(const T& x, const T& y, float a) +{ + T log_x = std::log(x); + T log_y = std::log(y); + return std::exp(lerp(log_x, log_y, a)); +} + +control_system::control_system(): + timestep(0.0f), + zoom(0.0f) +{ + control_set.add_control(&move_forward_control); + control_set.add_control(&move_back_control); + control_set.add_control(&move_right_control); + control_set.add_control(&move_left_control); + control_set.add_control(&rotate_ccw_control); + control_set.add_control(&rotate_cw_control); + control_set.add_control(&tilt_up_control); + control_set.add_control(&tilt_down_control); + control_set.add_control(&zoom_in_control); + control_set.add_control(&zoom_out_control); + control_set.add_control(&adjust_camera_control); + control_set.add_control(&ascend_control); + control_set.add_control(&descend_control); + control_set.add_control(&toggle_view_control); + control_set.add_control(&tool_menu_control); + + // Set deadzone at 15% for all controls + const std::list* controls = control_set.get_controls(); + for (control* control: *controls) + { + control->set_deadzone(0.15f); + } + + zoom_speed = 4.0f; //1 + min_elevation = vmq::radians(-85.0f); + max_elevation = vmq::radians(85.0f); + near_focal_distance = 2.0f; + far_focal_distance = 200.0f; + near_movement_speed = 10.0f; + far_movement_speed = 80.0f; + near_fov = vmq::radians(80.0f); + far_fov = vmq::radians(35.0f); + near_clip_near = 0.1f; + far_clip_near = 5.0f; + near_clip_far = 100.0f; + far_clip_far = 2000.0f; + + nest = nullptr; + orbit_cam = nullptr; +} + +void control_system::update(float dt) +{ + this->timestep = dt; + + if (adjust_camera_control.is_active() && !adjust_camera_control.was_active()) + { + } + + // Determine zoom + if (zoom_in_control.is_active()) + zoom += zoom_speed * dt * zoom_in_control.get_current_value(); + if (zoom_out_control.is_active()) + zoom -= zoom_speed * dt * zoom_out_control.get_current_value(); + zoom = std::max(0.0f, std::min(1.0f, zoom)); + + float focal_distance = log_lerp(near_focal_distance, far_focal_distance, 1.0f - zoom); + + float fov = log_lerp(near_fov, far_fov, 1.0f - zoom); + + //float elevation_factor = (orbit_cam->get_target_elevation() - min_elevation) / max_elevation; + //fov = log_lerp(near_fov, far_fov, elevation_factor); + float clip_near = log_lerp(near_clip_near, far_clip_near, 1.0f - zoom); + float clip_far = log_lerp(near_clip_far, far_clip_far, 1.0f - zoom); + float movement_speed = log_lerp(near_movement_speed * dt, far_movement_speed * dt, 1.0f - zoom); + + orbit_cam->set_target_focal_distance(focal_distance); + orbit_cam->get_camera()->set_perspective(fov, orbit_cam->get_camera()->get_aspect_ratio(), clip_near, clip_far); + + const float rotation_speed = 2.0f * dt; + float rotation = 0.0f; + if (rotate_ccw_control.is_active()) + rotation += rotate_ccw_control.get_current_value() * rotation_speed; + if (rotate_cw_control.is_active()) + rotation -= rotate_cw_control.get_current_value() * rotation_speed; + if (rotation) + { + orbit_cam->rotate(rotation); + } + + const float tilt_speed = 2.0f * dt; + float tilt = 0.0f; + if (tilt_up_control.is_active()) + tilt -= tilt_up_control.get_current_value() * tilt_speed; + if (tilt_down_control.is_active()) + tilt += tilt_down_control.get_current_value() * tilt_speed; + if (tilt) + { + orbit_cam->tilt(tilt); + float elevation = orbit_cam->get_target_elevation(); + elevation = std::min(max_elevation, std::max(min_elevation, elevation)); + orbit_cam->set_target_elevation(elevation); + } + + float2 movement{0.0f, 0.0f}; + if (move_right_control.is_active()) + movement[0] += move_right_control.get_current_value(); + if (move_left_control.is_active()) + movement[0] -= move_left_control.get_current_value(); + if (move_forward_control.is_active()) + movement[1] -= move_forward_control.get_current_value(); + if (move_back_control.is_active()) + movement[1] += move_back_control.get_current_value(); + + const float deadzone = 0.01f; + float magnitude_squared = vmq::length_squared(movement); + + if (magnitude_squared > deadzone) + { + if (magnitude_squared > 1.0f) + { + movement = vmq::normalize(movement); + } + + orbit_cam->move(movement * movement_speed); + } + + float ascention = 0.0f; + if (ascend_control.is_active()) + ascention += ascend_control.get_current_value(); + if (descend_control.is_active()) + ascention -= descend_control.get_current_value(); + + if (ascention) + { + float old_depth = -orbit_cam->get_target_focal_point()[1]; + + orbit_cam->set_target_focal_point(orbit_cam->get_target_focal_point() + float3{0.0f, ascention * movement_speed, 0.0f}); + + if (nest) + { + float3 focal_point = orbit_cam->get_target_focal_point(); + float depth = -focal_point[1]; + + float delta_shaft_angle = nest->get_shaft_angle(*nest->get_central_shaft(), depth) - nest->get_shaft_angle(*nest->get_central_shaft(), old_depth); + + //orbit_cam->set_target_azimuth(orbit_cam->get_target_azimuth() - delta_shaft_angle); + orbit_cam->set_target_focal_point(nest->get_shaft_position(*nest->get_central_shaft(), depth)); + } + } + + orbit_cam->update(dt); + + camera* camera = orbit_cam->get_camera(); + + float3 pick_near = camera->unproject({mouse_position[0], viewport[3] - mouse_position[1], 0.0f}, viewport); + float3 pick_far = camera->unproject({mouse_position[0], viewport[3] - mouse_position[1], 1.0f}, viewport); + float3 pick_direction = vmq::normalize(pick_far - pick_near); + ray picking_ray = {pick_near, pick_direction}; + plane ground_plane = {float3{0, 1, 0}, 0.0f}; + + auto intersection_result = ray_plane_intersection(picking_ray, ground_plane); + if (std::get<0>(intersection_result)) + { + float3 pick = picking_ray.extrapolate(std::get<1>(intersection_result)); + if (tool) + tool->set_translation(pick); + } + + if (toggle_view_control.is_active() && !toggle_view_control.was_active()) + { + } +} + +void control_system::set_orbit_cam(::orbit_cam* orbit_cam) +{ + this->orbit_cam = orbit_cam; +} + +void control_system::set_nest(::nest* nest) +{ + this->nest = nest; +} + +void control_system::set_tool(model_instance* tool) +{ + this->tool = tool; +} + +void control_system::set_viewport(const float4& viewport) +{ + this->viewport = viewport; +} + +void control_system::handle_event(const mouse_moved_event& event) +{ + if (adjust_camera_control.is_active()) + { + bool invert_x = true; + bool invert_y = false; + + float rotation_factor = event.dx; + float elevation_factor = event.dy; + + if (invert_x) + { + rotation_factor *= -1.0f; + } + if (invert_y) + { + elevation_factor *= -1.0f; + } + + float rotation = vmq::radians(22.5f) * rotation_factor * timestep; + float elevation = orbit_cam->get_target_elevation() + elevation_factor * 0.25f * timestep; + elevation = std::min(max_elevation, std::max(min_elevation, elevation)); + + orbit_cam->rotate(rotation); + orbit_cam->set_target_elevation(elevation); + } + else if (!adjust_camera_control.was_active()) + { + mouse_position[0] = event.x; + mouse_position[1] = event.y; + } +} + diff --git a/src/antkeeper/systems/control-system.hpp b/src/antkeeper/systems/control-system.hpp new file mode 100644 index 0000000..d11e255 --- /dev/null +++ b/src/antkeeper/systems/control-system.hpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2020 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_CONTROL_SYSTEM_HPP +#define ANTKEEPER_CONTROL_SYSTEM_HPP + +#include "event/event-handler.hpp" +#include "input/input-events.hpp" +#include "input/control.hpp" +#include "input/control-set.hpp" +#include "scene/model-instance.hpp" + +class orbit_cam; +class nest; + +class control_system: + public event_handler +{ +public: + control_system(); + + void update(float dt); + + void set_orbit_cam(orbit_cam* orbit_cam); + void set_nest(::nest* nest); + void set_tool(model_instance* tool); + void set_viewport(const float4& viewport); + + control_set* get_control_set(); + control* get_move_forward_control(); + control* get_move_back_control(); + control* get_move_left_control(); + control* get_move_right_control(); + control* get_rotate_ccw_control(); + control* get_rotate_cw_control(); + control* get_tilt_up_control(); + control* get_tilt_down_control(); + control* get_zoom_in_control(); + control* get_zoom_out_control(); + control* get_adjust_camera_control(); + control* get_ascend_control(); + control* get_descend_control(); + control* get_toggle_view_control(); + control* get_tool_menu_control(); + +private: + virtual void handle_event(const mouse_moved_event& event); + + control_set control_set; + control move_forward_control; + control move_back_control; + control move_left_control; + control move_right_control; + control rotate_ccw_control; + control rotate_cw_control; + control tilt_up_control; + control tilt_down_control; + control zoom_in_control; + control zoom_out_control; + control adjust_camera_control; + control ascend_control; + control descend_control; + control toggle_view_control; + control tool_menu_control; + + float zoom_speed; + float min_elevation; + float max_elevation; + float near_focal_distance; + float far_focal_distance; + float near_movement_speed; + float far_movement_speed; + float near_fov; + float far_fov; + float near_clip_near; + float far_clip_near; + float near_clip_far; + float far_clip_far; + + float timestep; + float zoom; + orbit_cam* orbit_cam; + ::nest* nest; + model_instance* tool; + float2 mouse_position; + float4 viewport; +}; + +inline control_set* control_system::get_control_set() +{ + return &control_set; +} + +inline control* control_system::get_move_forward_control() +{ + return &move_forward_control; +} + +inline control* control_system::get_move_back_control() +{ + return &move_back_control; +} + +inline control* control_system::get_move_left_control() +{ + return &move_left_control; +} + +inline control* control_system::get_move_right_control() +{ + return &move_right_control; +} + +inline control* control_system::get_rotate_ccw_control() +{ + return &rotate_ccw_control; +} + +inline control* control_system::get_rotate_cw_control() +{ + return &rotate_cw_control; +} + +inline control* control_system::get_tilt_up_control() +{ + return &tilt_up_control; +} + +inline control* control_system::get_tilt_down_control() +{ + return &tilt_down_control; +} + +inline control* control_system::get_zoom_in_control() +{ + return &zoom_in_control; +} + +inline control* control_system::get_zoom_out_control() +{ + return &zoom_out_control; +} + +inline control* control_system::get_adjust_camera_control() +{ + return &adjust_camera_control; +} + +inline control* control_system::get_ascend_control() +{ + return &ascend_control; +} + +inline control* control_system::get_descend_control() +{ + return &descend_control; +} + +inline control* control_system::get_toggle_view_control() +{ + return &toggle_view_control; +} + +inline control* control_system::get_tool_menu_control() +{ + return &tool_menu_control; +} + +#endif // ANTKEEPER_CONTROL_SYSTEM_HPP + diff --git a/src/states/loading-state.cpp b/src/antkeeper/systems/entity-system.cpp similarity index 55% rename from src/states/loading-state.cpp rename to src/antkeeper/systems/entity-system.cpp index 495b311..a29e31a 100644 --- a/src/states/loading-state.cpp +++ b/src/antkeeper/systems/entity-system.cpp @@ -1,27 +1,25 @@ -/* - * Copyright (C) 2017-2019 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.hpp" - -void Game::enterLoadingState() -{} - -void Game::exitLoadingState() -{} - +/* + * Copyright (C) 2020 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 "entity-system.hpp" + +entity_system::entity_system(entt::registry& registry): + registry(registry) +{} + diff --git a/src/antkeeper/systems/entity-system.hpp b/src/antkeeper/systems/entity-system.hpp new file mode 100644 index 0000000..6604eff --- /dev/null +++ b/src/antkeeper/systems/entity-system.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 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_ENTITY_SYSTEM_HPP +#define ANTKEEPER_ENTITY_SYSTEM_HPP + +#include "updatable-system.hpp" +#include + +/** + * Abstract base class for updatable systems which operate on entities and entity components. + */ +class entity_system: public updatable_system +{ +public: + entity_system(entt::registry& registry); + +protected: + entt::registry& registry; +}; + +#endif // ANTKEEPER_ENTITY_SYSTEM_HPP + diff --git a/src/antkeeper/systems/locomotion-system.cpp b/src/antkeeper/systems/locomotion-system.cpp new file mode 100644 index 0000000..7997209 --- /dev/null +++ b/src/antkeeper/systems/locomotion-system.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 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 "locomotion-system.hpp" +#include "entity/components/collision-component.hpp" +#include "entity/components/locomotion-component.hpp" +#include "entity/components/transform-component.hpp" + +using namespace ecs; + +locomotion_system::locomotion_system(entt::registry& registry): + entity_system(registry) +{} + +void locomotion_system::update(double t, double dt) +{ + registry.view().each( + [&](auto entity, auto& transform, auto& locomotion) + { + }); +} + diff --git a/src/antkeeper/systems/locomotion-system.hpp b/src/antkeeper/systems/locomotion-system.hpp new file mode 100644 index 0000000..2cba1b0 --- /dev/null +++ b/src/antkeeper/systems/locomotion-system.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 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_LOCOMOTION_SYSTEM_HPP +#define ANTKEEPER_LOCOMOTION_SYSTEM_HPP + +#include "entity-system.hpp" + +class locomotion_system: + public entity_system +{ +public: + locomotion_system(entt::registry& registry); + virtual void update(double t, double dt); +}; + +#endif // ANTKEEPER_LOCOMOTION_SYSTEM_HPP + diff --git a/src/antkeeper/systems/model-system.cpp b/src/antkeeper/systems/model-system.cpp new file mode 100644 index 0000000..5d478d1 --- /dev/null +++ b/src/antkeeper/systems/model-system.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 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 "model-system.hpp" +#include "entity/components/transform-component.hpp" + +using namespace ecs; + +model_system::model_system(entt::registry& registry, ::scene& scene): + entity_system(registry), + scene(scene) +{ + registry.on_construct().connect<&model_system::on_model_construct>(this); + registry.on_replace().connect<&model_system::on_model_replace>(this); + registry.on_destroy().connect<&model_system::on_model_destroy>(this); +} + +void model_system::update(double t, double dt) +{ + registry.view().each( + [this](auto entity, auto& transform, auto& model) + { + model_instance* instance = model_instances[entity]; + + instance->set_transform(transform.transform); + + if (transform.warp) + { + instance->get_transform_tween().update(); + instance->update_tweens(); + transform.warp = false; + } + }); +} + +void model_system::update_model_and_materials(entt::entity entity, model_component& model) +{ + if (auto model_it = model_instances.find(entity); model_it != model_instances.end()) + { + model_it->second->set_model(model.model); + model_it->second->set_instanced((model.instance_count > 0), model.instance_count); + + for (auto material_it = model.materials.begin(); material_it != model.materials.end(); ++material_it) + { + model_it->second->set_material(material_it->first, material_it->second); + } + } +} + +void model_system::on_model_construct(entt::registry& registry, entt::entity entity, model_component& model) +{ + ::model_instance* model_instance = new ::model_instance(); + scene.add_object(model_instance); + model_instances[entity] = model_instance; + + update_model_and_materials(entity, model); +} + +void model_system::on_model_replace(entt::registry& registry, entt::entity entity, model_component& model) +{ + update_model_and_materials(entity, model); +} + +void model_system::on_model_destroy(entt::registry& registry, entt::entity entity) +{ + if (auto it = model_instances.find(entity); it != model_instances.end()) + { + scene.remove_object(it->second); + delete it->second; + model_instances.erase(it); + } +} + diff --git a/src/antkeeper/systems/model-system.hpp b/src/antkeeper/systems/model-system.hpp new file mode 100644 index 0000000..e16ed53 --- /dev/null +++ b/src/antkeeper/systems/model-system.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 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_MODEL_SYSTEM_HPP +#define ANTKEEPER_MODEL_SYSTEM_HPP + +#include "entity-system.hpp" +#include "scene/scene.hpp" +#include "scene/model-instance.hpp" +#include "entity/components/model-component.hpp" +#include + +class model_system: public entity_system +{ +public: + model_system(entt::registry& registry, ::scene& scene); + virtual void update(double t, double dt); + +private: + void update_model_and_materials(entt::entity entity, ecs::model_component& model); + + void on_model_construct(entt::registry& registry, entt::entity entity, ecs::model_component& model); + void on_model_replace(entt::registry& registry, entt::entity entity, ecs::model_component& model); + void on_model_destroy(entt::registry& registry, entt::entity entity); + + ::scene& scene; + std::unordered_map model_instances; +}; + +#endif // ANTKEEPER_MODEL_SYSTEM_HPP + diff --git a/src/antkeeper/systems/nest-system.cpp b/src/antkeeper/systems/nest-system.cpp new file mode 100644 index 0000000..eb24827 --- /dev/null +++ b/src/antkeeper/systems/nest-system.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2020 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 "nest-system.hpp" +#include "nest.hpp" + +using namespace ecs; + +nest_system::nest_system(entt::registry& registry, ::resource_manager* resource_manager): + entity_system(registry), + resource_manager(resource_manager) +{ + registry.on_construct().connect<&nest_system::on_nest_construct>(this); + registry.on_destroy().connect<&nest_system::on_nest_destroy>(this); +} + +nest_system::~nest_system() +{} + +void nest_system::update(double t, double dt) +{} + +void nest_system::on_nest_construct(entt::registry& registry, entt::entity entity, nest_component& component) +{ + // Allocate a nest + nest* nest = new ::nest(); + + // Setup initial nest parameters + nest->set_tunnel_radius(1.15f); + nest::shaft* central_shaft = nest->get_central_shaft(); + central_shaft->chirality = -1.0f; + central_shaft->rotation = vmq::radians(0.0f); + central_shaft->depth = {0.0f, 100.0f}; + central_shaft->current_depth = 0.0f; + central_shaft->radius = {0.0f, 5.0f}; + central_shaft->pitch = {4.0f, 8.0f}; + central_shaft->translation = {{{0.0f, 0.0f}, {20.0f, 11.0f}}}; + for (std::size_t i = 0; i < 4; ++i) + { + nest::chamber chamber; + chamber.shaft = central_shaft; + chamber.depth = (i + 1) * 23.0f; + chamber.rotation = vmq::radians(0.0f); + chamber.inner_radius = 4.0f; + chamber.outer_radius = 10.0f; + central_shaft->chambers.push_back(chamber); + } +} + +void nest_system::on_nest_destroy(entt::registry& registry, entt::entity entity) +{} + diff --git a/src/antkeeper/systems/nest-system.hpp b/src/antkeeper/systems/nest-system.hpp new file mode 100644 index 0000000..9208a7a --- /dev/null +++ b/src/antkeeper/systems/nest-system.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 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_NEST_SYSTEM_HPP +#define ANTKEEPER_NEST_SYSTEM_HPP + +#include "entity-system.hpp" +#include "entity/components/nest-component.hpp" + +class nest; +class resource_manager; + +class nest_system: public entity_system +{ +public: + nest_system(entt::registry& registry, ::resource_manager* resource_manager); + ~nest_system(); + virtual void update(double t, double dt); + +private: + resource_manager* resource_manager; + + void on_nest_construct(entt::registry& registry, entt::entity entity, ecs::nest_component& component); + void on_nest_destroy(entt::registry& registry, entt::entity entity); +}; + +#endif // ANTKEEPER_NEST_SYSTEM_HPP + diff --git a/src/antkeeper/systems/placement-system.cpp b/src/antkeeper/systems/placement-system.cpp new file mode 100644 index 0000000..b6130c6 --- /dev/null +++ b/src/antkeeper/systems/placement-system.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 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 "placement-system.hpp" +#include "entity/components/collision-component.hpp" +#include "entity/components/placement-component.hpp" +#include "entity/components/transform-component.hpp" +#include "entity/components/terrain-component.hpp" + +using namespace ecs; + +placement_system::placement_system(entt::registry& registry): + entity_system(registry) +{} + +void placement_system::update(double t, double dt) +{ + registry.view().each( + [&](auto entity, auto& transform, auto& placement) + { + bool intersection = false; + float a = std::numeric_limits::infinity(); + float3 pick; + + registry.view().each( + [&](auto entity, auto& transform, auto& collision) + { + // Transform ray into local space of collision component + vmq::transform inverse_transform = vmq::inverse(transform.transform); + float3 origin = inverse_transform * placement.ray.origin; + float3 direction = vmq::normalize(vmq::conjugate(transform.transform.rotation) * placement.ray.direction); + ray transformed_ray = {origin, direction}; + + // Broad phase AABB test + auto aabb_result = ray_aabb_intersection(transformed_ray, collision.bounds); + if (!std::get<0>(aabb_result)) + { + return; + } + + auto mesh_result = collision.mesh_accelerator.query_nearest(transformed_ray); + if (mesh_result) + { + intersection = true; + if (mesh_result->t < a) + { + a = mesh_result->t; + pick = placement.ray.extrapolate(a); + } + } + }); + + if (intersection) + { + transform.transform.translation = pick; + transform.warp = true; + registry.remove(entity); + } + }); +} + diff --git a/src/antkeeper/systems/placement-system.hpp b/src/antkeeper/systems/placement-system.hpp new file mode 100644 index 0000000..7ec32c8 --- /dev/null +++ b/src/antkeeper/systems/placement-system.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 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_PLACEMENT_SYSTEM_HPP +#define ANTKEEPER_PLACEMENT_SYSTEM_HPP + +#include "entity-system.hpp" + +class placement_system: + public entity_system +{ +public: + placement_system(entt::registry& registry); + virtual void update(double t, double dt); +}; + +#endif // ANTKEEPER_PLACEMENT_SYSTEM_HPP + diff --git a/src/antkeeper/systems/samara-system.cpp b/src/antkeeper/systems/samara-system.cpp new file mode 100644 index 0000000..40c2ed6 --- /dev/null +++ b/src/antkeeper/systems/samara-system.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 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 "samara-system.hpp" +#include "entity/components/transform-component.hpp" +#include "entity/components/samara-component.hpp" +#include "math.hpp" + +using namespace vmq::operators; +using namespace ecs; + +samara_system::samara_system(entt::registry& registry): + entity_system(registry) +{} + +void samara_system::update(double t, double dt) +{ + registry.view().each( + [&](auto entity, auto& samara, auto& transform) + { + samara.angle += samara.chirality * vmq::radians(360.0f * 6.0f) * dt; + + transform.transform.translation += samara.direction * 20.0f * (float)dt; + transform.transform.rotation = + vmq::angle_axis(samara.angle, float3{0, 1, 0}) * + vmq::angle_axis(vmq::radians(20.0f), float3{1, 0, 0}) * + ((samara.chirality < 0.0f) ? vmq::angle_axis(vmq::radians(180.0f), float3{0, 0, -1}) : vmq::quaternion{1, 0, 0, 0}); + + if (transform.transform.translation.y < 0.0f) + { + const float zone = 200.0f; + transform.transform.translation.x = frand(-zone, zone); + transform.transform.translation.y = frand(100.0f, 150.0f); + transform.transform.translation.z = frand(-zone, zone); + transform.warp = true; + + samara.chirality = (frand(0, 1) < 0.5f) ? -1.0f : 1.0f; + } + }); +} + diff --git a/src/antkeeper/systems/samara-system.hpp b/src/antkeeper/systems/samara-system.hpp new file mode 100644 index 0000000..6497888 --- /dev/null +++ b/src/antkeeper/systems/samara-system.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2020 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_SAMARA_SYSTEM_HPP +#define ANTKEEPER_SAMARA_SYSTEM_HPP + +#include "entity-system.hpp" + +class samara_system: public entity_system +{ +public: + samara_system(entt::registry& registry); + virtual void update(double t, double dt); +}; + +#endif // ANTKEEPER_SAMARA_SYSTEM_HPP + diff --git a/src/antkeeper/systems/subterrain-system.cpp b/src/antkeeper/systems/subterrain-system.cpp new file mode 100644 index 0000000..298d44b --- /dev/null +++ b/src/antkeeper/systems/subterrain-system.cpp @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2020 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 "subterrain-system.hpp" +#include "entity/components/model-component.hpp" +#include "entity/components/cavity-component.hpp" +#include "renderer/model.hpp" +#include "renderer/material.hpp" +#include "geometry/mesh-functions.hpp" +#include "renderer/vertex-attributes.hpp" +#include "rasterizer/vertex-attribute-type.hpp" +#include "rasterizer/drawing-mode.hpp" +#include "rasterizer/vertex-buffer.hpp" +#include "resources/resource-manager.hpp" +#include "marching-cubes.hpp" +#include "geometry/intersection.hpp" +#include "scene/scene.hpp" +#include "scene/model-instance.hpp" +#include "math.hpp" + +using namespace vmq::operators; +using namespace ecs; + +#include +#include +#include + +/** + * An octree containing cubes for the marching cubes algorithm. + */ +struct cube_tree +{ +public: + cube_tree(const aabb& bounds, int max_depth); + ~cube_tree(); + + const bool is_leaf() const; + const aabb& get_bounds() const; + + /// Subdivides all nodes intersecting with a region to the max depth. + void subdivide_max(const aabb& region); + + /// Fills a list with all leaf nodes that intersect with a region. + void query_leaves(std::list& nodes, const aabb& region); + void visit_leaves(const aabb& region, const std::function& f); + + /// Counts then number of nodes in the octree. + std::size_t size() const; + + cube_tree* children[8]; + float3 corners[8]; + float distances[8]; + const int max_depth; + const int depth; + const aabb bounds; + +private: + cube_tree(const aabb& bounds, int max_depth, int depth); + void subdivide(); +}; + +cube_tree::cube_tree(const aabb& bounds, int max_depth): + cube_tree(bounds, max_depth, 0) +{} + +cube_tree::cube_tree(const aabb& bounds, int max_depth, int depth): + bounds(bounds), + max_depth(max_depth), + depth(depth) +{ + corners[0] = {bounds.min_point.x, bounds.min_point.y, bounds.min_point.z}; + corners[1] = {bounds.max_point.x, bounds.min_point.y, bounds.min_point.z}; + corners[2] = {bounds.max_point.x, bounds.max_point.y, bounds.min_point.z}; + corners[3] = {bounds.min_point.x, bounds.max_point.y, bounds.min_point.z}; + corners[4] = {bounds.min_point.x, bounds.min_point.y, bounds.max_point.z}; + corners[5] = {bounds.max_point.x, bounds.min_point.y, bounds.max_point.z}; + corners[6] = {bounds.max_point.x, bounds.max_point.y, bounds.max_point.z}; + corners[7] = {bounds.min_point.x, bounds.max_point.y, bounds.max_point.z}; + + for (int i = 0; i < 8; ++i) + { + children[i] = nullptr; + distances[i] = -std::numeric_limits::infinity(); + + // For outside normals + //distances[i] = std::numeric_limits::infinity(); + } +} + +cube_tree::~cube_tree() +{ + for (cube_tree* child: children) + delete child; +} + +void cube_tree::subdivide_max(const aabb& region) +{ + if (depth != max_depth && aabb_aabb_intersection(bounds, region)) + { + if (is_leaf()) + subdivide(); + + for (cube_tree* child: children) + child->subdivide_max(region); + } +} + +void cube_tree::query_leaves(std::list& nodes, const aabb& region) +{ + if (aabb_aabb_intersection(bounds, region)) + { + if (is_leaf()) + { + nodes.push_back(this); + } + else + { + for (cube_tree* child: children) + child->query_leaves(nodes, region); + } + } +} + +void cube_tree::visit_leaves(const aabb& region, const std::function& f) +{ + if (aabb_aabb_intersection(bounds, region)) + { + if (is_leaf()) + { + f(*this); + } + else + { + for (cube_tree* child: children) + child->visit_leaves(region, f); + } + } +} + +std::size_t cube_tree::size() const +{ + std::size_t node_count = 1; + if (!is_leaf()) + { + for (cube_tree* child: children) + node_count += child->size(); + } + + return node_count; +} + +inline const bool cube_tree::is_leaf() const +{ + return (children[0] == nullptr); +} + +inline const aabb& cube_tree::get_bounds() const +{ + return bounds; +} + +void cube_tree::subdivide() +{ + const float3 center = (bounds.min_point + bounds.max_point) * 0.5f; + + for (int i = 0; i < 8; ++i) + { + aabb child_bounds; + for (int j = 0; j < 3; ++j) + { + child_bounds.min_point[j] = std::min(corners[i][j], center[j]); + child_bounds.max_point[j] = std::max(corners[i][j], center[j]); + } + + children[i] = new cube_tree(child_bounds, max_depth, depth + 1); + } +} + +subterrain_system::subterrain_system(entt::registry& registry, ::resource_manager* resource_manager): + entity_system(registry), + resource_manager(resource_manager) +{ + + // Load subterrain materials + subterrain_inside_material = resource_manager->load("subterrain-inside.mtl"); + subterrain_inside_material = resource_manager->load("subterrain-outside.mtl"); + + // Allocate subterrain model + subterrain_model = new model(); + + // Create inside model group + subterrain_inside_group = subterrain_model->add_group("inside"); + subterrain_inside_group->set_material(resource_manager->load("subterrain-inside.mtl")); + subterrain_inside_group->set_drawing_mode(drawing_mode::triangles); + subterrain_inside_group->set_start_index(0); + subterrain_inside_group->set_index_count(0); + + // Create outside model group + subterrain_outside_group = subterrain_model->add_group("outside"); + subterrain_outside_group->set_material(resource_manager->load("subterrain-outside.mtl")); + subterrain_outside_group->set_drawing_mode(drawing_mode::triangles); + subterrain_outside_group->set_start_index(0); + subterrain_outside_group->set_index_count(0); + + // Determine vertex size (position, normal, barycentric) + subterrain_model_vertex_size = 3 + 3 + 3; + subterrain_model_vertex_stride = subterrain_model_vertex_size * sizeof(float); + + // Bind vertex attributes + vertex_buffer* vbo = subterrain_model->get_vertex_buffer(); + vertex_array* vao = subterrain_model->get_vertex_array(); + std::size_t offset = 0; + vao->bind_attribute(VERTEX_POSITION_LOCATION, *vbo, 3, vertex_attribute_type::float_32, subterrain_model_vertex_stride, 0); + offset += 3; + vao->bind_attribute(VERTEX_NORMAL_LOCATION, *vbo, 3, vertex_attribute_type::float_32, subterrain_model_vertex_stride, sizeof(float) * offset); + offset += 3; + vao->bind_attribute(VERTEX_BARYCENTRIC_LOCATION, *vbo, 3, vertex_attribute_type::float_32, subterrain_model_vertex_stride, sizeof(float) * offset); + offset += 3; + + // Calculate adjusted bounds to fit isosurface resolution + //isosurface_resolution = 0.325f; + isosurface_resolution = 0.5f; + float ideal_volume_size = 200.0f; + int octree_depth = std::floor(std::log(ideal_volume_size / isosurface_resolution) / std::log(2)) + 1; + float adjusted_volume_size = std::pow(2.0f, octree_depth) * isosurface_resolution; + + // Set subterrain bounds + subterrain_bounds.min_point = float3{-0.5f, -1.0f, -0.5f} * adjusted_volume_size; + subterrain_bounds.max_point = float3{ 0.5f, 0.0f, 0.5f} * adjusted_volume_size; + + // Set subterrain model bounds + subterrain_model->set_bounds(subterrain_bounds); + + // Allocate cube tree + cube_tree = new ::cube_tree(subterrain_bounds, octree_depth); + + // Allocate mesh + subterrain_mesh = new 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; + //auto subterrain_entity = registry.create(); + //registry.assign(subterrain_entity, subterrain_model); + + subterrain_model_instance = new model_instance(subterrain_model); + scene->add_object(subterrain_model_instance); + } + + bool digging = false; + + registry.view().each( + [this, &digging](auto entity, auto& cavity) + { + this->dig(cavity.position, cavity.radius); + registry.destroy(entity); + + digging = true; + }); + + if (digging) + { + std::cout << "regenerating subterrain mesh...\n"; + regenerate_subterrain_mesh(); + std::cout << "regenerating subterrain mesh... done\n"; + + std::cout << "regenerating subterrain model...\n"; + regenerate_subterrain_model(); + std::cout << "regenerating subterrain model... done\n"; + } +} + +void subterrain_system::set_scene(::scene* scene) +{ + this->scene = scene; +} + +void subterrain_system::regenerate_subterrain_mesh() +{ + delete subterrain_mesh; + subterrain_mesh = new mesh(); + subterrain_vertices.clear(); + subterrain_triangles.clear(); + subterrain_vertex_map.clear(); + + std::cout << "marching...\n"; + merged = 0; + march(cube_tree); + std::cout << "merged " << merged << " vertices\n"; + std::cout << "marching...done\n"; + + std::cout << "vertex count: " << subterrain_vertices.size() << std::endl; + std::cout << "triangle count: " << subterrain_triangles.size() << std::endl; + + 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) + march(child); + return; + } + else if (node->depth != node->max_depth) + { + return; + } + + // Get node bounds + const aabb& bounds = node->get_bounds(); + + // Polygonize cube + float vertex_buffer[12 * 3]; + std::uint_fast8_t vertex_count; + std::int_fast8_t triangle_buffer[5 * 3]; + std::uint_fast8_t triangle_count; + const float* corners = &node->corners[0][0]; + const float* distances = &node->distances[0]; + mc::polygonize(vertex_buffer, &vertex_count, triangle_buffer, &triangle_count, corners, distances); + + // Remap local vertex buffer indices (0-11) to mesh vertex indices + std::uint_fast32_t vertex_remap[12]; + for (int i = 0; i < vertex_count; ++i) + { + const float3& vertex = reinterpret_cast(vertex_buffer[i * 3]); + + if (auto it = subterrain_vertex_map.find(vertex); it != subterrain_vertex_map.end()) + { + vertex_remap[i] = it->second; + ++merged; + } + else + { + vertex_remap[i] = subterrain_vertices.size(); + subterrain_vertex_map[vertex] = subterrain_vertices.size(); + subterrain_vertices.push_back(vertex); + } + } + + // Add triangles + for (std::uint_fast32_t i = 0; i < triangle_count; ++i) + { + subterrain_triangles.push_back( + { + vertex_remap[triangle_buffer[i * 3]], + vertex_remap[triangle_buffer[i * 3 + 1]], + vertex_remap[triangle_buffer[i * 3 + 2]] + }); + } +} + +void subterrain_system::regenerate_subterrain_model() +{ + float* face_normals = new float[subterrain_mesh->get_faces().size() * 3]; + calculate_face_normals(face_normals, *subterrain_mesh); + + static const float3 barycentric_coords[3] = + { + float3{1, 0, 0}, + float3{0, 1, 0}, + float3{0, 0, 1} + }; + + float* vertex_data = new float[subterrain_model_vertex_size * subterrain_mesh->get_faces().size() * 3]; + float* v = vertex_data; + for (std::size_t i = 0; i < subterrain_mesh->get_faces().size(); ++i) + { + mesh::face* face = subterrain_mesh->get_faces()[i]; + mesh::edge* ab = face->edge; + mesh::edge* bc = face->edge->next; + mesh::edge* ca = face->edge->previous; + mesh::vertex* a = ab->vertex; + mesh::vertex* b = bc->vertex; + mesh::vertex* c = ca->vertex; + mesh::vertex* vertices[3] = {a, b, c}; + + for (std::size_t j = 0; j < 3; ++j) + { + mesh::vertex* vertex = vertices[j]; + + float3 n = {0, 0, 0}; + mesh::edge* start = vertex->edge; + mesh::edge* edge = start; + do + { + if (edge->face) + { + n += reinterpret_cast(face_normals[edge->face->index * 3]); + } + + edge = edge->previous->symmetric; + } + while (edge != start); + n = vmq::normalize(n); + + //float3 n = reinterpret_cast(face_normals[i * 3]); + + *(v++) = vertex->position[0]; + *(v++) = vertex->position[1]; + *(v++) = vertex->position[2]; + + *(v++) = n[0]; + *(v++) = n[1]; + *(v++) = n[2]; + + *(v++) = barycentric_coords[j][0]; + *(v++) = barycentric_coords[j][1]; + *(v++) = barycentric_coords[j][2]; + } + } + + // Resized VBO and upload vertex data + vertex_buffer* vbo = subterrain_model->get_vertex_buffer(); + vbo->resize(subterrain_mesh->get_faces().size() * 3 * subterrain_model_vertex_stride, vertex_data); + + // Deallocate vertex data + delete[] face_normals; + delete[] vertex_data; + + // 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 + aabb region = {position, position}; + for (int i = 0; i < 3; ++i) + { + region.min_point[i] -= radius + isosurface_resolution; + region.max_point[i] += radius + isosurface_resolution; + } + + // Subdivide the octree to the maximum depth within the region + cube_tree->subdivide_max(region); + + // Query all octree leaf nodes within the region + std::list<::cube_tree*> nodes; + cube_tree->visit_leaves(region, + [&position, radius](::cube_tree& node) + { + for (int i = 0; i < 8; ++i) + { + // For outside normals (also set node initial distance to +infinity) + //float distance = vmq::length(node->corners[i] - position) - radius; + // if (distance < node->distances[i]) + + float distance = radius - vmq::length(node.corners[i] - position); + if (distance > node.distances[i]) + node.distances[i] = distance; + } + }); +} + diff --git a/src/antkeeper/systems/subterrain-system.hpp b/src/antkeeper/systems/subterrain-system.hpp new file mode 100644 index 0000000..6aade28 --- /dev/null +++ b/src/antkeeper/systems/subterrain-system.hpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2020 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_SUBTERRAIN_SYSTEM_HPP +#define ANTKEEPER_SUBTERRAIN_SYSTEM_HPP + +#include "entity-system.hpp" +#include "geometry/mesh.hpp" +#include "geometry/aabb.hpp" +#include + +class resource_manager; +class model; +class model_group; +class material; +struct cube_tree; +class scene; +class model_instance; + +using namespace vmq::types; + +template +struct epsilon +{ + static const double value; +}; + +template +const double epsilon::value = static_cast(Mantissa) * std::pow(10.0, Exponent); + +typedef epsilon<1, -5> epsilon_1en5; + +template +struct vector_hasher +{ + typedef vmq::vector vector_type; + + std::size_t operator()(const vector_type& v) const noexcept + { + static const T inverse_epsilon = T(1) / Epsilon::value; + + std::size_t hash = 0; + for (std::size_t i = 0; i < N; ++i) + { + std::int64_t j = static_cast(v[i] * inverse_epsilon); + hash ^= std::hash()(j) + 0x9e3779b9 + (hash << 6) + (hash >> 2); + } + + return hash; + } +}; + +template +struct vector_equals +{ + typedef vmq::vector vector_type; + + bool operator()(const vector_type& a, const vector_type& b) const noexcept + { + for (std::size_t i = 0; i < N; ++i) + { + if (std::fabs(b[i] - a[i]) >= Epsilon::value) + return false; + } + + return true; + } +}; + +class subterrain_system: public entity_system +{ +public: + subterrain_system(entt::registry& registry, ::resource_manager* resource_manager); + ~subterrain_system(); + virtual void update(double t, double dt); + + void set_scene(::scene* scene); + +private: + void regenerate_subterrain_mesh(); + void march(::cube_tree* node); + void regenerate_subterrain_model(); + void dig(const float3&position, float radius); + float distance(const ::cube_tree& node, const float3& sample) const; + + resource_manager* resource_manager; + mesh* subterrain_mesh; + model* subterrain_model; + material* subterrain_inside_material; + material* subterrain_outside_material; + model_group* subterrain_inside_group; + model_group* subterrain_outside_group; + int subterrain_model_vertex_size; + int subterrain_model_vertex_stride; + aabb subterrain_bounds; + ::cube_tree* cube_tree; + std::vector subterrain_vertices; + std::vector> subterrain_triangles; + float isosurface_resolution; + bool first_run; + int merged; + + std::unordered_map< + float3, + std::uint_fast32_t, + vector_hasher, + vector_equals> subterrain_vertex_map; + + ::scene* scene; + model_instance* subterrain_model_instance; +}; + +#endif // ANTKEEPER_SUBTERRAIN_SYSTEM_HPP + diff --git a/src/antkeeper/systems/terrain-system.cpp b/src/antkeeper/systems/terrain-system.cpp new file mode 100644 index 0000000..a233be1 --- /dev/null +++ b/src/antkeeper/systems/terrain-system.cpp @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2020 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 "terrain-system.hpp" +#include "entity/components/model-component.hpp" +#include "entity/components/collision-component.hpp" +#include "entity/components/transform-component.hpp" +#include "renderer/model.hpp" +#include "geometry/mesh.hpp" +#include "geometry/mesh-functions.hpp" +#include "renderer/vertex-attributes.hpp" +#include "rasterizer/vertex-attribute-type.hpp" +#include "rasterizer/drawing-mode.hpp" +#include "rasterizer/vertex-buffer.hpp" +#include "resources/resource-manager.hpp" +#include "resources/image.hpp" +#include + +using namespace vmq::operators; +using namespace ecs; + +terrain_system::terrain_system(entt::registry& registry, ::resource_manager* resource_manager): + entity_system(registry), + resource_manager(resource_manager) +{ + registry.on_construct().connect<&terrain_system::on_terrain_construct>(this); + registry.on_destroy().connect<&terrain_system::on_terrain_destroy>(this); + + heightmap = resource_manager->load("grassland-heightmap.png"); + heightmap_size = 2000.0f; + heightmap_scale = 150.0f; +} + +terrain_system::~terrain_system() +{} + +void terrain_system::update(double t, double dt) +{ + registry.view().each( + [this](auto entity, auto& terrain, auto& transform) + { + transform.transform.translation = float3{(float)terrain.x * patch_size, 0.0f, (float)terrain.z * patch_size}; + transform.warp = true; + }); +} + +void terrain_system::set_patch_size(float size) +{ + patch_size = size; +} + +mesh* terrain_system::generate_terrain_mesh(float size, int subdivisions) +{ + // Allocate terrain mesh + mesh* terrain_mesh = new mesh(); + + // Determine vertex count and placement + int columns = static_cast(std::pow(2, subdivisions)); + int rows = columns; + int vertex_count = (columns + 1) * (rows + 1); + float vertex_increment = size / static_cast(columns); + float radius = size * 0.5f; + + // Generate mesh vertices + float3 position = {0.0f, 0.0f, -radius}; + for (int i = 0; i <= rows; ++i) + { + position[0] = -radius; + + for (int j = 0; j <= columns; ++j) + { + terrain_mesh->add_vertex(position); + position[0] += vertex_increment; + + } + + position[2] += vertex_increment; + } + + + // Function to eliminate duplicate edges + std::map, mesh::edge*> edge_map; + auto add_or_find_edge = [&](mesh::vertex* start, mesh::vertex* end) -> mesh::edge* + { + mesh::edge* edge; + if (auto it = edge_map.find({start->index, end->index}); it != edge_map.end()) + { + edge = it->second; + } + else + { + edge = terrain_mesh->add_edge(start, end); + edge_map[{start->index, end->index}] = edge; + edge_map[{end->index, start->index}] = edge->symmetric; + } + + return edge; + }; + + const std::vector& vertices = terrain_mesh->get_vertices(); + for (int i = 0; i < rows; ++i) + { + for (int j = 0; j < columns; ++j) + { + mesh::vertex* a = vertices[i * (columns + 1) + j]; + mesh::vertex* b = vertices[(i + 1) * (columns + 1) + j]; + mesh::vertex* c = vertices[i * (columns + 1) + j + 1]; + mesh::vertex* d = vertices[(i + 1) * (columns + 1) + j + 1]; + + // +---+---+ + // | \ | / | + // |---+---| + // | / | \ | + // +---+---+ + if ((j % 2) == (i % 2)) + { + mesh::edge* ab = add_or_find_edge(a, b); + mesh::edge* bd = add_or_find_edge(b, d); + mesh::edge* da = add_or_find_edge(d, a); + + mesh::edge* ca = add_or_find_edge(c, a); + mesh::edge* ad = da->symmetric; + mesh::edge* dc = add_or_find_edge(d, c); + + // a---c + // | \ | + // b---d + terrain_mesh->add_face({ab, bd, da}); + terrain_mesh->add_face({ca, ad, dc}); + } + else + { + mesh::edge* ab = add_or_find_edge(a, b); + mesh::edge* bc = add_or_find_edge(b, c); + mesh::edge* ca = add_or_find_edge(c, a); + mesh::edge* cb = bc->symmetric; + mesh::edge* bd = add_or_find_edge(b, d); + mesh::edge* dc = add_or_find_edge(d, c); + + // a---c + // | / | + // b---d + terrain_mesh->add_face({ab, bc, ca}); + terrain_mesh->add_face({cb, bd, dc}); + } + } + } + + return terrain_mesh; +} + +model* terrain_system::generate_terrain_model(mesh* terrain_mesh) +{ + // Allocate model + model* terrain_model = new model(); + + // Get model's VAO and VBO + vertex_buffer* vbo = terrain_model->get_vertex_buffer(); + vertex_array* vao = terrain_model->get_vertex_array(); + + // Resize VBO + int vertex_size = 3 + 3 + 3; + int vertex_stride = vertex_size * sizeof(float); + vbo->resize(terrain_mesh->get_faces().size() * 3 * vertex_stride, nullptr); + + // Bind vertex attributes + std::size_t offset = 0; + vao->bind_attribute(VERTEX_POSITION_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, 0); + offset += 3; + vao->bind_attribute(VERTEX_NORMAL_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset); + offset += 3; + vao->bind_attribute(VERTEX_BARYCENTRIC_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset); + offset += 3; + + // Create model group + model_group* model_group = terrain_model->add_group("terrain"); + model_group->set_material(resource_manager->load("grassland-terrain.mtl")); + model_group->set_drawing_mode(drawing_mode::triangles); + model_group->set_start_index(0); + model_group->set_index_count(terrain_mesh->get_faces().size() * 3); + + return terrain_model; +} + +void terrain_system::project_terrain_mesh(mesh* terrain_mesh, const terrain_component& component) +{ + + float offset_x = (float)component.x * patch_size; + float offset_z = (float)component.z * patch_size; + + for (mesh::vertex* vertex: terrain_mesh->get_vertices()) + { + int pixel_x = (vertex->position[0] + offset_x + heightmap_size * 0.5f) / heightmap_size * (float)(heightmap->get_width() - 1); + int pixel_y = (vertex->position[2] + offset_z + heightmap_size * 0.5f) / heightmap_size * (float)(heightmap->get_height() - 1); + + pixel_x = std::max(0, std::min(heightmap->get_width() - 1, pixel_x)); + pixel_y = std::max(0, std::min(heightmap->get_height() - 1, pixel_y)); + + int pixel_index = (pixel_y * heightmap->get_width() + pixel_x) * heightmap->get_channels(); + const unsigned char* pixel = static_cast(heightmap->get_pixels()) + pixel_index; + + float elevation = (static_cast(*pixel) / 255.0f - 0.5) * heightmap_scale; + vertex->position[1] = elevation; + } + +} + +void terrain_system::update_terrain_model(model* terrain_model, mesh* terrain_mesh) +{ + aabb bounds = + { + {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}, + {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()} + }; + + static const float3 barycentric_coords[3] = + { + float3{1, 0, 0}, + float3{0, 1, 0}, + float3{0, 0, 1} + }; + + int triangle_count = terrain_mesh->get_faces().size(); + int vertex_count = triangle_count * 3; + int vertex_size = 3 + 3 + 3; + + // Allocate vertex data + float* vertex_data = new float[vertex_size * vertex_count]; + + // Allocate face normals + float* face_normals = new float[terrain_mesh->get_faces().size() * 3]; + calculate_face_normals(face_normals, *terrain_mesh); + + // Generate vertex data + float* v = vertex_data; + const std::vector& faces = terrain_mesh->get_faces(); + for (int i = 0; i < triangle_count; ++i) + { + const mesh::face* triangle = faces[i]; + const mesh::vertex* a = triangle->edge->vertex; + const mesh::vertex* b = triangle->edge->next->vertex; + const mesh::vertex* c = triangle->edge->previous->vertex; + const mesh::vertex* abc[] = {a, b, c}; + + for (int j = 0; j < 3; ++j) + { + const mesh::vertex* vertex = abc[j]; + + float3 n = {0, 0, 0}; + mesh::edge* start = vertex->edge; + mesh::edge* edge = start; + do + { + if (edge->face) + { + n += reinterpret_cast(face_normals[edge->face->index * 3]); + } + + edge = edge->previous->symmetric; + } + while (edge != start); + n = vmq::normalize(n); + + *(v++) = vertex->position[0]; + *(v++) = vertex->position[1]; + *(v++) = vertex->position[2]; + *(v++) = n[0]; + *(v++) = n[1]; + *(v++) = n[2]; + *(v++) = barycentric_coords[j][0]; + *(v++) = barycentric_coords[j][1]; + *(v++) = barycentric_coords[j][2]; + + // Add position to bounds + for (int i = 0; i < 3; ++i) + { + bounds.min_point[i] = std::min(bounds.min_point[i], vertex->position[i]); + bounds.max_point[i] = std::max(bounds.max_point[i], vertex->position[i]); + } + } + } + + // Update bounds + terrain_model->set_bounds(bounds); + + // Update VBO + terrain_model->get_vertex_buffer()->update(0, vertex_count * vertex_size * sizeof(float), vertex_data); + + // Free vertex data + delete[] face_normals; + delete[] vertex_data; +} + +void terrain_system::on_terrain_construct(entt::registry& registry, entt::entity entity, terrain_component& component) +{ + mesh* terrain_mesh = generate_terrain_mesh(patch_size, component.subdivisions); + model* terrain_model = generate_terrain_model(terrain_mesh); + project_terrain_mesh(terrain_mesh, component); + update_terrain_model(terrain_model, terrain_mesh); + + // Assign the entity a collision component with the terrain mesh + collision_component collision; + collision.mesh = terrain_mesh; + collision.bounds = calculate_bounds(*terrain_mesh); + collision.mesh_accelerator.build(*collision.mesh); + registry.assign_or_replace(entity, collision); + + // Assign the entity a model component with the terrain model + model_component model; + model.model = terrain_model; + model.instance_count = 0; + registry.assign_or_replace(entity, model); + + // Assign the entity a transform component + transform_component transform; + transform.transform = vmq::identity_transform; + transform.transform.translation = float3{(float)component.x * patch_size, 0.0f, (float)component.z * patch_size}; + transform.warp = true; + registry.assign_or_replace(entity, transform); +} + +void terrain_system::on_terrain_destroy(entt::registry& registry, entt::entity entity) +{ + /* + if (auto it = terrain_map.find(entity); it != terrain_map.end()) + { + delete std::get<0>(it->second); + delete std::get<1>(it->second); + terrain_map.erase(it); + } + */ +} + diff --git a/src/antkeeper/systems/terrain-system.hpp b/src/antkeeper/systems/terrain-system.hpp new file mode 100644 index 0000000..1eaaf9c --- /dev/null +++ b/src/antkeeper/systems/terrain-system.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 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_TERRAIN_SYSTEM_HPP +#define ANTKEEPER_TERRAIN_SYSTEM_HPP + +#include "entity-system.hpp" +#include "entity/components/terrain-component.hpp" + +class terrain; +class resource_manager; +class mesh; +class model; +class image; + +class terrain_system: public entity_system +{ +public: + terrain_system(entt::registry& registry, ::resource_manager* resource_manager); + ~terrain_system(); + virtual void update(double t, double dt); + + /** + * Sets the size of a single terrain patch. + */ + void set_patch_size(float size); + +private: + mesh* generate_terrain_mesh(float size, int subdivisions); + model* generate_terrain_model(mesh* terrain_mesh); + void project_terrain_mesh(mesh* terrain_mesh, const ecs::terrain_component& component); + void update_terrain_model(model* terrain_model, mesh* terrain_mesh); + + void on_terrain_construct(entt::registry& registry, entt::entity entity, ecs::terrain_component& component); + void on_terrain_destroy(entt::registry& registry, entt::entity entity); + + resource_manager* resource_manager; + float patch_size; + float heightmap_size; + float heightmap_scale; + image* heightmap; +}; + +#endif // ANTKEEPER_TERRAIN_SYSTEM_HPP + diff --git a/src/antkeeper/systems/tool-system.cpp b/src/antkeeper/systems/tool-system.cpp new file mode 100644 index 0000000..2d2bf74 --- /dev/null +++ b/src/antkeeper/systems/tool-system.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2020 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 "tool-system.hpp" +#include "entity/components/collision-component.hpp" +#include "entity/components/tool-component.hpp" +#include "entity/components/transform-component.hpp" +#include "scene/camera.hpp" +#include "orbit-cam.hpp" +#include "geometry/mesh.hpp" +#include "geometry/intersection.hpp" +#include + +using namespace vmq::operators; +using namespace ecs; + +tool_system::tool_system(entt::registry& registry): + entity_system(registry), + camera(nullptr), + orbit_cam(orbit_cam), + viewport{0, 0, 0, 0}, + mouse_position{0, 0}, + pick_enabled(true) +{} + +void tool_system::update(double t, double dt) +{ + if (!camera) + return; + + float3 pick_near = camera->unproject({mouse_position[0], viewport[3] - mouse_position[1], 0.0f}, viewport); + float3 pick_far = camera->unproject({mouse_position[0], viewport[3] - mouse_position[1], 1.0f}, viewport); + float3 pick_origin = pick_near; + float3 pick_direction = vmq::normalize(pick_far - pick_near); + ray picking_ray = {pick_near, pick_direction}; + + float a = std::numeric_limits::infinity(); + bool intersection = false; + float3 pick; + + registry.view().each( + [&](auto entity, auto& transform, auto& collision) + { + vmq::transform inverse_transform = vmq::inverse(transform.transform); + float3 origin = inverse_transform * pick_origin; + float3 direction = vmq::normalize(vmq::conjugate(transform.transform.rotation) * pick_direction); + ray transformed_ray = {origin, direction}; + + // Broad phase AABB test + auto aabb_result = ray_aabb_intersection(transformed_ray, collision.bounds); + if (!std::get<0>(aabb_result)) + { + return; + } + + auto mesh_result = collision.mesh_accelerator.query_nearest(transformed_ray); + if (mesh_result) + { + intersection = true; + if (mesh_result->t < a) + { + a = mesh_result->t; + pick = picking_ray.extrapolate(a); + } + } + + /* + // Narrow phase mesh test + auto mesh_result = ray_mesh_intersection(transformed_ray, *collision.mesh); + if (std::get<0>(mesh_result)) + { + intersection = true; + if (std::get<1>(mesh_result) < a) + { + a = std::get<1>(mesh_result); + pick = picking_ray.extrapolate(a); + } + } + */ + }); + + const float3& camera_position = camera->get_translation(); + float3 pick_planar_position = float3{pick.x, 0, pick.z}; + float3 camera_planar_position = float3{camera_position.x, 0, camera_position.z}; + + float pick_angle = 0.0f; + float3 pick_planar_direction = vmq::normalize(pick_planar_position - camera_planar_position); + float3 camera_planar_focal_point = float3{orbit_cam->get_focal_point().x, 0, orbit_cam->get_focal_point().z}; + float3 camera_planar_direction = vmq::normalize(camera_planar_focal_point - camera_planar_position); + if (std::fabs(vmq::length_squared(camera_planar_direction - pick_planar_direction) > 0.0001f)) + { + pick_angle = std::acos(vmq::dot(camera_planar_direction, pick_planar_direction)); + if (vmq::dot(vmq::cross(camera_planar_direction, pick_planar_direction), float3{0, 1, 0}) < 0.0f) + pick_angle = -pick_angle; + } + + + registry.view().each( + [&](auto entity, auto& tool, auto& transform) + { + if (!tool.active) + return; + + if (intersection) + { + transform.transform.translation = pick; + } + + vmq::quaternion rotation = vmq::angle_axis(orbit_cam->get_azimuth() + pick_angle, float3{0, 1, 0}); + transform.transform.rotation = rotation; + }); +} + +void tool_system::set_camera(const ::camera* camera) +{ + this->camera = camera; +} + +void tool_system::set_orbit_cam(const ::orbit_cam* orbit_cam) +{ + this->orbit_cam = orbit_cam; +} + +void tool_system::set_viewport(const float4& viewport) +{ + this->viewport = viewport; +} + +void tool_system::set_pick(bool enabled) +{ + pick_enabled = enabled; +} + +void tool_system::handle_event(const mouse_moved_event& event) +{ + if (pick_enabled) + { + mouse_position[0] = event.x; + mouse_position[1] = event.y; + } +} + diff --git a/src/antkeeper/systems/tool-system.hpp b/src/antkeeper/systems/tool-system.hpp new file mode 100644 index 0000000..74526ce --- /dev/null +++ b/src/antkeeper/systems/tool-system.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 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_TOOL_SYSTEM_HPP +#define ANTKEEPER_TOOL_SYSTEM_HPP + +#include "entity-system.hpp" +#include "event/event-handler.hpp" +#include "input/input-events.hpp" +#include +using namespace vmq::types; + +class camera; +class orbit_cam; + +class tool_system: + public entity_system, + public event_handler +{ +public: + tool_system(entt::registry& registry); + virtual void update(double t, double dt); + + void set_camera(const camera* camera); + void set_orbit_cam(const orbit_cam* camera); + void set_viewport(const float4& viewport); + void set_pick(bool enabled); + +private: + virtual void handle_event(const mouse_moved_event& event); + + const camera* camera; + const orbit_cam* orbit_cam; + float4 viewport; + float2 mouse_position; + bool pick_enabled; +}; + +#endif // ANTKEEPER_TOOL_SYSTEM_HPP + diff --git a/src/antkeeper/systems/ui-system.cpp b/src/antkeeper/systems/ui-system.cpp new file mode 100644 index 0000000..4668ad2 --- /dev/null +++ b/src/antkeeper/systems/ui-system.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2020 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 "ui-system.hpp" +#include "input/control.hpp" +#include "resources/resource-manager.hpp" + +using namespace vmq::operators; + +ui_system::ui_system(::resource_manager* resource_manager): + resource_manager(resource_manager), + tool_menu_control(nullptr) +{ + // Setup camera + camera.look_at({0.0f, 0.0f, 500.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}); + + // Setup lighting + indirect_light.set_intensity(0.25f); + indirect_light.update_tweens(); + direct_light.look_at({-0.1f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}); + direct_light.set_intensity(1.0f); + direct_light.update_tweens(); + + // Setup modal background + modal_bg_material.set_shader_program(resource_manager->load("ui-element-untextured.glsl")); + modal_bg_material.set_flags(1); + modal_bg_material.add_property("tint")->set_value({0, 0, 0, 0.25f}); + modal_bg.set_material(&modal_bg_material); + modal_bg.set_translation({0, 0, -10.0f}); + + // Setup tool selector background + tool_selector_bg.set_material(resource_manager->load("tool-selector.mtl")); + tool_selector_bg.set_translation({0, 0, -4.0f}); + tool_selector_bg.set_scale({270, 270, 270}); + + // Setup tool selector ant + tool_selector_ant.set_model(resource_manager->load("worker-ant.obj")); + tool_selector_ant.set_scale({350, 350, 350}); + tool_selector_ant.set_rotation(vmq::angle_axis(vmq::radians(180.0f), {0, 0, 1}) * vmq::angle_axis(vmq::radians(90.0f), {1, 0, 0})); + tool_selector_ant.update_tweens(); + + // Setup energy symbol + energy_symbol.set_model(resource_manager->load("energy.obj")); + energy_symbol.set_scale({30, 30, 30}); + energy_symbol.update_tweens(); + energy_symbol.set_active(false); + + // Setup scene + scene.add_object(&camera); + scene.add_object(&indirect_light); + scene.add_object(&direct_light); + scene.add_object(&energy_symbol); +} + +void ui_system::update(float dt) +{} + +void ui_system::set_viewport(const float4& viewport) +{ + this->viewport = viewport; + + // Calculate viewport center + viewport_center[0] = (viewport[2] - viewport[0]) * 0.5f; + viewport_center[1] = (viewport[3] - viewport[1]) * 0.5f; + + // Recalculate orthographic projection + float clip_left = -viewport[2] * 0.5f; + float clip_right = viewport[2] * 0.5f; + float clip_top = -viewport[3] * 0.5f; + float clip_bottom = viewport[3] * 0.5f; + float clip_near = 0.0f; + float clip_far = 1000.0f; + camera.set_orthographic(clip_left, clip_right, clip_top, clip_bottom, clip_near, clip_far); + + energy_symbol.set_translation({viewport[2] * 0.25f, 0.0f, 0.0f}); + energy_symbol.update_tweens(); + + // Resize modal BG + modal_bg.set_scale({viewport[2] * 0.5f, viewport[3] * 0.5f, 1.0f}); + modal_bg.update_tweens(); +} + +void ui_system::set_tool_menu_control(control* control) +{ + tool_menu_control = control; + tool_menu_control->set_activated_callback(std::bind(&ui_system::open_tool_menu, this)); + tool_menu_control->set_deactivated_callback(std::bind(&ui_system::close_tool_menu, this)); +} + +void ui_system::handle_event(const mouse_moved_event& event) +{ + if (tool_menu_control->is_active()) + { + tool_selection_vector.x += event.dx; + tool_selection_vector.y += event.dy; + + float max_length = 200.0f; + float selection_threshold = 20.0f; + int sector_count = 6; + float sector_angle = vmq::two_pi / static_cast(sector_count); + + float length_squared = vmq::length_squared(tool_selection_vector); + + // Select tool if length of selection vector within threshold + if (length_squared >= selection_threshold * selection_threshold) + { + // Limit length of tool selection vector + if (length_squared > max_length * max_length) + { + tool_selection_vector = (tool_selection_vector / std::sqrt(length_squared)) * max_length; + } + + float2 selection_direction = tool_selection_vector / std::sqrt(length_squared); + float selection_angle = std::atan2(-selection_direction.y, selection_direction.x) - vmq::radians(90.0f); + selection_angle = (selection_angle >= 0.0f ? selection_angle : (vmq::two_pi + selection_angle)); + + int sector = static_cast((selection_angle + sector_angle * 0.5f) / sector_angle) % sector_count; + + float rotation_angle = static_cast(sector) * sector_angle; + tool_selector_bg.set_rotation(vmq::angle_axis(rotation_angle, {0, 0, 1})); + tool_selector_bg.update_tweens(); + + tool_selector_ant.set_rotation(vmq::angle_axis(rotation_angle + vmq::radians(180.0f), {0, 0, 1}) * vmq::angle_axis(vmq::radians(90.0f), {1, 0, 0})); + } + } + + mouse_position[0] = event.x; + mouse_position[1] = event.y; +} + +void ui_system::open_tool_menu() +{ + scene.add_object(&modal_bg); + scene.add_object(&tool_selector_bg); + scene.add_object(&tool_selector_ant); + tool_selection_vector = {0, 0}; +} + +void ui_system::close_tool_menu() +{ + scene.remove_object(&modal_bg); + scene.remove_object(&tool_selector_bg); + scene.remove_object(&tool_selector_ant); +} diff --git a/src/antkeeper/systems/ui-system.hpp b/src/antkeeper/systems/ui-system.hpp new file mode 100644 index 0000000..14a311b --- /dev/null +++ b/src/antkeeper/systems/ui-system.hpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 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_UI_SYSTEM_HPP +#define ANTKEEPER_UI_SYSTEM_HPP + +#include "event/event-handler.hpp" +#include "input/input-events.hpp" +#include "scene/scene.hpp" +#include "scene/camera.hpp" +#include "scene/directional-light.hpp" +#include "scene/ambient-light.hpp" +#include "scene/model-instance.hpp" +#include "scene/billboard.hpp" +#include "renderer/material.hpp" +#include + +using namespace vmq::types; + +class control; +class scene; +class resource_manager; + +class ui_system: + public event_handler +{ +public: + ui_system(::resource_manager* resource_manager); + + void update(float dt); + + void set_viewport(const float4& viewport); + void set_tool_menu_control(control* control); + + const ::scene* get_scene() const; + ::scene* get_scene(); + const ::camera* get_camera() const; + ::camera* get_camera(); + +private: + virtual void handle_event(const mouse_moved_event& event); + + void open_tool_menu(); + void close_tool_menu(); + + ::resource_manager* resource_manager; + ::scene scene; + ::camera camera; + ambient_light indirect_light; + directional_light direct_light; + model_instance tool_selector_ant; + billboard tool_selector_bg; + material modal_bg_material; + billboard modal_bg; + + model_instance energy_symbol; + + float2 mouse_position; + float4 viewport; + float2 viewport_center; + float2 tool_selection_vector; + control* tool_menu_control; +}; + +inline const scene* ui_system::get_scene() const +{ + return &scene; +} + +inline scene* ui_system::get_scene() +{ + return &scene; +} + +inline const camera* ui_system::get_camera() const +{ + return &camera; +} + +inline camera* ui_system::get_camera() +{ + return &camera; +} + +#endif // ANTKEEPER_UI_SYSTEM_HPP + diff --git a/src/resources/resource-handle.cpp b/src/antkeeper/systems/updatable-system.cpp similarity index 56% rename from src/resources/resource-handle.cpp rename to src/antkeeper/systems/updatable-system.cpp index f869e7c..6e06c89 100644 --- a/src/resources/resource-handle.cpp +++ b/src/antkeeper/systems/updatable-system.cpp @@ -1,25 +1,22 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#include "resource-handle.hpp" +#include "updatable-system.hpp" -ResourceHandleBase::ResourceHandleBase(): - referenceCount(0) -{} diff --git a/src/antkeeper/systems/updatable-system.hpp b/src/antkeeper/systems/updatable-system.hpp new file mode 100644 index 0000000..efcf045 --- /dev/null +++ b/src/antkeeper/systems/updatable-system.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 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_UPDATABLE_SYSTEM_HPP +#define ANTKEEPER_UPDATABLE_SYSTEM_HPP + +/** + * Abstract base class for updatable systems. + */ +class updatable_system +{ +public: + /** + * Perform's a system's update() function. + * + * @param t Total elapsed time, in seconds. + * @param dt Delta time, in seconds. + * @param registry Entity registry. + */ + virtual void update(double t, double dt) = 0; +}; + +#endif // ANTKEEPER_UPDATABLE_SYSTEM_HPP + diff --git a/src/antkeeper/systems/vegetation-system.cpp b/src/antkeeper/systems/vegetation-system.cpp new file mode 100644 index 0000000..7f89155 --- /dev/null +++ b/src/antkeeper/systems/vegetation-system.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2020 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 "vegetation-system.hpp" +#include "entity/components/model-component.hpp" +#include "entity/components/transform-component.hpp" +#include "scene/model-instance.hpp" +#include "scene/lod-group.hpp" +#include "scene/scene.hpp" +#include "renderer/material.hpp" +#include "geometry/aabb.hpp" +#include + +using namespace vmq::types; +using namespace vmq::operators; +using namespace ecs; + +vegetation_system::vegetation_system(entt::registry& registry): + entity_system(registry), + terrain_patch_size(1.0f), + vegetation_patch_size(1.0f), + vegetation_patch_columns(1), + vegetation_patch_rows(1), + vegetation_density(1.0f), + vegetation_model(nullptr) +{ + registry.on_construct().connect<&vegetation_system::on_terrain_construct>(this); + registry.on_destroy().connect<&vegetation_system::on_terrain_destroy>(this); +} + +vegetation_system::~vegetation_system() +{} + +void vegetation_system::update(double t, double dt) +{} + +void vegetation_system::set_terrain_patch_size(float size) +{ + terrain_patch_size = size; + vegetation_patch_size = terrain_patch_size / static_cast(vegetation_patch_columns); +} + +void vegetation_system::set_vegetation_patch_resolution(int subdivisions) +{ + // Determine number of vegetation patch columns and rows per terrain patch + vegetation_patch_columns = static_cast(std::pow(2, subdivisions)); + vegetation_patch_rows = vegetation_patch_columns; + vegetation_patch_size = terrain_patch_size / static_cast(vegetation_patch_columns); +} + +void vegetation_system::set_vegetation_density(float density) +{ + vegetation_density = density; +} + +void vegetation_system::set_vegetation_model(::model* model) +{ + vegetation_model = model; +} + +void vegetation_system::set_scene(::scene* scene) +{ + this->scene = scene; +} + +void vegetation_system::on_terrain_construct(entt::registry& registry, entt::entity entity, terrain_component& component) +{ + // Find corner of terrain patch + float terrain_patch_min_x = static_cast(component.x) * terrain_patch_size - terrain_patch_size * 0.5f; + float terrain_patch_min_z = static_cast(component.z) * terrain_patch_size - terrain_patch_size * 0.5f; + + // Create vegetation patches + for (int column = 0; column < vegetation_patch_columns; ++column) + { + for (int row = 0; row < vegetation_patch_rows; ++row) + { + // Calculate center of vegetation patch + + + /* + // Create vegetation patch entity + auto vegetation_patch_entity = registry.create(); + + // Assign a transform component + transform_component transform; + transform.transform = vmq::identity_transform; + transform.transform.translation = float3{vegetation_patch_x, 0.0f, vegetation_patch_z}; + transform.warp = true; + registry.assign_or_replace(vegetation_patch_entity, transform); + + // Assign a model component + model_component model; + model.model = vegetation_model; + model.instance_count = 500; + registry.assign_or_replace(vegetation_patch_entity, model); + */ + + // Find patch translation + float vegetation_patch_x = terrain_patch_min_x + vegetation_patch_size * static_cast(column) + vegetation_patch_size * 0.5f; + float vegetation_patch_z = terrain_patch_min_z + vegetation_patch_size * static_cast(row) + vegetation_patch_size * 0.5f; + float3 translation = {vegetation_patch_x, 0.0f, vegetation_patch_z}; + + // Generate culling mask + aabb* culling_mask = new aabb(vegetation_model->get_bounds()); + culling_mask->min_point.x = std::min(culling_mask->min_point.x, translation.x - vegetation_patch_size * 0.5f); + culling_mask->min_point.z = std::min(culling_mask->min_point.z, translation.z - vegetation_patch_size * 0.5f); + culling_mask->max_point.x = std::max(culling_mask->max_point.x, translation.x + vegetation_patch_size * 0.5f); + culling_mask->max_point.z = std::max(culling_mask->max_point.z, translation.z + vegetation_patch_size * 0.5f); + + std::size_t lod_count = 4; + std::size_t instance_count_lod0 = 500; + std::size_t instance_count_lod1 = instance_count_lod0 / 2; + std::size_t instance_count_lod2 = instance_count_lod1 / 2; + + // Generate LOD materials + const material* lod0_material = (*vegetation_model->get_groups())[0]->get_material(); + material* lod1_material = new material(*lod0_material); + static_cast*>(lod1_material->get_property("instance_multiplier"))->set_value(2); + material* lod2_material = new material(*lod0_material); + static_cast*>(lod2_material->get_property("instance_multiplier"))->set_value(4); + + // Create LOD 0 + model_instance* patch_lod0 = new model_instance(); + patch_lod0->set_model(vegetation_model); + patch_lod0->set_translation(translation); + patch_lod0->set_instanced(true, instance_count_lod0); + patch_lod0->set_culling_mask(culling_mask); + patch_lod0->update_tweens(); + + // Create LOD 1 + model_instance* patch_lod1 = new model_instance(); + patch_lod1->set_model(vegetation_model); + patch_lod1->set_material(0, lod1_material); + patch_lod1->set_translation(translation); + patch_lod1->set_instanced(true, instance_count_lod1); + patch_lod1->set_culling_mask(culling_mask); + patch_lod1->update_tweens(); + + // Create LOD 2 + model_instance* patch_lod2 = new model_instance(); + patch_lod2->set_model(vegetation_model); + patch_lod2->set_material(0, lod2_material); + patch_lod2->set_translation(translation); + patch_lod2->set_instanced(true, instance_count_lod2); + patch_lod2->set_culling_mask(culling_mask); + patch_lod2->update_tweens(); + + // Create LOD group + ::lod_group* lod_group = new ::lod_group(lod_count); + lod_group->add_object(0, patch_lod0); + lod_group->add_object(1, patch_lod1); + lod_group->add_object(2, patch_lod2); + lod_group->set_translation(translation); + lod_group->update_tweens(); + + // Add LOD group to scene + scene->add_object(lod_group); + } + } +} + +void vegetation_system::on_terrain_destroy(entt::registry& registry, entt::entity entity) +{} diff --git a/src/antkeeper/systems/vegetation-system.hpp b/src/antkeeper/systems/vegetation-system.hpp new file mode 100644 index 0000000..2d646de --- /dev/null +++ b/src/antkeeper/systems/vegetation-system.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2020 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_VEGETATION_SYSTEM_HPP +#define ANTKEEPER_VEGETATION_SYSTEM_HPP + +#include "entity-system.hpp" +#include "entity/components/terrain-component.hpp" + +class model; +class scene; + +/** + * Places vegetation patches on terrain. + */ +class vegetation_system: public entity_system +{ +public: + vegetation_system(entt::registry& registry); + ~vegetation_system(); + virtual void update(double t, double dt); + + /** + * Sets the terrain patch size. + * + * @param size Size of the terrain patch. + */ + void set_terrain_patch_size(float size); + + /** + * Sets the vegetation patch size. + * + * @param subdivisions Number of times a terrain patch should be subdivided into vegetation patches. + */ + void set_vegetation_patch_resolution(int subdivisions); + + void set_vegetation_density(float density); + + void set_vegetation_model(::model* model); + + void set_scene(::scene* scene); + +private: + void on_terrain_construct(entt::registry& registry, entt::entity entity, ecs::terrain_component& component); + void on_terrain_destroy(entt::registry& registry, entt::entity entity); + + float terrain_patch_size; + float vegetation_patch_size; + int vegetation_patch_columns; + int vegetation_patch_rows; + float vegetation_density; + model* vegetation_model; + ::scene* scene; +}; + +#endif // ANTKEEPER_VEGETATION_SYSTEM_HPP + diff --git a/src/timestamp.cpp b/src/antkeeper/timestamp.cpp similarity index 81% rename from src/timestamp.cpp rename to src/antkeeper/timestamp.cpp index 7d0eff2..2e0bfaa 100644 --- a/src/timestamp.cpp +++ b/src/antkeeper/timestamp.cpp @@ -1,20 +1,20 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ #include "timestamp.hpp" @@ -40,7 +40,6 @@ std::string timestamp() std::stringstream stream; stream << std::put_time(timeinfo, "%Y%m%d-%H%M%S-"); stream << std::setfill('0') << std::setw(3) << ms; - #endif return stream.str(); diff --git a/src/timestamp.hpp b/src/antkeeper/timestamp.hpp similarity index 62% rename from src/timestamp.hpp rename to src/antkeeper/timestamp.hpp index 0998bbb..55c49c9 100644 --- a/src/timestamp.hpp +++ b/src/antkeeper/timestamp.hpp @@ -1,24 +1,24 @@ /* - * Copyright (C) 2017-2019 Christopher J. Howard + * Copyright (C) 2020 Christopher J. Howard * - * This file is part of Antkeeper Source Code. + * This file is part of Antkeeper source code. * - * Antkeeper Source Code is free software: you can redistribute it and/or modify + * 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, + * 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 . + * along with Antkeeper source code. If not, see . */ -#ifndef TIMESTAMP_HPP -#define TIMESTAMP_HPP +#ifndef ANTKEEPER_TIMESTAMP_HPP +#define ANTKEEPER_TIMESTAMP_HPP #include @@ -27,4 +27,4 @@ */ std::string timestamp(); -#endif // TIMESTAMP_HPP +#endif // ANTKEEPER_TIMESTAMP_HPP diff --git a/src/configuration.hpp.in b/src/configuration.hpp.in deleted file mode 100644 index bc0c8d0..0000000 --- a/src/configuration.hpp.in +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 CONFIGURATION_HPP -#define CONFIGURATION_HPP - -#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@ -#define VERSION_MINOR @PROJECT_VERSION_MINOR@ -#define VERSION_PATCH @PROJECT_VERSION_PATCH@ -#define VERSION_STRING "@PROJECT_VERSION@" - -#endif // CONFIGURATION_HPP diff --git a/src/debug/ansi-escape-codes.cpp b/src/debug/ansi-escape-codes.cpp deleted file mode 100644 index fcb5c99..0000000 --- a/src/debug/ansi-escape-codes.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "ansi-escape-codes.hpp" - -const char* ANSI_CODE_RESET = "\u001b[0m"; - -const char* ANSI_CODE_BLACK = "\u001b[30m"; -const char* ANSI_CODE_RED = "\u001b[31m"; -const char* ANSI_CODE_GREEN = "\u001b[32m"; -const char* ANSI_CODE_YELLOW = "\u001b[33m"; -const char* ANSI_CODE_BLUE = "\u001b[34m"; -const char* ANSI_CODE_MAGENTA = "\u001b[35m"; -const char* ANSI_CODE_CYAN = "\u001b[36m"; -const char* ANSI_CODE_WHITE = "\u001b[37m"; - -const char* ANSI_CODE_BRIGHT_BLACK = "\u001b[30;1m"; -const char* ANSI_CODE_BRIGHT_RED = "\u001b[31;1m"; -const char* ANSI_CODE_BRIGHT_GREEN = "\u001b[32;1m"; -const char* ANSI_CODE_BRIGHT_YELLOW = "\u001b[33;1m"; -const char* ANSI_CODE_BRIGHT_BLUE = "\u001b[34;1m"; -const char* ANSI_CODE_BRIGHT_MAGENTA = "\u001b[35;1m"; -const char* ANSI_CODE_BRIGHT_CYAN = "\u001b[36;1m"; -const char* ANSI_CODE_BRIGHT_WHITE = "\u001b[37;1m"; - -const char* ANSI_CODE_BACKGROUND_BLACK = "\u001b[40m"; -const char* ANSI_CODE_BACKGROUND_RED = "\u001b[41m"; -const char* ANSI_CODE_BACKGROUND_GREEN = "\u001b[42m"; -const char* ANSI_CODE_BACKGROUND_YELLOW = "\u001b[43m"; -const char* ANSI_CODE_BACKGROUND_BLUE = "\u001b[44m"; -const char* ANSI_CODE_BACKGROUND_MAGENTA = "\u001b[45m"; -const char* ANSI_CODE_BACKGROUND_CYAN = "\u001b[46m"; -const char* ANSI_CODE_BACKGROUND_WHITE = "\u001b[47m"; - -const char* ANSI_CODE_BACKGROUND_BRIGHT_BLACK = "\u001b[40;1m"; -const char* ANSI_CODE_BACKGROUND_BRIGHT_RED = "\u001b[41;1m"; -const char* ANSI_CODE_BACKGROUND_BRIGHT_GREEN = "\u001b[42;1m"; -const char* ANSI_CODE_BACKGROUND_BRIGHT_YELLOW = "\u001b[43;1m"; -const char* ANSI_CODE_BACKGROUND_BRIGHT_BLUE = "\u001b[44;1m"; -const char* ANSI_CODE_BACKGROUND_BRIGHT_MAGENTA = "\u001b[45;1m"; -const char* ANSI_CODE_BACKGROUND_BRIGHT_CYAN = "\u001b[46;1m"; -const char* ANSI_CODE_BACKGROUND_BRIGHT_WHITE = "\u001b[47;1m"; - -const char* ANSI_CODE_BOLD = "\u001b[1m"; -const char* ANSI_CODE_UNDERLINE = "\u001b[4m"; -const char* ANSI_CODE_REVERSED = "\u001b[7m"; - - - - diff --git a/src/debug/ansi-escape-codes.hpp b/src/debug/ansi-escape-codes.hpp deleted file mode 100644 index ae92768..0000000 --- a/src/debug/ansi-escape-codes.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 ANSI_ESCAPE_CODES_HPP -#define ANSI_ESCAPE_CODES_HPP - -// Reset code -extern const char* ANSI_CODE_RESET; - -// Standard colors -extern const char* ANSI_CODE_BLACK; -extern const char* ANSI_CODE_RED; -extern const char* ANSI_CODE_GREEN; -extern const char* ANSI_CODE_YELLOW; -extern const char* ANSI_CODE_BLUE; -extern const char* ANSI_CODE_MAGENTA; -extern const char* ANSI_CODE_CYAN; -extern const char* ANSI_CODE_WHITE; - -// Bright colors -extern const char* ANSI_CODE_BRIGHT_BLACK; -extern const char* ANSI_CODE_BRIGHT_RED; -extern const char* ANSI_CODE_BRIGHT_GREEN; -extern const char* ANSI_CODE_BRIGHT_YELLOW; -extern const char* ANSI_CODE_BRIGHT_BLUE; -extern const char* ANSI_CODE_BRIGHT_MAGENTA; -extern const char* ANSI_CODE_BRIGHT_CYAN; -extern const char* ANSI_CODE_BRIGHT_WHITE; - -// Standard background colors -extern const char* ANSI_CODE_BACKGROUND_BLACK; -extern const char* ANSI_CODE_BACKGROUND_RED; -extern const char* ANSI_CODE_BACKGROUND_GREEN; -extern const char* ANSI_CODE_BACKGROUND_YELLOW; -extern const char* ANSI_CODE_BACKGROUND_BLUE; -extern const char* ANSI_CODE_BACKGROUND_MAGENTA; -extern const char* ANSI_CODE_BACKGROUND_CYAN; -extern const char* ANSI_CODE_BACKGROUND_WHITE; - -// Bright background colors -extern const char* ANSI_CODE_BACKGROUND_BRIGHT_BLACK; -extern const char* ANSI_CODE_BACKGROUND_BRIGHT_RED; -extern const char* ANSI_CODE_BACKGROUND_BRIGHT_GREEN; -extern const char* ANSI_CODE_BACKGROUND_BRIGHT_YELLOW; -extern const char* ANSI_CODE_BACKGROUND_BRIGHT_BLUE; -extern const char* ANSI_CODE_BACKGROUND_BRIGHT_MAGENTA; -extern const char* ANSI_CODE_BACKGROUND_BRIGHT_CYAN; -extern const char* ANSI_CODE_BACKGROUND_BRIGHT_WHITE; - -// Decorations -extern const char* ANSI_CODE_BOLD; -extern const char* ANSI_CODE_UNDERLINE; -extern const char* ANSI_CODE_REVERSED; - -#endif // ANSI_ESCAPE_CODES_HPP - diff --git a/src/debug/command-interpreter.cpp b/src/debug/command-interpreter.cpp deleted file mode 100644 index 4060637..0000000 --- a/src/debug/command-interpreter.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "command-interpreter.hpp" -#include - -template<> -int ArgumentParser::parse(const std::string& argument) -{ - return std::stoi(argument); -} - -template<> -unsigned int ArgumentParser::parse(const std::string& argument) -{ - return static_cast(std::stoul(argument)); -} - -template<> -long ArgumentParser::parse(const std::string& argument) -{ - return std::stol(argument); -} - -template<> -unsigned long ArgumentParser::parse(const std::string& argument) -{ - return std::stoul(argument); -} - -template<> -float ArgumentParser::parse(const std::string& argument) -{ - return std::stof(argument); -} - -template<> -double ArgumentParser::parse(const std::string& argument) -{ - return std::stod(argument); -} - -template<> -std::string ArgumentParser::parse(const std::string& argument) -{ - return argument; -} - -void CommandInterpreter::set(const std::string& name, const std::string& value) -{ - variableMap[name] = value; -} - -void CommandInterpreter::unset(const std::string& name) -{ - auto it = variableMap.find(name); - if (it != variableMap.end()) - { - variableMap.erase(it); - } -} - -const std::map& CommandInterpreter::help() const -{ - return helpStrings; -} - -const std::map& CommandInterpreter::variables() const -{ - return variableMap; -} - -std::tuple, std::function> CommandInterpreter::interpret(const std::string& line) -{ - // Split line into arguments - std::vector arguments; - std::stringstream stream(line); - std::string argument; - while (std::getline(stream, argument, ' ')) - { - arguments.push_back(argument); - } - - if (arguments.empty()) - { - return {std::string(), std::vector(), nullptr}; - } - - // Perform variable substitution/expansion on '$' operators - for (std::string& argument: arguments) - { - if (!argument.empty() && argument[0] == '$') - { - std::string variableName = argument.substr(1); - std::string variableValue = variableMap[variableName]; - argument = variableValue; - } - } - - // Get command name - std::string commandName = arguments[0]; - - // Remove command name from arguments - arguments.erase(arguments.begin()); - - // Find command linker for this command - auto linker = linkers.find(commandName); - if (linker == linkers.end()) - { - return {commandName, arguments, nullptr}; - } - - // Link command function and its arguments into a callable object - std::function call = linker->second(arguments); - - return {commandName, arguments, call}; -} - diff --git a/src/debug/command-interpreter.hpp b/src/debug/command-interpreter.hpp deleted file mode 100644 index a866e0a..0000000 --- a/src/debug/command-interpreter.hpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 COMMAND_INTERPRETER_HPP -#define COMMAND_INTERPRETER_HPP - -#include -#include -#include -#include -#include -#include -#include - -/** - * Parses an argument string into a typed value. - */ -template -class ArgumentParser -{ -public: - static T parse(const std::string& argument); -}; - -template<> -int ArgumentParser::parse(const std::string& argument); -template<> -unsigned int ArgumentParser::parse(const std::string& argument); -template<> -long ArgumentParser::parse(const std::string& argument); -template<> -unsigned long ArgumentParser::parse(const std::string& argument); -template<> -float ArgumentParser::parse(const std::string& argument); -template<> -double ArgumentParser::parse(const std::string& argument); -template<> -std::string ArgumentParser::parse(const std::string& argument); - -/** - * Parses an argument vector of strings into a tuple of typed values. - */ -class ArgumentVectorParser -{ -public: - template - static std::tuple parse(const std::vector& arguments) - { - if (arguments.size() != sizeof...(Args)) - { - throw std::invalid_argument("Argument vector size doesn't match function parameter count."); - } - - return parse(arguments, std::make_index_sequence{}); - } - -private: - template - static std::tuple parse(const std::vector& arguments, std::index_sequence) - { - return {ArgumentParser::parse(arguments[index])...}; - } -}; - -class CommandLinker -{ -public: - /** - * Links a function and its arguments together into a single callable object. - */ - template - static std::function link(const std::function& function, const std::vector& arguments) - { - // Parse argument vectors and store in a tuple - auto parsedArguments = ArgumentVectorParser::parse(arguments); - - // Return callable object which will invoke the function with the parsed arguments - return std::bind - ( - [function, parsedArguments]() - { - std::apply(function, parsedArguments); - } - ); - } -}; - -/** - * - */ -class CommandInterpreter -{ -public: - /** - * Registers a command. - * - * @param name Name used to invoke the command. - * @param function Function associated with the command. - */ - template - void registerCommand(const std::string& name, const std::function& function, const std::string& helpString = std::string()); - template - void registerCommand(const std::string& name, void(*function)(Args...)); - - /** - * Sets the value of an interpreter variable. - * - * @param name Interpreter variable name. - * @param value Interpreter variable value. - */ - void set(const std::string& name, const std::string& value); - - /** - * Unsets an interpreter variable. - * - * @param name Interpreter variable name. - */ - void unset(const std::string& name); - - - /** - * Returns the help strings for all commands. - */ - const std::map& help() const; - - /** - * Returns all variables and their values. - */ - const std::map& variables() const; - - - /** - * Interprets a line of text as a function call, returning the interpreted command name, argument vector, and callable function object. - * - * @param line Line of text to be interpreted. Arguments are delimeted by spaces, with the first argument as the command name. Command names containing the '.' operator will have the post-dot string substituted for its console variable value, then the string will be transposed around the dot, and the dot will be replaced by a space, such that the command "object.setValue 10" would become "setValue x 10" if that console variable "object" was set to "x". Arguments beginning with substitution operator '$' will interpreted as variables and substituted with their values. - */ - std::tuple, std::function> interpret(const std::string& line); - -private: - template - void addCommandLinker(const std::string& name, const Function& function, const Linker& linker); - - // A command name-keyed map of command linkers - std::unordered_map(const std::vector&)>> linkers; - std::map helpStrings; - std::map variableMap; -}; - -template -void CommandInterpreter::registerCommand(const std::string& name, const std::function& function, const std::string& helpString) -{ - addCommandLinker(name, function, CommandLinker::link); - helpStrings[name] = helpString; -} - -template -void CommandInterpreter::registerCommand(const std::string& name, void(*function)(Args...)) -{ - addCommandLinker(name, function, CommandLinker::link); -} - -template -void CommandInterpreter::addCommandLinker(const std::string& name, const Function& function, const Linker& linker) -{ - linkers[name] = std::bind(linker, function, std::placeholders::_1); -} - -#endif // COMMAND_INTERPRETER_HPP - diff --git a/src/debug/logger.cpp b/src/debug/logger.cpp deleted file mode 100644 index 0498c7e..0000000 --- a/src/debug/logger.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "logger.hpp" -#include "ansi-escape-codes.hpp" -#include - -Logger::Logger(): - os(&std::cout), - logPrefix(std::string()), - logPostfix("\n"), - warningPrefix(ANSI_CODE_YELLOW + std::string("Warning: ")), - warningPostfix(ANSI_CODE_RESET), - errorPrefix(ANSI_CODE_RED + std::string("Error: ")), - errorPostfix(ANSI_CODE_RESET), - successPrefix(ANSI_CODE_GREEN + std::string("Success: ")), - successPostfix(ANSI_CODE_RESET) -{} - -Logger::~Logger() -{} - -void Logger::redirect(std::ostream* stream) -{ - os = stream; -} - -void Logger::log(const std::string& text) -{ - if (os) - { - (*os) << (logPrefix + text + logPostfix); - os->flush(); - } -} - -void Logger::warning(const std::string& text) -{ - log(warningPrefix + text + warningPostfix); -} - -void Logger::error(const std::string& text) -{ - log(errorPrefix + text + errorPostfix); -} - -void Logger::success(const std::string& text) -{ - log(successPrefix + text + successPostfix); -} - -void Logger::setLogPrefix(const std::string& prefix) -{ - logPrefix = prefix; -} - -void Logger::setLogPostfix(const std::string& postfix) -{ - logPostfix = postfix; -} - -void Logger::setWarningPrefix(const std::string& prefix) -{ - warningPrefix = prefix; -} - -void Logger::setWarningPostfix(const std::string& postfix) -{ - warningPostfix = postfix; -} - -void Logger::setErrorPrefix(const std::string& prefix) -{ - errorPrefix = prefix; -} - -void Logger::setErrorPostfix(const std::string& postfix) -{ - errorPostfix = postfix; -} - -void Logger::setSuccessPrefix(const std::string& prefix) -{ - successPrefix = prefix; -} - -void Logger::setSuccessPostfix(const std::string& postfix) -{ - successPostfix = postfix; -} - diff --git a/src/debug/logger.hpp b/src/debug/logger.hpp deleted file mode 100644 index d343e3e..0000000 --- a/src/debug/logger.hpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 LOGGER_HPP -#define LOGGER_HPP - -#include -#include - -class Logger -{ -public: - Logger(); - ~Logger(); - - /** - * Redirects log output to the specified output stream. - * - * @param stream Output stream to which log text will be written. - */ - void redirect(std::ostream* stream); - /** - * Outputs text to the log. - */ - void log(const std::string& text); - - void warning(const std::string& text); - void error(const std::string& text); - void success(const std::string& text); - - void setLogPrefix(const std::string& prefix); - void setLogPostfix(const std::string& postfix); - void setWarningPrefix(const std::string& prefix); - void setWarningPostfix(const std::string& postfix); - void setErrorPrefix(const std::string& prefix); - void setErrorPostfix(const std::string& postfix); - void setSuccessPrefix(const std::string& prefix); - void setSuccessPostfix(const std::string& postfix); - -private: - std::ostream* os; - std::string logPrefix; - std::string logPostfix; - std::string warningPrefix; - std::string warningPostfix; - std::string errorPrefix; - std::string errorPostfix; - std::string successPrefix; - std::string successPostfix; -}; - -#endif // LOGGER_HPP - diff --git a/src/entity/component-manager.cpp b/src/entity/component-manager.cpp deleted file mode 100644 index f39a7b5..0000000 --- a/src/entity/component-manager.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "component-manager.hpp" -#include "component-observer.hpp" - -void ComponentManager::addComponentObserver(ComponentObserver* observer) -{ - componentObservers.push_back(observer); -} - -void ComponentManager::removeComponentObserver(ComponentObserver* observer) -{ - componentObservers.remove(observer); -} - -void ComponentManager::addComponent(EntityID entity, ComponentBase* component) -{ - ComponentType componentType = component->getComponentType(); - - entityMap[entity][componentType] = component; - - // Notify observers - for (auto observer: componentObservers) - { - observer->componentAdded(entity, component); - } -} - -ComponentBase* ComponentManager::removeComponent(EntityID entity, ComponentType type) -{ - ComponentMap& componentMap = entityMap[entity]; - - auto it = componentMap.find(type); - if (it == componentMap.end()) - { - return nullptr; - } - - ComponentBase* component = it->second; - - // Notify observers - for (auto observer: componentObservers) - { - observer->componentRemoved(entity, component); - } - - componentMap.erase(it); - - return component; -} - -ComponentBase* ComponentManager::getComponent(EntityID entity, ComponentType type) -{ - ComponentMap& componentMap = entityMap[entity]; - - auto it = componentMap.find(type); - if (it == componentMap.end()) - { - return nullptr; - } - - return it->second; -} - diff --git a/src/entity/component-manager.hpp b/src/entity/component-manager.hpp deleted file mode 100644 index c1e6615..0000000 --- a/src/entity/component-manager.hpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 COMPONENT_MANAGER_HPP -#define COMPONENT_MANAGER_HPP - -#include "entity-id.hpp" -#include "component.hpp" -#include -#include - -class ComponentObserver; - -/// Maps component types to components. -typedef std::map ComponentMap; - -/// Maps entity IDs to a component map. -typedef std::map EntityComponentMap; - -/** - * Manages the aggregation of components which make up entities. - */ -class ComponentManager -{ -public: - /** - * Creates an instance of ComponentManager. - */ - ComponentManager() = default; - - /** - * Destroys an instance of ComponentManager. - */ - ~ComponentManager() = default; - - /** - * Adds a ComponentObserver. - * - * @param observer Pointer to the observer to add. - */ - void addComponentObserver(ComponentObserver* observer); - - /** - * Removes a ComponentObserver. - * - * @param observer Pointer to the observer to remove. - */ - void removeComponentObserver(ComponentObserver* observer); - - /** - * Adds a component to the specified entity. - * - * @param entity Specifies an entity. - * @param component Specifies the component to add. - */ - void addComponent(EntityID entity, ComponentBase* component); - - /** - * Removes a component from the specified entity. - * - * @param entity Specifies an entity. - * @param type Specifies the type of component. - * - * @return Pointer to the removed component. - */ - ComponentBase* removeComponent(EntityID entity, ComponentType type); - - /** - * Returns the specified component of an entity. - * - * @param entity Specifies an entity. - * @param type Specifies the type of component. - * - * @return Pointer to the component, or `nullptr` if the specified component was not found. - */ - ComponentBase* getComponent(EntityID entity, ComponentType type); - - /** - * Returns the specified component of an entity. - * - * @param entity Specifies an entity. - * @tparam type Specifies the component type. - * - * @return Pointer to the component, or `nullptr` if the specified component was not found. - */ - template - T* getComponent(EntityID entity); - - /** - * Returns the component map of the specified entity. - * - * @param entity Specifies an entity. - * - * @return Pointer to the component map. - */ - ComponentMap* getComponents(EntityID entity); - -private: - ComponentManager(const ComponentManager&) = delete; - ComponentManager& operator=(const ComponentManager&) = delete; - - EntityComponentMap entityMap; - std::list componentObservers; -}; - -template -T* ComponentManager::getComponent(EntityID entity) -{ - ComponentMap& componentMap = entityMap[entity]; - - auto it = componentMap.find(T::TYPE); - if (it == componentMap.end()) - { - return nullptr; - } - - return static_cast(it->second); -} - -inline ComponentMap* ComponentManager::getComponents(EntityID entity) -{ - return &entityMap[entity]; -} - -#endif // COMPONENT_MANAGER_HPP diff --git a/src/entity/component-observer.cpp b/src/entity/component-observer.cpp deleted file mode 100644 index 2beca1e..0000000 --- a/src/entity/component-observer.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "component-observer.hpp" -#include "component-manager.hpp" - -ComponentObserver::ComponentObserver(ComponentManager* componentManager): - componentManager(componentManager) -{ - componentManager->addComponentObserver(this); -} - -ComponentObserver::~ComponentObserver() -{ - componentManager->removeComponentObserver(this); -} - diff --git a/src/entity/component-observer.hpp b/src/entity/component-observer.hpp deleted file mode 100644 index 2049ac0..0000000 --- a/src/entity/component-observer.hpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 COMPONENT_OBSERVER_HPP -#define COMPONENT_OBSERVER_HPP - -#include "entity-id.hpp" - -class ComponentBase; -class ComponentManager; - -/** - * Abstract base class for component observers. - */ -class ComponentObserver -{ -public: - /** - * Creates a component observer. - * - * @param componentManager Specifies the component manager with which to associate this component observer. - */ - ComponentObserver(ComponentManager* componentManager); - - /** - * Destroys a component observer. - */ - virtual ~ComponentObserver(); - -protected: - ComponentManager* componentManager; - -private: - friend class ComponentManager; - - /** - * Called after a component is added to an entity. - * - * @param entity Specifies the entity with which the component is associated. - * @param component Specifies the component added. - */ - virtual void componentAdded(EntityID entity, ComponentBase* component) = 0; - - /** - * Called after a component is removed from an entity. - * - * @param entity Specifies the entity with which the component is associated. - * @param component Specifies the component removed. - */ - virtual void componentRemoved(EntityID entity, ComponentBase* component) = 0; -}; - -#endif // COMPONENT_OBSERVER_HPP diff --git a/src/entity/component.hpp b/src/entity/component.hpp deleted file mode 100644 index 5884420..0000000 --- a/src/entity/component.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 COMPONENT_HPP -#define COMPONENT_HPP - -enum class ComponentType; - -/** - * Abstract base class for entity components. - */ -class ComponentBase -{ -public: - /** - * Destroys a component. - */ - virtual ~ComponentBase() = default; - - /** - * Clones the component. - */ - virtual ComponentBase* clone() const = 0; - - /** - * Returns the component type. - */ - virtual ComponentType getComponentType() const = 0; -}; - -/** - * Abstract templated class for entity components. - */ -template -class Component: public ComponentBase -{ -public: - static const ComponentType TYPE; - - /** - * Destroys a component. - */ - virtual ~Component() = default; - - /** - * Returns the component type. - */ - virtual ComponentType getComponentType() const final; -}; - -template -const ComponentType Component::TYPE = type; - -template -inline ComponentType Component::getComponentType() const -{ - return type; -} - -#endif // COMPONENT_HPP - diff --git a/src/entity/components/animation-component.hpp b/src/entity/components/animation-component.hpp deleted file mode 100644 index 3032683..0000000 --- a/src/entity/components/animation-component.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 ANIMATION_COMPONENT_HPP -#define ANIMATION_COMPONENT_HPP - -#include "../component.hpp" -#include "component-type.hpp" - -#include -using namespace Emergent; - -#include -#include - -class AnimationComponent: public Component -{ -public: - virtual ComponentBase* clone() const; - - // List of animation-blend weight pairs - std::list, float>> animations; -}; - -#endif // ANIMATION_COMPONENT_HPP - diff --git a/src/entity/components/ant-hill-component.hpp b/src/entity/components/ant-hill-component.hpp deleted file mode 100644 index 0118389..0000000 --- a/src/entity/components/ant-hill-component.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 ANT_HILL_COMPONENT_HPP -#define ANT_HILL_COMPONENT_HPP - -#include "../component.hpp" -#include "component-type.hpp" - -#include -using namespace Emergent; - -class AntHillComponent: public Component -{ -public: - virtual ComponentBase* clone() const; -}; - -#endif // ANT_HILL_COMPONENT_HPP - diff --git a/src/entity/components/behavior-component.cpp b/src/entity/components/behavior-component.cpp deleted file mode 100644 index 40cf0fd..0000000 --- a/src/entity/components/behavior-component.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "behavior-component.hpp" - -ComponentBase* BehaviorComponent::clone() const -{ - BehaviorComponent* component = new BehaviorComponent(); - component->wanderDirection = wanderDirection; - component->wanderTriangle = wanderTriangle; - return component; -} - diff --git a/src/entity/components/behavior-component.hpp b/src/entity/components/behavior-component.hpp deleted file mode 100644 index 777fc18..0000000 --- a/src/entity/components/behavior-component.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 BEHAVIOR_COMPONENT_HPP -#define BEHAVIOR_COMPONENT_HPP - -#include "../component.hpp" -#include "component-type.hpp" - -#include -using namespace Emergent; - -class BehaviorComponent: public Component -{ -public: - virtual ComponentBase* clone() const; - float wanderCircleDistance; // cm - float wanderCircleRadius; // cm - float wanderRate; // radians/s - Vector3 wanderDirection; - TriangleMesh::Triangle* wanderTriangle; -}; - -#endif // BEHAVIOR_COMPONENT_HPP - diff --git a/src/entity/components/camera-component.hpp b/src/entity/components/camera-component.hpp deleted file mode 100644 index 6300ccf..0000000 --- a/src/entity/components/camera-component.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 CAMERA_COMPONENT_HPP -#define CAMERA_COMPONENT_HPP - -#include "../component.hpp" -#include "component-type.hpp" - -#include -using namespace Emergent; - -class CameraComponent: public Component -{ -public: - virtual ComponentBase* clone() const; - - Camera* camera; -}; - -#endif // CAMERA_COMPONENT_HPP - diff --git a/src/entity/components/collision-component.cpp b/src/entity/components/collision-component.cpp deleted file mode 100644 index 67d690a..0000000 --- a/src/entity/components/collision-component.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "collision-component.hpp" - -ComponentBase* CollisionComponent::clone() const -{ - CollisionComponent* component = new CollisionComponent(); - component->radius = radius; - component->collisions = collisions; - component->mesh = mesh; - return component; -} - diff --git a/src/entity/components/collision-component.hpp b/src/entity/components/collision-component.hpp deleted file mode 100644 index 4e9d142..0000000 --- a/src/entity/components/collision-component.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 COLLISION_COMPONENT_HPP -#define COLLISION_COMPONENT_HPP - -#include "../component.hpp" -#include "../entity-id.hpp" -#include "component-type.hpp" - -#include -using namespace Emergent; - -class CollisionComponent: public Component -{ -public: - virtual ComponentBase* clone() const; - - float radius; - std::list collisions; - - TriangleMesh* mesh; -}; - -#endif // COLLISION_COMPONENT_HPP - diff --git a/src/entity/components/component-type.hpp b/src/entity/components/component-type.hpp deleted file mode 100644 index 9034fda..0000000 --- a/src/entity/components/component-type.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 COMPONENT_TYPE_HPP -#define COMPONENT_TYPE_HPP - -enum class ComponentType -{ - ANIMATION, - ANT_HILL, - BEHAVIOR, - CAMERA, - COLLISION, - LEGGED_LOCOMOTION, - MODEL, - ORBIT_CONSTRAINT, - STEERING, - SOUND_SOURCE, - TERRAIN_PATCH, - TOOL, - TRANSFORM -}; - -#endif // COMPONENT_TYPE_HPP - diff --git a/src/entity/components/legged-locomotion-component.cpp b/src/entity/components/legged-locomotion-component.cpp deleted file mode 100644 index 25d3701..0000000 --- a/src/entity/components/legged-locomotion-component.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "legged-locomotion-component.hpp" - -ComponentBase* LeggedLocomotionComponent::clone() const -{ - LeggedLocomotionComponent* component = new LeggedLocomotionComponent(); - component->legCount = legCount; - component->crawlingSpeed = crawlingSpeed; - component->walkingSpeed = walkingSpeed; - component->runningSpeed = runningSpeed; - component->turningSpeed = turningSpeed; - component->speed = walkingSpeed; - component->surface = nullptr; - return component; -} - diff --git a/src/entity/components/legged-locomotion-component.hpp b/src/entity/components/legged-locomotion-component.hpp deleted file mode 100644 index a213ee0..0000000 --- a/src/entity/components/legged-locomotion-component.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 LEGGED_LOCOMOTION_COMPONENT_HPP -#define LEGGED_LOCOMOTION_COMPONENT_HPP - -#include "../component.hpp" -#include "component-type.hpp" - -#include -using namespace Emergent; - -class LeggedLocomotionComponent: public Component -{ -public: - virtual ComponentBase* clone() const; - - unsigned char legCount; - float crawlingSpeed; // cm/s - float walkingSpeed; // cm/s - float runningSpeed; // cm/s - float turningSpeed; // radians/s - float speed; - TriangleMesh::Triangle* surface; - Vector3 barycentricPosition; -}; - -#endif // LEGGED_LOCOMOTION_COMPONENT_HPP - diff --git a/src/entity/components/model-component.cpp b/src/entity/components/model-component.cpp deleted file mode 100644 index 36af59f..0000000 --- a/src/entity/components/model-component.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "model-component.hpp" - -ComponentBase* ModelComponent::clone() const -{ - ModelComponent* component = new ModelComponent(); - component->model.setModel(model.getModel()); - component->model.setPose(nullptr); - return component; -} - diff --git a/src/entity/components/model-component.hpp b/src/entity/components/model-component.hpp deleted file mode 100644 index a97c536..0000000 --- a/src/entity/components/model-component.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 MODEL_COMPONENT_HPP -#define MODEL_COMPONENT_HPP - -#include "../component.hpp" -#include "component-type.hpp" - -#include -using namespace Emergent; - -class ModelComponent: public Component -{ -public: - virtual ComponentBase* clone() const; - ModelInstance model; -}; - -#endif // MODEL_COMPONENT_HPP - diff --git a/src/entity/components/orbit-constraint-component.cpp b/src/entity/components/orbit-constraint-component.cpp deleted file mode 100644 index dee0d24..0000000 --- a/src/entity/components/orbit-constraint-component.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "orbit-constraint-component.hpp" - -ComponentBase* OrbitConstraintComponent::clone() const -{ - OrbitConstraintComponent* component = new OrbitConstraintComponent(); - component->target = target; - component->distance = distance; - component->elevation = elevation; - component->azimuth = azimuth; - - return component; -} - diff --git a/src/entity/components/orbit-constraint-component.hpp b/src/entity/components/orbit-constraint-component.hpp deleted file mode 100644 index 43ff855..0000000 --- a/src/entity/components/orbit-constraint-component.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 ORBIT_CONSTRAINT_COMPONENT_HPP -#define ORBIT_CONSTRAINT_COMPONENT_HPP - -#include "../component.hpp" -#include "component-type.hpp" -#include "entity/entity-id.hpp" - -#include -using namespace Emergent; - -class OrbitConstraintComponent: public Component -{ -public: - virtual ComponentBase* clone() const; - - EntityID target; - float distance; - float elevation; - float azimuth; -}; - -#endif // ORBIT_CONSTRAINT_COMPONENT_HPP - diff --git a/src/entity/components/sound-source-component.cpp b/src/entity/components/sound-source-component.cpp deleted file mode 100644 index a52bc60..0000000 --- a/src/entity/components/sound-source-component.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "sound-source-component.hpp" - -ComponentBase* SoundSourceComponent::clone() const -{ - SoundSourceComponent* component = new SoundSourceComponent(); - component->playing = playing; - return component; -} - diff --git a/src/entity/components/sound-source-component.hpp b/src/entity/components/sound-source-component.hpp deleted file mode 100644 index b179c44..0000000 --- a/src/entity/components/sound-source-component.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 SOUND_SOURCE_COMPONENT_HPP -#define SOUND_SOURCE_COMPONENT_HPP - -#include "../component.hpp" -#include "../entity-id.hpp" -#include "component-type.hpp" - -#include -using namespace Emergent; - -class SoundSourceComponent: public Component -{ -public: - virtual ComponentBase* clone() const; - - bool playing; -}; - -#endif // SOUND_SOURCE_COMPONENT_HPP - diff --git a/src/entity/components/steering-component.hpp b/src/entity/components/steering-component.hpp deleted file mode 100644 index c1b6e5a..0000000 --- a/src/entity/components/steering-component.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 STEERING_COMPONENT_HPP -#define STEERING_COMPONENT_HPP - -#include "../component.hpp" -#include "component-type.hpp" - -#include -using namespace Emergent; - -#define MAX_STEERING_BEHAVIORS 8 - -struct SteeringBehavior -{ - /// Function object which calculates steering force - std::function function; - - /// Priority value which determines in what order the behaviors will be evaluated - float priority; - - /// Weight factor by which the calculated steering force should be multiplied - float weight; -}; - -class SteeringComponent: public Component -{ -public: - virtual ComponentBase* clone() const; - - SteeringBehavior behaviors[MAX_STEERING_BEHAVIORS]; - std::size_t behaviorCount; - Vector3 force; - float speed; - float maxSpeed; -}; - -#endif // STEERING_COMPONENT_HPP - diff --git a/src/entity/components/terrain-patch-component.cpp b/src/entity/components/terrain-patch-component.cpp deleted file mode 100644 index 9902039..0000000 --- a/src/entity/components/terrain-patch-component.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "terrain-patch-component.hpp" - -ComponentBase* TerrainPatchComponent::clone() const -{ - TerrainPatchComponent* component = new TerrainPatchComponent(); - component->subdivisions = subdivisions; - component->position = position; - - return component; -} - diff --git a/src/entity/components/terrain-patch-component.hpp b/src/entity/components/terrain-patch-component.hpp deleted file mode 100644 index 7e387ad..0000000 --- a/src/entity/components/terrain-patch-component.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 TERRAIN_PATCH_COMPONENT_HPP -#define TERRAIN_PATCH_COMPONENT_HPP - -#include "../component.hpp" -#include "component-type.hpp" -#include - -class TerrainPatchComponent: public Component -{ -public: - virtual ComponentBase* clone() const; - - // Number of terrain mesh subdivisions (at LOD 0) - int subdivisions; - - // Position in integer terrain coordinates - std::tuple position; -}; - -#endif // TERRAIN_PATCH_COMPONENT_HPP - diff --git a/src/entity/components/tool-component.cpp b/src/entity/components/tool-component.cpp deleted file mode 100644 index 9c8a88f..0000000 --- a/src/entity/components/tool-component.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "tool-component.hpp" - -ComponentBase* ToolComponent::clone() const -{ - ToolComponent* component = new ToolComponent(); - component->active = active; - component->hoverDistance = hoverDistance; - - return component; -} - diff --git a/src/entity/components/tool-component.hpp b/src/entity/components/tool-component.hpp deleted file mode 100644 index 19e8243..0000000 --- a/src/entity/components/tool-component.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 TOOL_COMPONENT_HPP -#define TOOL_COMPONENT_HPP - -#include "../component.hpp" -#include "component-type.hpp" - -class ToolComponent: public Component -{ -public: - virtual ComponentBase* clone() const; - - bool active; - float hoverDistance; -}; - -#endif // TOOL_COMPONENT_HPP - diff --git a/src/entity/components/transform-component.hpp b/src/entity/components/transform-component.hpp deleted file mode 100644 index a2c1b2f..0000000 --- a/src/entity/components/transform-component.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 TRANSFORM_COMPONENT_HPP -#define TRANSFORM_COMPONENT_HPP - -#include "../component.hpp" -#include "component-type.hpp" - -#include -using namespace Emergent; - -class TransformComponent: public Component -{ -public: - virtual ComponentBase* clone() const; - - Transform transform; -}; - -#endif // TRANSFORM_COMPONENT_HPP - diff --git a/src/entity/entity-group-member.hpp b/src/entity/entity-group-member.hpp deleted file mode 100644 index 5c04b1b..0000000 --- a/src/entity/entity-group-member.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 ENTITY_GROUP_MEMBER_HPP -#define ENTITY_GROUP_MEMBER_HPP - -#include "entity-id.hpp" -#include -#include - -/** - * A group of entities which share a set of specified component types. - * - * @tparam T Set of components which are required for group membership. - */ -template -struct EntityGroupMember -{ - /// Entity ID of the group member. - EntityID entity; - - /// A tuple containing pointers to the member's group-related components, in the order specified by the order of the template parameters. - std::tuple components; -}; - -#endif // ENTITY_GROUP_MEMBER_HPP - diff --git a/src/entity/entity-group-observer.hpp b/src/entity/entity-group-observer.hpp deleted file mode 100644 index 940cd61..0000000 --- a/src/entity/entity-group-observer.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 ENTITY_GROUP_OBSERVER_HPP -#define ENTITY_GROUP_OBSERVER_HPP - -#include "entity-group-member.hpp" - -/** - * Abstract base class for entity group observers, which are notified each time a member is registered or unregistered from the group. - * - * @tparam T Set of components which are required for group membership. - */ -template -class EntityGroupObserver -{ -public: - typedef EntityGroupMember Member; - - /** - * Called each time an entity joins the entity group by obtaining the necessary component types. - * - * @param entity Entity ID of the new member. - */ - virtual void memberRegistered(const Member* member) = 0; - - /** - * Called each time an entity leaves an the entity group by no longer possessing the necessary component types. - * - * @param entity Entity ID of the former member. - */ - virtual void memberUnregistered(const Member* member) = 0; -}; - -#endif // ENTITY_GROUP_OBSERVER_HPP - diff --git a/src/entity/entity-group.cpp b/src/entity/entity-group.cpp deleted file mode 100644 index 22bfd26..0000000 --- a/src/entity/entity-group.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "component.hpp" -#include "component-manager.hpp" -#include "entity-group.hpp" - -EntityGroupBase::EntityGroupBase(ComponentManager* componentManager, const ComponentFilter& componentFilter): - ComponentObserver(componentManager), - componentFilter(componentFilter) -{} - -void EntityGroupBase::componentAdded(EntityID entity, ComponentBase* component) -{ - if (componentFilter.find(component->getComponentType()) != componentFilter.end()) - { - for (auto it = componentFilter.begin(); it != componentFilter.end(); ++it) - { - if (*it == component->getComponentType()) - { - continue; - } - else if (!componentManager->getComponent(entity, *it)) - { - return; - } - } - - registerMember(entity); - } -} - -void EntityGroupBase::componentRemoved(EntityID entity, ComponentBase* component) -{ - if (componentFilter.find(component->getComponentType()) != componentFilter.end()) - { - if (isRegistered(entity)) - { - unregisterMember(entity); - } - } -} - diff --git a/src/entity/entity-group.hpp b/src/entity/entity-group.hpp deleted file mode 100644 index 5ebe0b0..0000000 --- a/src/entity/entity-group.hpp +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 ENTITY_GROUP_HPP -#define ENTITY_GROUP_HPP - -#include "component-observer.hpp" -#include "component-manager.hpp" -#include "entity-group-member.hpp" -#include "entity-group-observer.hpp" -#include -#include -#include -#include -#include - -enum class ComponentType; - -/// A set of component types used to filter entities -typedef std::set ComponentFilter; - -/** - * Abstract base class for entity groups. - */ -class EntityGroupBase: protected ComponentObserver -{ -public: - /** - * Creates a entity group base. - * - * @param componentManager The component manager with which to associate this entity group. - * @param componentFilter Set of component types which an entity must possess in order to join this entity group. - */ - EntityGroupBase(ComponentManager* componentManager, const ComponentFilter& componentFilter); - - /// Returns the set of components which an entity must possess in order to join this entity group. - const ComponentFilter& getComponentFilter() const; - - /** - * Returns true if the specified entity is registered with this entity group. - * - * @param entity ID of the entity to check. - */ - virtual bool isRegistered(EntityID entity) const = 0; - -private: - virtual void componentAdded(EntityID entity, ComponentBase* component); - virtual void componentRemoved(EntityID entity, ComponentBase* component); - - /** - * Called each time an entity joins the entity group by obtaining the necessary component types. - * - * @param entity Entity ID of the new member. - */ - virtual void registerMember(EntityID entity) = 0; - - /** - * Called each time an entity leaves an the entity group by no longer possessing the necessary component types. - * - * @param entity Entity ID of the former member. - */ - virtual void unregisterMember(EntityID entity) = 0; - - ComponentFilter componentFilter; -}; - -inline const ComponentFilter& EntityGroupBase::getComponentFilter() const -{ - return componentFilter; -} - -/** - * A group of entities which share a set of specified component types. - * - * @tparam T Set of components which are required for group membership. - */ -template -class EntityGroup: public EntityGroupBase -{ -public: - typedef EntityGroupMember Member; - typedef EntityGroupObserver Observer; - - /** - * Creates a entity group. - * - * @param componentManager Component manager with which to associate this entity group. - */ - EntityGroup(ComponentManager* componentManager); - - /// Destroys a entity group. - ~EntityGroup(); - - /** - * Adds a group observer. - * - * @param observer Observer to add. - */ - void addGroupObserver(Observer* observer); - - /** - * Removes a group observer. - * - * @param observer Observer to remove. - */ - void removeGroupObserver(Observer* observer); - - /// Removes all group observers. - void removeGroupObservers(); - - /// @copydoc EntityGroupBase::isRegistered(EntityID) const - virtual bool isRegistered(EntityID entity) const; - - /** - * Returns the member list. - * - * @return List of members. - */ - const std::list* getMembers() const; - - /** - * Returns the member with the specified ID. - * - * @param entity Entity ID of a group member. - * @return Member with the specified ID, or nullptr if an entity with that ID is not registered. - */ - const Member* getMemberByEntity(EntityID entity) const; - -private: - template - typename std::enable_if<(sizeof...(V) == 0), void>::type attachComponents(Member* member) - { - std::get(member->components) = static_cast(componentManager->getComponent(member->entity, U::TYPE)); - } - - template - typename std::enable_if<(sizeof...(V) > 0), void>::type attachComponents(Member* member) - { - std::get(member->components) = static_cast(componentManager->getComponent(member->entity, U::TYPE)); - attachComponents(member); - } - - virtual void registerMember(EntityID entity); - virtual void unregisterMember(EntityID entity); - - std::list members; - std::map memberMap; - std::list observers; -}; - -template -EntityGroup::EntityGroup(ComponentManager* componentManager): - EntityGroupBase(componentManager, ComponentFilter({(T::TYPE)...})) -{} - -template -EntityGroup::~EntityGroup() -{ - while (!members.empty()) - { - Member* member = members.back(); - - members.pop_back(); - memberMap.erase(memberMap.find(member->entity)); - - for (Observer* observer: observers) - { - observer->memberUnregistered(member); - } - - delete member; - } -} - - -template -void EntityGroup::addGroupObserver(Observer* observer) -{ - observers.push_back(observer); -} - -template -void EntityGroup::removeGroupObserver(Observer* observer) -{ - observers.remove(observer); -} - -template -void EntityGroup::removeGroupObservers() -{ - observers.clear(); -} - -template -inline bool EntityGroup::isRegistered(EntityID entity) const -{ - return (memberMap.find(entity) != memberMap.end()); -} - -template -inline const std::list::Member*>* EntityGroup::getMembers() const -{ - return &members; -} - -template -inline const typename EntityGroup::Member* EntityGroup::getMemberByEntity(EntityID entity) const -{ - auto it = memberMap.find(entity); - if (it != memberMap.end()) - { - return it->second; - } - - return nullptr; -} - -template -void EntityGroup::registerMember(EntityID entity) -{ - Member* member = new Member(); - member->entity = entity; - attachComponents<0, T...>(member); - - members.push_back(member); - memberMap[entity] = member; - - for (Observer* observer: observers) - { - observer->memberRegistered(member); - } -} - -template -void EntityGroup::unregisterMember(EntityID entity) -{ - auto it = memberMap.find(entity); - Member* member = it->second; - - memberMap.erase(it); - members.remove(member); - - for (Observer* observer: observers) - { - observer->memberUnregistered(member); - } - - delete member; -} - -#endif // ENTITY_GROUP_HPP - diff --git a/src/entity/entity-id-pool.cpp b/src/entity/entity-id-pool.cpp deleted file mode 100644 index 7850dff..0000000 --- a/src/entity/entity-id-pool.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "entity-id-pool.hpp" - -EntityIDPool::EntityIDPool(): - nextID(0) -{} - -EntityIDPool::~EntityIDPool() -{} - -EntityID EntityIDPool::reserveNextID() -{ - EntityID id; - - if (!availableIDs.empty()) - { - id = *availableIDs.begin(); - availableIDs.erase(availableIDs.begin()); - reservedIDs.insert(id); - } - else - { - id = nextID; - reservedIDs.insert(id); - findNextID(); - } - - return id; -} - -void EntityIDPool::reserveID(EntityID id) -{ - availableIDs.erase(id); - reservedIDs.insert(id); - - if (nextID == id) - { - findNextID(); - } -} - -inline void EntityIDPool::findNextID() -{ - do - { - ++nextID; - } - while (reservedIDs.find(nextID) != reservedIDs.end()); -} - diff --git a/src/entity/entity-id-pool.hpp b/src/entity/entity-id-pool.hpp deleted file mode 100644 index b34fe38..0000000 --- a/src/entity/entity-id-pool.hpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 ENTITY_ID_POOL_HPP -#define ENTITY_ID_POOL_HPP - -#include "entity-id.hpp" -#include - -/** - * Manages the creation and destruction of entities. - */ -class EntityIDPool -{ -public: - /** - * Creates an instance of EntityIDPool. - */ - EntityIDPool(); - - /** - * Destroys an instance of EntityIDPool. - */ - ~EntityIDPool(); - - /** - * Reserves the next available ID. - * - * @return Reserved ID. - */ - EntityID reserveNextID(); - - /** - * Reserves the specified ID. - * - * @param id Specifies an ID to reserve. - */ - void reserveID(EntityID id); - - /** - * Frees the specified ID. - * - * @param id Specifies an ID to free. - */ - void freeID(EntityID id); - - /** - * Returns `true` if the specified ID is reserved. - */ - bool isReserved(EntityID id) const; - -private: - void findNextID(); - - EntityID nextID; - std::set reservedIDs; - std::set availableIDs; -}; - -inline void EntityIDPool::freeID(EntityID id) -{ - reservedIDs.erase(id); - availableIDs.insert(id); -} - -inline bool EntityIDPool::isReserved(EntityID id) const -{ - return (reservedIDs.find(id) != reservedIDs.end()); -} - -#endif // ENTITY_ID_POOL_HPP diff --git a/src/entity/entity-manager.cpp b/src/entity/entity-manager.cpp deleted file mode 100644 index 97ae493..0000000 --- a/src/entity/entity-manager.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "entity-manager.hpp" -#include "component-manager.hpp" - -EntityManager::EntityManager(ComponentManager* componentManager): - componentManager(componentManager) -{} - -EntityManager::~EntityManager() -{} - - -EntityID EntityManager::createEntity() -{ - return idPool.reserveNextID(); -} - -bool EntityManager::createEntity(EntityID id) -{ - if (idPool.isReserved(id)) - { - return false; - } - - idPool.reserveID(id); - - return true; -} - -bool EntityManager::destroyEntity(EntityID id) -{ - if (!idPool.isReserved(id)) - { - return false; - } - - // Delete components - ComponentMap* components = componentManager->getComponents(id); - for (auto it = components->begin(); it != components->end(); it = components->begin()) - { - ComponentBase* component = it->second; - componentManager->removeComponent(id, component->getComponentType()); - delete component; - } - - // Free ID - idPool.freeID(id); - - return true; -} - diff --git a/src/entity/entity-manager.hpp b/src/entity/entity-manager.hpp deleted file mode 100644 index a629f7f..0000000 --- a/src/entity/entity-manager.hpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 ENTITY_MANAGER_HPP -#define ENTITY_MANAGER_HPP - -#include "entity-id.hpp" -#include "entity-id-pool.hpp" - -class ComponentManager; - -/** - * Manages the creation and destruction of entities. - */ -class EntityManager -{ -public: - /** - * Creates an entity manager. - * - * @param componentManager Component manager with which to associate this entity manag.er - */ - EntityManager(ComponentManager* componentManager); - - /** - * Destroys an entity manager. - */ - ~EntityManager(); - - /** - * Creates an entity with the next available ID. - * - * @return ID of the created entity. - */ - EntityID createEntity(); - - /** - * Creates an entity with the specified ID. - * - * @param id ID of the entity to be created. - * @return `true` if the entity was created, and `false` if an entity with the specified ID already exists. - */ - bool createEntity(EntityID id); - - /** - * Destroys an entity with the specified ID. - * - * @param id ID of the entity to be destroyed. - * - * @return `true` if the entity was destroyed, and `false` if an invalid ID was supplied. - */ - bool destroyEntity(EntityID id); - - /** - * Returns the component manager associated with this entity manager. - */ - const ComponentManager* getComponentManager() const; - - /// @copydoc EntityManager::getComponentManager() const - ComponentManager* getComponentManager(); - -private: - EntityManager(const EntityManager&) = delete; - EntityManager& operator=(const EntityManager&) = delete; - - EntityIDPool idPool; - ComponentManager* componentManager; -}; - -inline const ComponentManager* EntityManager::getComponentManager() const -{ - return componentManager; -} - -inline ComponentManager* EntityManager::getComponentManager() -{ - return componentManager; -} - -#endif // ENTITY_MANAGER_HPP - diff --git a/src/entity/entity-template.cpp b/src/entity/entity-template.cpp deleted file mode 100644 index 668fcc1..0000000 --- a/src/entity/entity-template.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "component.hpp" -#include "component-manager.hpp" -#include "entity-template.hpp" - -EntityTemplate::EntityTemplate(const std::list& components) -{ - for (ComponentBase* component: components) - { - this->components.push_back(component->clone()); - } -} - -EntityTemplate::~EntityTemplate() -{ - for (ComponentBase* component: components) - { - delete component; - } -} - -void EntityTemplate::apply(EntityID entity, ComponentManager* componentManager) -{ - for (ComponentBase* component: components) - { - ComponentBase* oldComponent = componentManager->getComponent(entity, component->getComponentType()); - if (oldComponent != nullptr) - { - componentManager->removeComponent(entity, component->getComponentType()); - delete oldComponent; - } - - componentManager->addComponent(entity, component->clone()); - } -} - diff --git a/src/entity/entity-template.hpp b/src/entity/entity-template.hpp deleted file mode 100644 index bf00e95..0000000 --- a/src/entity/entity-template.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 ENTITY_TEMPLATE_HPP -#define ENTITY_TEMPLATE_HPP - -#include -#include "entity-id.hpp" - -class ComponentBase; -class ComponentManager; - -/** - * A template which can be applied to entities. - */ -class EntityTemplate -{ -public: - /** - * Creates an entity template. - * - * @param components List of components which make up the template. The components in the list will be cloned and the cloned data managed by this template. - */ - EntityTemplate(const std::list& components); - - /** - * Destroys an entity template. - */ - ~EntityTemplate(); - - /** - * Applies the template to an entity. - * - * @param entity ID of an entity to which the template should be applied. - * @param componentManager Component manager with which the entity is associated. - */ - void apply(EntityID entity, ComponentManager* componentManager); - -private: - std::list components; -}; - -#endif // ENTITY_TEMPLATE_HPP - diff --git a/src/entity/system-manager.cpp b/src/entity/system-manager.cpp deleted file mode 100644 index ddb69e3..0000000 --- a/src/entity/system-manager.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "system-manager.hpp" -#include "system.hpp" - -SystemManager::SystemManager() -{} - -SystemManager::~SystemManager() -{} - -void SystemManager::update(float t, float dt) -{ - for (System* system: systems) - { - system->update(t, dt); - } -} - -void SystemManager::addSystem(System* system) -{ - systems.push_back(system); -} - -void SystemManager::removeSystem(System* system) -{ - systems.remove(system); -} diff --git a/src/entity/system-manager.hpp b/src/entity/system-manager.hpp deleted file mode 100644 index b70704e..0000000 --- a/src/entity/system-manager.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 SYSTEM_MANAGER_HPP -#define SYSTEM_MANAGER_HPP - -#include - -class System; - -/** - * Manages a series of systems. - */ -class SystemManager -{ -public: - /** - * Creates a system manager. - */ - SystemManager(); - - /** - * Destroys a system manager. - */ - ~SystemManager(); - - /** - * Updates all systems. Systems will be updated in the order that they were added to the system manager. - * - * @param t Total elapsed time, in seconds. - * @param dt Time elapsed since last update, in seconds. - */ - void update(float t, float dt); - - /** - * Adds a system to the system manager. - * - * @param system System to add. - */ - void addSystem(System* system); - - /** - * Removes a system from the system manager. - */ - void removeSystem(System* system); - -private: - std::list systems; -}; - -#endif // SYSTEM_MANAGER_HPP - diff --git a/src/entity/system.hpp b/src/entity/system.hpp deleted file mode 100644 index 0f4c67b..0000000 --- a/src/entity/system.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 SYSTEM_HPP -#define SYSTEM_HPP - -class ComponentManager; - -/** - * Abstract base class for entity systems. - */ -class System -{ -public: - /** - * Creates a system. - * - * @param componentManager Component manager with which to associate the system. - */ - System(ComponentManager* componentManager); - - /** - * Destroys a system. - */ - virtual ~System() = default; - - /** - * Updates the system. - * - * @param t Total elapsed time, in seconds. - * @param dt Time elapsed since last update, in seconds. - */ - virtual void update(float t, float dt) = 0; - -protected: - ComponentManager* componentManager; -}; - -#endif // SYSTEM_HPP - diff --git a/src/entity/systems/animation-system.cpp b/src/entity/systems/animation-system.cpp deleted file mode 100644 index 3cc7443..0000000 --- a/src/entity/systems/animation-system.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "animation-system.hpp" - -AnimationSystem::AnimationSystem(ComponentManager* componentManager): - System(componentManager), - animationGroup(componentManager) -{} - -AnimationSystem::~AnimationSystem() -{} - -void AnimationSystem::update(float t, float dt) -{ - auto members = animationGroup.getMembers(); - - for (const AnimationEntityGroup::Member* member: *members) - { - AnimationComponent* animationComponent = std::get<0>(member->components); - ModelComponent* modelComponent = std::get<1>(member->components); - - Pose* pose = modelComponent->model.getPose(); - - for (const std::pair, float>& clipWeightPair: animationComponent->animations) - { - const AnimationClip& clip = clipWeightPair.first; - float weight = clipWeightPair.second; - } - } -} - diff --git a/src/entity/systems/animation-system.hpp b/src/entity/systems/animation-system.hpp deleted file mode 100644 index b7ffc28..0000000 --- a/src/entity/systems/animation-system.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 ANIMATION_SYSTEM_HPP -#define ANIMATION_SYSTEM_HPP - -#include "../entity-group.hpp" -#include "../components/animation-component.hpp" -#include "../components/model-component.hpp" -#include "../system.hpp" - -#include -using namespace Emergent; - -typedef EntityGroup AnimationEntityGroup; - -class AnimationSystem: public System -{ -public: - AnimationSystem(ComponentManager* componentManager); - virtual ~AnimationSystem(); - - virtual void update(float t, float dt); - -private: - AnimationEntityGroup animationGroup; -}; - -#endif // ANIMATION_SYSTEM_HPP - diff --git a/src/entity/systems/behavior-system.cpp b/src/entity/systems/behavior-system.cpp deleted file mode 100644 index d63fd31..0000000 --- a/src/entity/systems/behavior-system.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "behavior-system.hpp" -#include - -BehaviorSystem::BehaviorSystem(ComponentManager* componentManager): - System(componentManager), - behaviorGroup(componentManager), - antHillGroup(componentManager) -{ - behaviorGroup.addGroupObserver(this); -} - -BehaviorSystem::~BehaviorSystem() -{} - -void BehaviorSystem::update(float t, float dt) -{ - auto members = behaviorGroup.getMembers(); - - for (const BehaviorGroup::Member* member: *members) - { - BehaviorComponent* behavior = std::get<0>(member->components); - LeggedLocomotionComponent* leggedLocomotion = std::get<1>(member->components); - SteeringComponent* steering = std::get<2>(member->components); - TransformComponent* transform = std::get<3>(member->components); - - steering->maxSpeed = leggedLocomotion->speed; - - steering->behaviorCount = 2; - steering->behaviors[0].priority = 2.0f; - steering->behaviors[0].weight = 1.0f; - steering->behaviors[0].function = std::bind(&BehaviorSystem::containment, this, member); - - steering->behaviors[1].priority = 1.0f; - steering->behaviors[1].weight = 0.5f; - steering->behaviors[1].function = std::bind(&BehaviorSystem::wander, this, dt, member); - } -} - -void BehaviorSystem::memberRegistered(const BehaviorGroup::Member* member) -{ - BehaviorComponent* behavior = std::get<0>(member->components); - LeggedLocomotionComponent* leggedLocomotion = std::get<1>(member->components); - - behavior->wanderDirection = Vector3(0.0f); - while (glm::length2(behavior->wanderDirection) == 0.0f) - { - behavior->wanderDirection = Vector3(frand(-1, 1), frand(-1, 1), frand(-1, 1)); - } - - behavior->wanderTriangle = leggedLocomotion->surface; - behavior->wanderDirection = glm::normalize(behavior->wanderDirection); - behavior->wanderCircleDistance = 3.0f; - behavior->wanderCircleRadius = 2.0f; - behavior->wanderRate = glm::radians(180.0f) * 5.0f; - - leggedLocomotion->speed = 2.0f; -} - -void BehaviorSystem::memberUnregistered(const BehaviorGroup::Member* member) -{} - - -Vector3 BehaviorSystem::containment(const BehaviorGroup::Member* agent) -{ - LeggedLocomotionComponent* leggedLocomotion = std::get<1>(agent->components); - TransformComponent* transform = std::get<3>(agent->components); - - float probeAngle = glm::radians(30.0f); - float probeDistance = 5.0f; - - Vector3 direction = transform->transform.rotation * Vector3(0, 0, 1); - - TriangleMesh::Triangle* surface = leggedLocomotion->surface; - - Vector3 forward = transform->transform.rotation * Vector3(0, 0, 1); - Vector3 up = surface->normal; - Vector3 right = glm::normalize(glm::cross(forward, up)); - - Vector3 force(0.0f); - - return force; -} - -Vector3 BehaviorSystem::wander(float dt, const BehaviorGroup::Member* agent) -{ - BehaviorComponent* behavior = std::get<0>(agent->components); - LeggedLocomotionComponent* leggedLocomotion = std::get<1>(agent->components); - SteeringComponent* steering = std::get<2>(agent->components); - TransformComponent* transform = std::get<3>(agent->components); - - // Reorientate wander direction - if (behavior->wanderTriangle != leggedLocomotion->surface) - { - if (behavior->wanderTriangle) - { - behavior->wanderDirection = glm::normalize(glm::rotation(behavior->wanderTriangle->normal, leggedLocomotion->surface->normal) * behavior->wanderDirection); - } - behavior->wanderTriangle = leggedLocomotion->surface; - } - - // Make wander direction coplanar with surface triangle - TriangleMesh::Triangle* triangle = leggedLocomotion->surface; - Vector3 triangleCenter = (triangle->edge->vertex->position + triangle->edge->next->vertex->position + triangle->edge->previous->vertex->position) * (1.0f / 3.0f); - behavior->wanderDirection = glm::normalize(projectOnPlane(transform->transform.translation + behavior->wanderDirection, triangleCenter, triangle->normal) - transform->transform.translation); - - Vector3 forward = transform->transform.rotation * Vector3(0, 0, 1); - Vector3 up = triangle->normal; - - // Calculate center of wander circle - Vector3 wanderCircleCenter = forward * behavior->wanderCircleDistance; - - // Calculate wander force - Vector3 wanderForce = wanderCircleCenter + behavior->wanderDirection * behavior->wanderCircleRadius; - - // Displace wander direction - float displacementAngle = frand(-behavior->wanderRate, behavior->wanderRate) * 0.5f * dt; - behavior->wanderDirection = glm::normalize(glm::angleAxis(displacementAngle, up) * behavior->wanderDirection); - - return wanderForce; -} - -Vector3 BehaviorSystem::forage(const BehaviorGroup::Member* agent) -{ - return Vector3(0.0f); -} - -Vector3 BehaviorSystem::homing(const BehaviorGroup::Member* agent) -{ - // Get ant position - const Vector3& antPosition = std::get<3>(agent->components)->transform.translation; - - // Find nearest ant-hill - bool found = false; - float minDistanceSquared = 0.0f; - Vector3 homingDirection(0.0f); - - auto antHills = antHillGroup.getMembers(); - for (const AntHillGroup::Member* antHill: *antHills) - { - // Get ant-hill position - const Vector3& antHillPosition = std::get<1>(antHill->components)->transform.translation; - - // Determine distance to ant-hill - Vector3 difference = antHillPosition - antPosition; - float distanceSquared = glm::length2(difference); - - if (!found || distanceSquared < minDistanceSquared) - { - minDistanceSquared = distanceSquared; - homingDirection = difference; - found = true; - } - } - - if (found) - { - homingDirection = glm::normalize(homingDirection); - } - - return homingDirection; -} - diff --git a/src/entity/systems/behavior-system.hpp b/src/entity/systems/behavior-system.hpp deleted file mode 100644 index 2612af1..0000000 --- a/src/entity/systems/behavior-system.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 BEHAVIOR_SYSTEM_HPP -#define BEHAVIOR_SYSTEM_HPP - -#include "../entity-group.hpp" -#include "../components/ant-hill-component.hpp" -#include "../components/behavior-component.hpp" -#include "../components/legged-locomotion-component.hpp" -#include "../components/steering-component.hpp" -#include "../components/transform-component.hpp" -#include "../system.hpp" - -#include -using namespace Emergent; - -typedef EntityGroup BehaviorGroup; -typedef EntityGroup AntHillGroup; - -class BehaviorSystem: public - System, - BehaviorGroup::Observer -{ -public: - BehaviorSystem(ComponentManager* componentManager); - virtual ~BehaviorSystem(); - - virtual void update(float t, float dt); - -private: - BehaviorGroup behaviorGroup; - AntHillGroup antHillGroup; - - virtual void memberRegistered(const BehaviorGroup::Member* member); - virtual void memberUnregistered(const BehaviorGroup::Member* member); - - Vector3 containment(const BehaviorGroup::Member* agent); - Vector3 wander(float dt, const BehaviorGroup::Member* agent); - Vector3 forage(const BehaviorGroup::Member* agent); - Vector3 homing(const BehaviorGroup::Member* agent); -}; - -#endif // BEHAVIOR_SYSTEM_HPP - diff --git a/src/entity/systems/collision-system.cpp b/src/entity/systems/collision-system.cpp deleted file mode 100644 index d921937..0000000 --- a/src/entity/systems/collision-system.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "collision-system.hpp" - -CollisionSystem::CollisionSystem(ComponentManager* componentManager): - System(componentManager), - entityGroup(componentManager) -{} - -CollisionSystem::~CollisionSystem() -{} - -void CollisionSystem::update(float t, float dt) -{ - auto members = entityGroup.getMembers(); - - // INEFFICIENT! Currently done twice (A vs B, then B vs A) - // Also no octrees or other structure - for (const CollisionEntityGroup::Member* memberA: *members) - { - CollisionComponent* collisionA = std::get<0>(memberA->components); - TransformComponent* transformA = std::get<1>(memberA->components); - - // Clear previous collisions - collisionA->collisions.clear(); - - for (const CollisionEntityGroup::Member* memberB: *members) - { - if (memberA == memberB) - { - continue; - } - - CollisionComponent* collisionB = std::get<0>(memberB->components); - TransformComponent* transformB = std::get<1>(memberB->components); - - Vector3 difference = transformA->transform.translation - transformB->transform.translation; - float distanceSquared = glm::length2(difference); - float collisionRadius = collisionA->radius + collisionB->radius; - - if (distanceSquared <= collisionRadius * collisionRadius) - { - collisionA->collisions.push_back(memberB->entity); - } - } - } -} - diff --git a/src/entity/systems/collision-system.hpp b/src/entity/systems/collision-system.hpp deleted file mode 100644 index 6eb6491..0000000 --- a/src/entity/systems/collision-system.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 COLLISION_SYSTEM_HPP -#define COLLISION_SYSTEM_HPP - -#include "../entity-group.hpp" -#include "../components/collision-component.hpp" -#include "../components/transform-component.hpp" -#include "../system.hpp" - -#include -using namespace Emergent; - -typedef EntityGroup CollisionEntityGroup; - -class CollisionSystem: public System -{ -public: - CollisionSystem(ComponentManager* componentManager); - virtual ~CollisionSystem(); - - virtual void update(float t, float dt); - -private: - CollisionEntityGroup entityGroup; -}; - -#endif // COLLISION_SYSTEM_HPP - diff --git a/src/entity/systems/constraint-system.cpp b/src/entity/systems/constraint-system.cpp deleted file mode 100644 index cf72b46..0000000 --- a/src/entity/systems/constraint-system.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "constraint-system.hpp" - -ConstraintSystem::ConstraintSystem(ComponentManager* componentManager): - System(componentManager), - orbitConstraintGroup(componentManager) -{} - -ConstraintSystem::~ConstraintSystem() -{} - -void ConstraintSystem::update(float t, float dt) -{ - // Solve orbit constraints - for (const OrbitConstraintGroup::Member* member: *orbitConstraintGroup.getMembers()) - { - OrbitConstraintComponent* constraint = std::get<0>(member->components); - TransformComponent* transform = std::get<1>(member->components); - - // Get target transform component - TransformComponent* target = componentManager->getComponent(constraint->target); - if (!target) - { - continue; - } - - // Calculate rotation quaternion - Quaternion azimuthRotation = glm::angleAxis(constraint->azimuth, Vector3(0.0f, 1.0f, 0.0f)); - Quaternion elevationRotation = glm::angleAxis(constraint->elevation, Vector3(-1.0f, 0.0f, 0.0f)); - Quaternion rotation = azimuthRotation * elevationRotation; - - // Calculate translation vector - Vector3 translation = target->transform.translation + rotation * Vector3(0, 0, constraint->distance); - - // Update transform - transform->transform.translation = translation; - transform->transform.rotation = rotation; - } -} - diff --git a/src/entity/systems/constraint-system.hpp b/src/entity/systems/constraint-system.hpp deleted file mode 100644 index 4dce25a..0000000 --- a/src/entity/systems/constraint-system.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 CONSTRAINT_SYSTEM_HPP -#define CONSTRAINT_SYSTEM_HPP - -#include "../entity-group.hpp" -#include "../components/transform-component.hpp" -#include "../components/orbit-constraint-component.hpp" -#include "../system.hpp" - -#include -using namespace Emergent; - -typedef EntityGroup OrbitConstraintGroup; - -class ConstraintSystem: - public System -{ -public: - ConstraintSystem(ComponentManager* componentManager); - virtual ~ConstraintSystem(); - - virtual void update(float t, float dt); - -private: - OrbitConstraintGroup orbitConstraintGroup; -}; - -#endif // CONSTRAINT_SYSTEM_HPP - diff --git a/src/entity/systems/locomotion-system.cpp b/src/entity/systems/locomotion-system.cpp deleted file mode 100644 index 377e9c2..0000000 --- a/src/entity/systems/locomotion-system.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "locomotion-system.hpp" -#include "../../triangle-mesh-operations.hpp" - -LocomotionSystem::LocomotionSystem(ComponentManager* componentManager): - System(componentManager), - leggedLocomotionGroup(componentManager) -{ - leggedLocomotionGroup.addGroupObserver(this); -} - -LocomotionSystem::~LocomotionSystem() -{} - -void LocomotionSystem::update(float t, float dt) -{ - auto members = leggedLocomotionGroup.getMembers(); - - // Perform legged locomotion - for (const LeggedLocomotionGroup::Member* member: *members) - { - LeggedLocomotionComponent* leggedLocomotion = std::get<0>(member->components); - SteeringComponent* steering = std::get<1>(member->components); - TransformComponent* transform = std::get<2>(member->components); - - // Skip entities which are not on a surface - if (!leggedLocomotion->surface) - { - continue; - } - - // Determine target position - Vector3 force = steering->force * dt; - float speed = steering->speed * dt; - - if (speed == 0.0f) - { - continue; - } - - // Calculate direction vector - Vector3 direction = force * (1.0f / speed); - - std::vector segments; - float wrapDistance = wrap(leggedLocomotion->surface, transform->transform.translation, direction, speed, &segments); - - WrapOperationSegment segment = segments.back(); - - Vector3 cartesianStart = cartesian(segment.startPosition, - segment.triangle->edge->vertex->position, - segment.triangle->edge->next->vertex->position, - segment.triangle->edge->previous->vertex->position); - Vector3 cartesianEnd = cartesian(segment.endPosition, - segment.triangle->edge->vertex->position, - segment.triangle->edge->next->vertex->position, - segment.triangle->edge->previous->vertex->position); - - // Calculate wrap direction of final segment - Vector3 segmentDirection(0.0f); - if (cartesianStart != cartesianEnd) - { - segmentDirection = glm::normalize(cartesianEnd - cartesianStart); - } - - // Determine angle between the triangles - float angle = std::acos(glm::dot(leggedLocomotion->surface->normal, segment.triangle->normal)); - if (std::abs(angle) > glm::radians(35.0f)) - { - // Transition - } - - - leggedLocomotion->surface = segment.triangle; - leggedLocomotion->barycentricPosition = segment.endPosition; - transform->transform.translation = cartesianEnd; - if (cartesianStart != cartesianEnd) - { - transform->transform.rotation = lookRotation(segmentDirection, segment.triangle->normal); - } - } -} - -void LocomotionSystem::memberRegistered(const LeggedLocomotionGroup::Member* member) -{} - -void LocomotionSystem::memberUnregistered(const LeggedLocomotionGroup::Member* member) -{} - - diff --git a/src/entity/systems/locomotion-system.hpp b/src/entity/systems/locomotion-system.hpp deleted file mode 100644 index 5d64ddb..0000000 --- a/src/entity/systems/locomotion-system.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 LOCOMOTION_SYSTEM_HPP -#define LOCOMOTION_SYSTEM_HPP - -#include "../entity-group.hpp" -#include "../components/legged-locomotion-component.hpp" -#include "../components/steering-component.hpp" -#include "../components/transform-component.hpp" -#include "../system.hpp" - -#include -using namespace Emergent; - -typedef EntityGroup LeggedLocomotionGroup; - -class LocomotionSystem: public - System, - LeggedLocomotionGroup::Observer -{ -public: - LocomotionSystem(ComponentManager* componentManager); - virtual ~LocomotionSystem(); - - virtual void update(float t, float dt); - -private: - LeggedLocomotionGroup leggedLocomotionGroup; - - virtual void memberRegistered(const LeggedLocomotionGroup::Member* member); - virtual void memberUnregistered(const LeggedLocomotionGroup::Member* member); -}; - -#endif // LOCOMOTION_SYSTEM_HPP - diff --git a/src/entity/systems/particle-system.cpp b/src/entity/systems/particle-system.cpp deleted file mode 100644 index 0c83a93..0000000 --- a/src/entity/systems/particle-system.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "particle-system.hpp" -#include "../../game/curl-noise.hpp" - -ParticleSystem::ParticleSystem(ComponentManager* componentManager): - System(componentManager), - range(nullptr), - material(nullptr) -{ - batch.setTransform(Transform::getIdentity()); - batch.setCullingEnabled(false); -} - -ParticleSystem::~ParticleSystem() -{} - -void ParticleSystem::resize(std::size_t count) -{ - particles.resize(count); - batch.resize(count); - range = batch.addRange(); - range->start = 0; - range->length = particles.size(); - range->material = material; - - while (!stack.empty()) - stack.pop(); - - for (int i = 0; i < particles.size(); ++i) - { - Particle& particle = particles[i]; - particle.life = 0.0f; - particle.size = 0.0f; - - Billboard* billboard = batch.getBillboard(i); - billboard->setDimensions(Vector2(particle.size)); - billboard->resetTweens(); - - stack.push(i); - } -} - -void ParticleSystem::emit(const Vector3& position) -{ - if (!stack.empty()) - { - std::size_t index = stack.top(); - stack.pop(); - - Particle& particle = particles[index]; - particle.life = frand(1.0f, 5.0f); - particle.translation = position; - particle.size = frand(0.01f, 0.2f); - particle.speed = frand(2.0f, 3.0f); - particle.direction = direction + Vector3(frand(-1, 1), 0, frand(-1, 1)) * 0.1f; - particle.direction = glm::normalize(particle.direction); - - Billboard* billboard = batch.getBillboard(index); - billboard->setTranslation(particle.translation); - billboard->setRotation(Quaternion(1, 0, 0, 0)); - billboard->setDimensions(Vector2(particle.size)); - billboard->setTintColor(Vector4(1.0f)); - billboard->resetTweens(); - } -} - -void ParticleSystem::update(float t, float dt) -{ - if (stack.size() == particles.size()) - { - // Inactive - return; - } - - batch.reset(); - - const Vector3 wind = glm::normalize(Vector3(1.0f, 0.0f, -1.0f)) * 1.5f * dt; - float frequency = 0.4f; - Vector3 noiseOffset = Vector3(77.7f, 33.3f, 11.1f) * t * 0.01f; - - for (std::size_t i = 0; i < particles.size(); ++i) - { - Particle& particle = particles[i]; - if (particle.life <= 0.0f) - { - continue; - } - - - Billboard* billboard = batch.getBillboard(i); - - bool reset = false; - - Vector3 smoke = curl(particle.translation, noiseOffset, frequency) * 8.0f; - - - particle.translation += particle.direction * particle.speed * dt + smoke * dt + wind; - particle.size += 0.1f * dt; - particle.life -= dt; - if (particle.life <= 0.0f) - { - particle.size = 0.0f; - reset = true; - - stack.push(i); - } - - billboard->setTranslation(particle.translation); - billboard->setRotation(Quaternion(1, 0, 0, 0)); - billboard->setDimensions(Vector2(particle.size)); - billboard->setTintColor(Vector4(0.5f)); - - if (reset) - { - billboard->resetTweens(); - } - } -} - -void ParticleSystem::setMaterial(Material* material) -{ - this->material = material; - if (range) - { - range->material = material; - } -} - -void ParticleSystem::setDirection(const Vector3& direction) -{ - this->direction = direction; -} - -void ParticleSystem::setLifeTime(float time) -{ - this->lifeTime = time; -} - -void ParticleSystem::setEmissionRate(float frequency) -{ - this->emissionRate = frequency; -} - diff --git a/src/entity/systems/particle-system.hpp b/src/entity/systems/particle-system.hpp deleted file mode 100644 index 7345791..0000000 --- a/src/entity/systems/particle-system.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 PARTICLE_SYSTEM_HPP -#define PARTICLE_SYSTEM_HPP - -#include "../components/model-component.hpp" -#include "../components/transform-component.hpp" -#include "../entity-group.hpp" -#include "../system.hpp" - -#include -using namespace Emergent; - -#include - -class ParticleSystem: - public System -{ -public: - ParticleSystem(ComponentManager* componentManager); - ~ParticleSystem(); - - void setMaterial(Material* material); - - void setParticleCount(std::size_t count); - void setDirection(const Vector3& direction); - void setLifeTime(float time); - void setEmissionRate(float frequency); - - const BillboardBatch* getBillboardBatch() const; - BillboardBatch* getBillboardBatch(); - - void resize(std::size_t count); - virtual void update(float t, float dt); - - void emit(const Vector3& position); - -private: - struct Particle - { - Vector3 translation; - float size; - float life; - float speed; - Vector3 direction; - }; - - BillboardBatch batch; - BillboardBatch::Range* range; - - Material* material; - std::vector particles; - Vector3 direction; - float lifeTime; - float emissionRate; - std::stack stack; -}; - -inline const BillboardBatch* ParticleSystem::getBillboardBatch() const -{ - return &batch; -} - -inline BillboardBatch* ParticleSystem::getBillboardBatch() -{ - return &batch; -} - -#endif // PARTICLE_SYSTEM_HPP diff --git a/src/entity/systems/render-system.cpp b/src/entity/systems/render-system.cpp deleted file mode 100644 index 6b19962..0000000 --- a/src/entity/systems/render-system.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "render-system.hpp" - -RenderSystem::RenderSystem(ComponentManager* componentManager, Scene* scene): - System(componentManager), - cameraGroup(componentManager), - modelGroup(componentManager), - scene(scene) -{ - cameraGroup.addGroupObserver(this); - modelGroup.addGroupObserver(this); -} - -RenderSystem::~RenderSystem() -{} - -void RenderSystem::update(float t, float dt) -{ - // Update transform of all cameras - for (const CameraGroup::Member* member: *cameraGroup.getMembers()) - { - CameraComponent* camera = std::get<0>(member->components); - TransformComponent* transform = std::get<1>(member->components); - - Vector3 forward = glm::normalize(transform->transform.rotation * Vector3(0.0f, 0.0f, -1.0f)); - Vector3 up = glm::normalize(transform->transform.rotation * Vector3(0.0f, 1.0f, 0.0f)); - Vector3 right = glm::normalize(glm::cross(forward, up)); - - camera->camera->lookAt(transform->transform.translation, transform->transform.translation + forward, up); - //camera->camera->setTransform(transform->transform); - } - - // Update transform of all model instances - for (const ModelGroup::Member* member: *modelGroup.getMembers()) - { - ModelComponent* model = std::get<0>(member->components); - TransformComponent* transform = std::get<1>(member->components); - - model->model.setTransform(transform->transform); - } -} - -void RenderSystem::memberRegistered(const CameraGroup::Member* member) -{ - CameraComponent* camera = std::get<0>(member->components); - scene->addObject(camera->camera); -} - -void RenderSystem::memberUnregistered(const CameraGroup::Member* member) -{ - CameraComponent* camera = std::get<0>(member->components); - scene->removeObject(camera->camera); -} - -void RenderSystem::memberRegistered(const ModelGroup::Member* member) -{ - ModelComponent* model = std::get<0>(member->components); - scene->addObject(&model->model); -} - -void RenderSystem::memberUnregistered(const ModelGroup::Member* member) -{ - ModelComponent* model = std::get<0>(member->components); - scene->removeObject(&model->model); -} - diff --git a/src/entity/systems/render-system.hpp b/src/entity/systems/render-system.hpp deleted file mode 100644 index 9c1ed30..0000000 --- a/src/entity/systems/render-system.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 RENDER_SYSTEM_HPP -#define RENDER_SYSTEM_HPP - -#include "../components/camera-component.hpp" -#include "../components/model-component.hpp" -#include "../components/transform-component.hpp" -#include "../entity-group.hpp" -#include "../system.hpp" - -#include -using namespace Emergent; - -typedef EntityGroup CameraGroup; -typedef EntityGroup ModelGroup; - -/** - * Abstract base class for entity systems. - */ -class RenderSystem: - public System, - public CameraGroup::Observer, - public ModelGroup::Observer -{ -public: - RenderSystem(ComponentManager* componentManager, Scene* scene); - virtual ~RenderSystem(); - - virtual void update(float t, float dt); - -private: - CameraGroup cameraGroup; - ModelGroup modelGroup; - Scene* scene; - - virtual void memberRegistered(const CameraGroup::Member* member); - virtual void memberUnregistered(const CameraGroup::Member* member); - virtual void memberRegistered(const ModelGroup::Member* member); - virtual void memberUnregistered(const ModelGroup::Member* member); -}; - -#endif // RENDER_SYSTEM_HPP - diff --git a/src/entity/systems/sound-system.cpp b/src/entity/systems/sound-system.cpp deleted file mode 100644 index b2b62bb..0000000 --- a/src/entity/systems/sound-system.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "sound-system.hpp" -#include -#include "dr_libs/dr_wav.h" - -SoundSystem::SoundSystem(ComponentManager* componentManager): - System(componentManager), - entityGroup(componentManager), - device(nullptr), - context(nullptr) -{ - device = alcOpenDevice(nullptr); - if (!device) - { - throw std::runtime_error("SoundSystem::SoundSystem(): Failed to open audio device."); - } - - context = alcCreateContext(device, nullptr); - if (!alcMakeContextCurrent(context)) - { - throw std::runtime_error("SoundSystem::SoundSystem(): Failed to create audio context."); - } - - ALfloat listenerOrientation[] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f }; - alListener3f(AL_POSITION, 0.0f, 0.0f, 1.0f); - alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f); - alListenerfv(AL_ORIENTATION, listenerOrientation); - - alGenSources((ALuint)1, &source); - alSourcef(source, AL_PITCH, 1); - alSourcef(source, AL_GAIN, 1); - alSource3f(source, AL_POSITION, 0, 0, 0); - alSource3f(source, AL_VELOCITY, 0, 0, 0); - alSourcei(source, AL_LOOPING, AL_FALSE); - - alGenBuffers((ALuint)1, &buffer); - - /* - // Load wav file - { - const char* filename = "data/shutter.wav"; - unsigned int channels; - unsigned int sampleRate; - drwav_uint64 frameCount; - std::int16_t* sampleData = drwav_open_file_and_read_pcm_frames_s16(filename, &channels, &sampleRate, &frameCount); - - if (sampleData == nullptr) - { - throw std::runtime_error("Couldn't load wav file"); - drwav_free(sampleData); - } - - bool stereo = (channels > 1); - ALenum format = (stereo) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16; - - std::size_t sampleCount = frameCount * channels; - std::size_t sampleDataSize = sampleCount * sizeof(drwav_int16); - - alBufferData(buffer, format, sampleData, sampleDataSize, sampleRate); - } - */ - - alSourcei(source, AL_BUFFER, buffer); - //alSourcePlay(source); - -} - -SoundSystem::~SoundSystem() -{ - alDeleteSources(1, &source); - alDeleteBuffers(1, &buffer); - alcMakeContextCurrent(nullptr); - alcDestroyContext(context); - alcCloseDevice(device); -} - -void SoundSystem::scrot() -{ - alSourcePlay(source); -} - -void SoundSystem::update(float t, float dt) -{ - auto members = entityGroup.getMembers(); - for (const SoundSourceEntityGroup::Member* member: *members) - { - TransformComponent* transform = std::get<0>(member->components); - SoundSourceComponent* sound = std::get<1>(member->components); - } -} - -void SoundSystem::memberRegistered(const SoundSourceEntityGroup::Member* member) -{ - TransformComponent* transform = std::get<0>(member->components); - SoundSourceComponent* sound = std::get<1>(member->components); -} - -void SoundSystem::memberUnregistered(const SoundSourceEntityGroup::Member* member) -{ - TransformComponent* transform = std::get<0>(member->components); - SoundSourceComponent* sound = std::get<1>(member->components); -} - diff --git a/src/entity/systems/sound-system.hpp b/src/entity/systems/sound-system.hpp deleted file mode 100644 index 013909d..0000000 --- a/src/entity/systems/sound-system.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 SOUND_SYSTEM_HPP -#define SOUND_SYSTEM_HPP - -#include "../entity-group.hpp" -#include "../system.hpp" -#include "../components/sound-source-component.hpp" -#include "../components/transform-component.hpp" -#include -#include - -typedef EntityGroup SoundSourceEntityGroup; - -class SoundSystem: - public System, - public SoundSourceEntityGroup::Observer -{ -public: - SoundSystem(ComponentManager* componentManager); - virtual ~SoundSystem(); - - virtual void update(float t, float dt); - - void scrot(); - -private: - virtual void memberRegistered(const SoundSourceEntityGroup::Member* member); - virtual void memberUnregistered(const SoundSourceEntityGroup::Member* member); - - SoundSourceEntityGroup entityGroup; - ALCdevice* device; - ALCcontext* context; - ALuint source; - ALuint buffer; -}; - -#endif // SOUND_SYSTEM_HPP - diff --git a/src/entity/systems/steering-system.cpp b/src/entity/systems/steering-system.cpp deleted file mode 100644 index d8f0ee0..0000000 --- a/src/entity/systems/steering-system.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "steering-system.hpp" - -SteeringSystem::SteeringSystem(ComponentManager* componentManager): - System(componentManager), - boids(componentManager) -{ - boids.addGroupObserver(this); -} - -SteeringSystem::~SteeringSystem() -{} - -void SteeringSystem::update(float t, float dt) -{ - auto members = boids.getMembers(); - - for (const SteeringGroup::Member* member: *members) - { - SteeringComponent* steering = std::get<0>(member->components); - - // Initialize summed steering force - steering->force = Vector3(0.0f); - steering->speed = 0.0f; - float speedSquared = 0.0f; - float maxSpeedSquared = steering->maxSpeed * steering->maxSpeed; - bool truncated = false; - - if (steering->behaviorCount > 0) - { - // Sort steering beaviors by priority - std::sort(steering->behaviors, steering->behaviors + steering->behaviorCount, - [](const SteeringBehavior& a, const SteeringBehavior& b) -> bool - { - return (a.priority >= b.priority); - } - ); - } - - // Evaluate steering forces in order - for (std::size_t i = 0; i < steering->behaviorCount; ++i) - { - const SteeringBehavior& behavior = steering->behaviors[i]; - - // Skip zero-weighted steering behaviors - if (behavior.weight == 0.0f) - { - continue; - } - - // Add weighted steering behavior force - steering->force += behavior.function() * behavior.weight; - - // Limit speed - speedSquared = glm::length2(steering->force); - if (speedSquared >= maxSpeedSquared) - { - steering->force *= (1.0f / std::sqrt(speedSquared)) * steering->maxSpeed; - steering->speed = steering->maxSpeed; - truncated = true; - break; - } - } - - if (!truncated) - { - steering->speed = std::sqrt(speedSquared); - } - } -} - -void SteeringSystem::memberRegistered(const SteeringGroup::Member* member) -{} - -void SteeringSystem::memberUnregistered(const SteeringGroup::Member* member) -{} - - diff --git a/src/entity/systems/steering-system.hpp b/src/entity/systems/steering-system.hpp deleted file mode 100644 index 798816f..0000000 --- a/src/entity/systems/steering-system.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 STEERING_SYSTEM_HPP -#define STEERING_SYSTEM_HPP - -#include "../entity-group.hpp" -#include "../components/steering-component.hpp" -#include "../system.hpp" - -#include -using namespace Emergent; - -typedef EntityGroup SteeringGroup; - -class SteeringSystem: public - System, - SteeringGroup::Observer -{ -public: - SteeringSystem(ComponentManager* componentManager); - virtual ~SteeringSystem(); - - /** - * Calculates the steering force for each steering component using weighted truncated running sums with prioritization. - */ - virtual void update(float t, float dt); - -private: - SteeringGroup boids; - - virtual void memberRegistered(const SteeringGroup::Member* member); - virtual void memberUnregistered(const SteeringGroup::Member* member); -}; - -#endif // STEERING_SYSTEM_HPP - diff --git a/src/entity/systems/terrain-system.cpp b/src/entity/systems/terrain-system.cpp deleted file mode 100644 index 70aba7f..0000000 --- a/src/entity/systems/terrain-system.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "terrain-system.hpp" -#include "graphics/vertex-format.hpp" - -TerrainSystem::TerrainSystem(ComponentManager* componentManager): - System(componentManager), - terrainCreationGroup(componentManager), - terrainGroup(componentManager) -{ - terrainCreationGroup.addGroupObserver(this); - terrainGroup.addGroupObserver(this); - patchSize = 1.0f; -} - -TerrainSystem::~TerrainSystem() -{} - -void TerrainSystem::update(float t, float dt) -{ - - auto members = terrainGroup.getMembers(); - for (const TerrainGroup::Member* member: *members) - { - ModelComponent* model = std::get<0>(member->components); - TerrainPatchComponent* patch = std::get<1>(member->components); - TransformComponent* transform = std::get<2>(member->components); - - transform->transform.translation = Vector3(std::get<0>(patch->position), 0.0f, std::get<1>(patch->position)) * patchSize; - } -} - -void TerrainSystem::setPatchSize(float size) -{ - patchSize = size; -} - -void TerrainSystem::memberRegistered(const TerrainCreationGroup::Member* member) -{ - TerrainPatchComponent* patch = std::get<0>(member->components); - TransformComponent* transform = std::get<1>(member->components); - - // Generate a subdivided plane mesh - TriangleMesh* patchMesh = generatePlane(patch->subdivisions); - - // Generate a model from the subdivided plane - Model* patchModel = generateModel(patchMesh); - - // Add model component to the entity - ModelComponent* model = new ModelComponent(); - model->model.setModel(patchModel); - componentManager->addComponent(member->entity, model); - - // Set scale of the transform component - transform->transform.scale = Vector3(patchSize, 1.0f, patchSize); - transform->transform.translation = Vector3(std::get<0>(patch->position), 0.0f, std::get<1>(patch->position)) * patchSize; -} - -void TerrainSystem::memberUnregistered(const TerrainCreationGroup::Member* member) -{} - -void TerrainSystem::memberRegistered(const TerrainGroup::Member* member) -{ - // Add terrain patch to the patch map - TerrainPatchComponent* patch = std::get<1>(member->components); - patchMap[patch->position] = member; -} - -void TerrainSystem::memberUnregistered(const TerrainGroup::Member* member) -{ - // Remove terrain patch from the patch map - TerrainPatchComponent* patch = std::get<1>(member->components); - auto it = patchMap.find(patch->position); - if (it != patchMap.end()) - { - patchMap.erase(it); - } - - // TODO: free created terrain patch model -} - -TriangleMesh* TerrainSystem::generatePlane(int subdivisions) -{ - //std::size_t quadCount = std::pow(4, subdivisions); - //std::size_t triangleCount = quadCount * 2; - std::size_t columns = std::pow(2, subdivisions); - std::size_t rows = columns; - std::size_t vertexCount = (columns + 1) * (rows + 1); - float vertexIncrement = 1.0f / static_cast(columns); - - // Generate vertices - std::vector vertices; - Vector3 position(0.0f); - position.z = -0.5f; - for (std::size_t i = 0; i <= rows; ++i) - { - position.x = -0.5f; - for (std::size_t j = 0; j <= columns; ++j) - { - vertices.push_back(position); - position.x += vertexIncrement; - - } - - position.z += vertexIncrement; - } - - // Generate indices - std::vector indices; - for (std::size_t i = 0; i < rows; ++i) - { - for (std::size_t j = 0; j < columns; ++j) - { - unsigned int a = i * (columns + 1) + j; - unsigned int b = (i + 1) * (columns + 1) + j; - unsigned int c = i * (columns + 1) + j + 1; - unsigned int d = (i + 1) * (columns + 1) + j + 1; - - indices.push_back(a); - indices.push_back(b); - indices.push_back(c); - indices.push_back(c); - indices.push_back(b); - indices.push_back(d); - } - } - - return new TriangleMesh(vertices, indices); -} - -Model* TerrainSystem::generateModel(TriangleMesh* mesh) -{ - std::size_t triangleCount = mesh->getTriangles()->size(); - // Vertex position + vertex normal - std::size_t vertexSize = 3 + 3; - std::size_t vertexCount = triangleCount * 3; - - #if defined(DEBUG) - const Vector3 barycentricCoordinates[3] = - { - Vector3(1, 0, 0), - Vector3(0, 1, 0), - Vector3(0, 0, 1) - }; - - vertexSize += 3; - #endif // DEBUG - - // Generate vertex data - float* vertexData = new float[vertexSize * vertexCount]; - float* v = vertexData; - for (std::size_t i = 0; i < triangleCount; ++i) - { - const TriangleMesh::Triangle* triangle = (*mesh->getTriangles())[i]; - - const TriangleMesh::Vertex* a = triangle->edge->vertex; - const TriangleMesh::Vertex* b = triangle->edge->next->vertex; - const TriangleMesh::Vertex* c = triangle->edge->previous->vertex; - const TriangleMesh::Vertex* abc[] = {a, b, c}; - const Vector3& normal = triangle->normal; - - - for (std::size_t j = 0; j < 3; ++j) - { - *(v++) = abc[j]->position[0]; - *(v++) = abc[j]->position[1]; - *(v++) = abc[j]->position[2]; - *(v++) = normal.x; - *(v++) = normal.y; - *(v++) = normal.z; - - #if defined(DEBUG) - { - *(v++) = barycentricCoordinates[j].x; - *(v++) = barycentricCoordinates[j].y; - *(v++) = barycentricCoordinates[j].z; - } - #endif // DEBUG - } - - // Calculate smoothed vertex normal - /* - Vector3 normal(0.0f); - TriangleMesh::Edge* start = vertex->edge; - TriangleMesh::Edge* e = start; - do - { - normal += e->triangle->normal; - e = e->previous->symmetric; - } - while (e != start && e != nullptr); - normal = glm::normalize(normal); - */ - } - - // Generate index data - std::size_t indexCount = triangleCount * 3; - std::uint32_t* indexData = new std::uint32_t[indexCount]; - std::uint32_t* index = indexData; - for (std::size_t i = 0; i < triangleCount; ++i) - { - *(index++) = i * 3; - *(index++) = i * 3 + 1; - *(index++) = i * 3 + 2; - } - - // Calculate AABB bounds - AABB bounds; - bounds.setMin(mesh->getVertices()->front()->position); - bounds.setMax(mesh->getVertices()->front()->position); - for (TriangleMesh::Vertex* vertex: *mesh->getVertices()) - { - bounds.add(vertex->position); - } - - GLuint vao; - GLuint vbo; - GLuint ibo; - - // Generate and bind VAO - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); - - // Generate and bind VBO, then upload vertex data - glGenBuffers(1, &vbo); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertexSize * vertexCount, vertexData, GL_STATIC_DRAW); - - // Setup vertex attribute arrays - std::size_t attribOffset = 0; - std::size_t attribSize = 0; - - // Vertex position attribute - attribSize = 3; - glEnableVertexAttribArray(VERTEX_POSITION); - glVertexAttribPointer(VERTEX_POSITION, attribSize, GL_FLOAT, GL_FALSE, sizeof(float) * vertexSize, (char*)0 + attribOffset * sizeof(float)); - attribOffset += attribSize; - - // Vertex normal attribute - attribSize = 3; - glEnableVertexAttribArray(VERTEX_NORMAL); - glVertexAttribPointer(VERTEX_NORMAL, attribSize, GL_FLOAT, GL_FALSE, sizeof(float) * vertexSize, (char*)0 + attribOffset * sizeof(float)); - attribOffset += attribSize; - - #if defined(DEBUG) - { - // Vertex barycentric coordinates attribute - attribSize = 3; - glEnableVertexAttribArray(VERTEX_BARYCENTRIC); - glVertexAttribPointer(VERTEX_BARYCENTRIC, attribSize, GL_FLOAT, GL_FALSE, sizeof(float) * vertexSize, (char*)0 + attribOffset * sizeof(float)); - attribOffset += attribSize; - } - #endif // DEBUG - - // Generate and bind IBO, then upload index data - glGenBuffers(1, &ibo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(std::uint32_t) * indexCount, indexData, GL_STATIC_DRAW); - - // Delete vertex and index data - delete[] vertexData; - delete[] indexData; - - // Create model - Model* model = new Model(); - - // Set model buffers - model->setVAO(vao); - model->setVBO(vbo); - model->setIBO(ibo); - - // Set model bounds - model->setBounds(bounds); - - // Create model material - Material* material = new Material(); - material->setShader(nullptr); - ShaderVariable* albedo = material->addVariable("albedo"); - ShaderVariable* roughness = material->addVariable("roughness"); - ShaderVariable* metalness = material->addVariable("metalness"); - ShaderVariable* opacity = material->addVariable("opacity"); - albedo->setValue(Vector3(frand(0.0f, 1.0f), frand(0.0f, 1.0f), frand(0.0, 1.0f))); - roughness->setValue(0.5f); - metalness->setValue(0.0f); - opacity->setValue(1.0f); - - // Create model group - Model::Group* group = new Model::Group(); - group->name = std::string(); - group->material = material; - group->indexOffset = 0; - group->triangleCount = triangleCount; - - // Add model group to the model - model->addGroup(group); - - return model; -} - -void TerrainSystem::projectMesh(TriangleMesh* mesh) -{ - -} - diff --git a/src/entity/systems/terrain-system.hpp b/src/entity/systems/terrain-system.hpp deleted file mode 100644 index e1319d9..0000000 --- a/src/entity/systems/terrain-system.hpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 TERRAIN_SYSTEM_HPP -#define TERRAIN_SYSTEM_HPP - -#include "../entity-group.hpp" -#include "../components/model-component.hpp" -#include "../components/terrain-patch-component.hpp" -#include "../components/transform-component.hpp" -#include "../system.hpp" -#include - -// The terrain system creates a model and adds the model component to new members of this group. -typedef EntityGroup TerrainCreationGroup; -typedef EntityGroup TerrainGroup; - -class TerrainSystem: - public System, - public TerrainCreationGroup::Observer, - public TerrainGroup::Observer -{ -public: - TerrainSystem(ComponentManager* componentManager); - virtual ~TerrainSystem(); - - virtual void update(float t, float dt); - - void setPatchSize(float size); - -private: - virtual void memberRegistered(const TerrainCreationGroup::Member* member); - virtual void memberUnregistered(const TerrainCreationGroup::Member* member); - virtual void memberRegistered(const TerrainGroup::Member* member); - virtual void memberUnregistered(const TerrainGroup::Member* member); - - /** - * Generates a subdivided plane triangle mesh. - * - * 0 subdivisions: - * +---+ - * | / | - * +---+ - * - * 1 subdivision: - * +---+---+ - * | / | / | - * +---+---+ - * | / | / | - * +---+---+ - * - * 2 subdivisions: - * +---+---+---+---+ - * | / | / | / | / | - * +---+---+---+---+ - * | / | / | / | / | - * +---+---+---+---+ - * | / | / | / | / | - * +---+---+---+---+ - * | / | / | / | / | - * +---+---+---+---+ - * - * @param subdivisions Number of subdivisions. - * @return Generated triangle mesh. - */ - TriangleMesh* generatePlane(int subdivisions); - - /** - * Generates a model from a triangle mesh. - * - * @param mesh Triangle mesh from which a model will be generated. - * @return Generated model. - */ - Model* generateModel(TriangleMesh* mesh); - - /** - * Projects the vertices of a triangle mesh onto terrain brush geometry. - */ - void projectMesh(TriangleMesh* mesh); - - TerrainCreationGroup terrainCreationGroup; - TerrainGroup terrainGroup; - - std::map, const TerrainGroup::Member*> patchMap; - float patchSize; -}; - -#endif // TERRAIN_SYSTEM_HPP - diff --git a/src/entity/systems/tool-system.cpp b/src/entity/systems/tool-system.cpp deleted file mode 100644 index 9a28b3b..0000000 --- a/src/entity/systems/tool-system.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 "tool-system.hpp" - -ToolSystem::ToolSystem(ComponentManager* componentManager): - System(componentManager), - toolGroup(componentManager), - picked(false) -{ - toolGroup.addGroupObserver(this); -} - -ToolSystem::~ToolSystem() -{} - -void ToolSystem::update(float t, float dt) -{ - pick(); - - auto members = toolGroup.getMembers(); - for (const ToolGroup::Member* member: *members) - { - ModelComponent* model = std::get<0>(member->components); - ToolComponent* tool = std::get<1>(member->components); - TransformComponent* transform = std::get<2>(member->components); - - model->model.setActive(tool->active); - - if (picked) - { - transform->transform.translation = mouseWorldPosition; - } - } - - picked = false; -} - -void ToolSystem::setPickingCamera(const Camera* camera) -{ - pickingCamera = camera; -} - -void ToolSystem::setPickingViewport(const Vector4& viewport) -{ - pickingViewport = viewport; -} - -void ToolSystem::pick() -{ - Vector3 mouseNear = pickingCamera->unproject(Vector3(mouseScreenPosition, 0.0f), pickingViewport); - Vector3 mouseFar = pickingCamera->unproject(Vector3(mouseScreenPosition, 1.0f), pickingViewport); - - Ray pickingRay; - pickingRay.origin = mouseNear; - pickingRay.direction = glm::normalize(mouseFar - mouseNear); - Plane pickingPlane(Vector3(0.0f, 1.0f, 0.0f), Vector3(0.0f)); - - auto pickingIntersection = pickingRay.intersects(pickingPlane); - picked = std::get<0>(pickingIntersection); - if (picked) - { - mouseWorldPosition = pickingRay.extrapolate(std::get<1>(pickingIntersection)); - } -} - -void ToolSystem::memberRegistered(const ToolGroup::Member* member) -{ - ToolComponent* tool = std::get<1>(member->components); -} - -void ToolSystem::memberUnregistered(const ToolGroup::Member* member) -{} - -void ToolSystem::handleEvent(const MouseMovedEvent& event) -{ - mouseScreenPosition = Vector2(event.x, pickingViewport[3] - event.y); -} - diff --git a/src/entity/systems/tool-system.hpp b/src/entity/systems/tool-system.hpp deleted file mode 100644 index 0adc9b5..0000000 --- a/src/entity/systems/tool-system.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 TOOL_SYSTEM_HPP -#define TOOL_SYSTEM_HPP - -#include "../entity-group.hpp" -#include "../components/model-component.hpp" -#include "../components/tool-component.hpp" -#include "../components/transform-component.hpp" -#include "../system.hpp" - -#include -using namespace Emergent; - -typedef EntityGroup ToolGroup; - -class ToolSystem: - public System, - public ToolGroup::Observer, - public EventHandler -{ -public: - ToolSystem(ComponentManager* componentManager); - virtual ~ToolSystem(); - - virtual void update(float t, float dt); - - void setPickingCamera(const Camera* camera); - void setPickingViewport(const Vector4& viewport); - -private: - void pick(); - virtual void memberRegistered(const ToolGroup::Member* member); - virtual void memberUnregistered(const ToolGroup::Member* member); - virtual void handleEvent(const MouseMovedEvent& event); - - Vector2 mouseScreenPosition; - Vector3 mouseWorldPosition; - const Camera* pickingCamera; - Vector4 pickingViewport; - bool picked; - ToolGroup toolGroup; -}; - -#endif // TOOL_SYSTEM_HPP - diff --git a/src/game.cpp b/src/game.cpp deleted file mode 100644 index 1e518d8..0000000 --- a/src/game.cpp +++ /dev/null @@ -1,3636 +0,0 @@ -/* - * Copyright (C) 2017-2019 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.hpp" -#include "resources/string-table.hpp" -#include "filesystem.hpp" -#include "timestamp.hpp" -#include "ui/ui.hpp" -#include "graphics/ui-render-pass.hpp" -#include "graphics/shadow-map-render-pass.hpp" -#include "graphics/clear-render-pass.hpp" -#include "graphics/sky-render-pass.hpp" -#include "graphics/lighting-render-pass.hpp" -#include "graphics/silhouette-render-pass.hpp" -#include "graphics/final-render-pass.hpp" -#include "resources/resource-manager.hpp" -#include "resources/text-file.hpp" -#include "game/camera-rig.hpp" -#include "game/lens.hpp" -#include "game/forceps.hpp" -#include "game/brush.hpp" -#include "entity/component-manager.hpp" -#include "entity/components/transform-component.hpp" -#include "entity/components/camera-component.hpp" -#include "entity/components/orbit-constraint-component.hpp" -#include "entity/components/model-component.hpp" -#include "entity/components/terrain-patch-component.hpp" -#include "entity/entity-manager.hpp" -#include "entity/entity-template.hpp" -#include "entity/system-manager.hpp" -#include "entity/systems/sound-system.hpp" -#include "entity/systems/collision-system.hpp" -#include "entity/systems/render-system.hpp" -#include "entity/systems/constraint-system.hpp" -#include "entity/systems/tool-system.hpp" -#include "entity/systems/locomotion-system.hpp" -#include "entity/systems/behavior-system.hpp" -#include "entity/systems/steering-system.hpp" -#include "entity/systems/particle-system.hpp" -#include "entity/systems/terrain-system.hpp" -#include "configuration.hpp" -#include "stb/stb_image_write.h" -#include "menu.hpp" -#include -#include -#include -#include -#include -#include -#include "debug/command-interpreter.hpp" -#include "debug/logger.hpp" -#include "debug/ansi-escape-codes.hpp" - -Game::Game(int argc, char* argv[]): - window(nullptr) -{ - // Determine application name - std::string applicationName; - #if defined(_WIN32) - applicationName = "Antkeeper"; - #else - applicationName = "antkeeper"; - #endif - - // Form resource paths - dataPath = getDataPath(applicationName) + "data/"; - configPath = getConfigPath(applicationName); - controlsPath = configPath + "controls/"; - - // Create nonexistent config directories - std::vector configPaths; - configPaths.push_back(configPath); - configPaths.push_back(controlsPath); - for (const std::string& path: configPaths) - { - if (!pathExists(path)) - { - createDirectory(path); - } - } - - // Setup resource manager - resourceManager = new ResourceManager(); - - // Include resource search paths in order of priority - resourceManager->include(controlsPath); - resourceManager->include(configPath); - resourceManager->include(dataPath); - - // Subscribe the game to scheduled function events - eventDispatcher.subscribe(this); - toggleFullscreenDisabled = false; - - float3x3 m1; - float3x3 m2; - float3x3 m3 = m1 + m2; - float3x3 m4 = m2 - m3; - - float4x4 projection = perspective(radians(45.0f), 3.0f / 4.0f, -1.0f, 1.0f); - float4x4 ortho_proj = ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); -} - -Game::~Game() -{ - if (window) - { - windowManager->destroyWindow(window); - } -} - -std::string Game::getString(const std::string& name, std::optional languageIndex) const -{ - std::string value; - - if (languageIndex == std::nullopt) - { - languageIndex = currentLanguageIndex; - } - - auto it = stringTableIndex.find(name); - if (it != stringTableIndex.end()) - { - value = (*stringTable)[it->second][(*languageIndex) + 2]; - if (value.empty()) - { - value = std::string("# EMPTY STRING: ") + name + std::string(" #"); - } - } - else - { - value = std::string("# MISSING STRING: ") + name + std::string(" #"); - } - - return value; -} - -void Game::changeLanguage(std::size_t nextLanguageIndex) -{ - // Get names of fonts - std::string menuFontFilename = getString("menu-font-filename"); - - // Unload fonts - delete menuFont; - resourceManager->unload(menuFontFilename); - - // Change current language index - currentLanguageIndex = nextLanguageIndex; - - // Reload fonts - loadFonts(); - - // Set window title - window->setTitle(getString("title").c_str()); - - // Repopulate UI element strings - restringUI(); - - // Resize the UI - resizeUI(w, h); - - // Reselect menu item - if (currentMenuItem) - { - menuSelectorSlideAnimation.stop(); - selectMenuItem(menuItemIndex, false); - - uiRootElement->update(); - currentMenu->getContainer()->resetTweens(); - } - - // Save settings - language = getString("language-code"); - saveSettings(); -} - -void Game::nextLanguage() -{ - changeLanguage((currentLanguageIndex + 1) % languageCount); -} - -void Game::openMenu(Menu* menu, int selectedItemIndex) -{ - if (currentMenu) - { - closeCurrentMenu(); - } - - currentMenu = menu; - uiRootElement->addChild(currentMenu->getContainer()); - currentMenu->getContainer()->addChild(menuSelectorImage); - currentMenu->getContainer()->setTintColor(Vector4(1.0f)); - - for (MenuItem* item: *currentMenu->getItems()) - { - item->getContainer()->setTintColor(menuItemInactiveColor); - } - - selectMenuItem(selectedItemIndex, false); - - uiRootElement->update(); - currentMenu->getContainer()->resetTweens(); -} - -void Game::closeCurrentMenu() -{ - uiRootElement->removeChild(currentMenu->getContainer()); - currentMenu->getContainer()->removeChild(menuSelectorImage); - currentMenu->getContainer()->setTintColor(Vector4(1.0f)); - for (MenuItem* item: *currentMenu->getItems()) - { - item->getContainer()->setTintColor(menuItemInactiveColor); - } - - currentMenu = nullptr; - currentMenuItem = nullptr; - menuItemIndex = -1; - - menuFadeAnimation.stop(); - menuSelectorSlideAnimation.stop(); - menuItemSelectAnimation.stop(); - menuItemDeselectAnimation.stop(); - - previousMenu = currentMenu; - currentMenu = nullptr; -} - -void Game::selectMenuItem(int index, bool tween) -{ - bool reselected = false; - - if (index != menuItemIndex) - { - if (menuItemSelectAnimation.isPlaying()) - { - menuItemSelectAnimation.stop(); - currentMenuItem->getContainer()->setTintColor(menuItemActiveColor); - } - - if (menuItemDeselectAnimation.isPlaying()) - { - menuItemDeselectAnimation.stop(); - previousMenuItem->getContainer()->setTintColor(menuItemInactiveColor); - } - - // Save previous menu item - previousMenuItem = currentMenuItem; - - // Determine current menu item - menuItemIndex = index; - currentMenuItem = (*(currentMenu->getItems()))[index]; - } - else - { - reselected = true; - } - - // Determine target position of menu item selector - Vector2 itemTranslation = currentMenuItem->getContainer()->getPosition() - currentMenu->getContainer()->getPosition(); - Vector2 itemDimensions = currentMenuItem->getContainer()->getDimensions(); - float spacing = fontSizePX; - Vector2 translation; - translation.x = itemTranslation.x - menuSelectorImage->getDimensions().x - spacing; - translation.y = itemTranslation.y + itemDimensions.y * 0.5f - menuSelectorImage->getDimensions().y * 0.5; - - // Create tween animations - if (!reselected && tween && previousMenuItem != nullptr) - { - float tweenDuration = 0.2f; - - Vector2 oldTranslation = menuSelectorImage->getTranslation(); - Vector2 newTranslation = translation; - - // Slide animation - { - menuSelectorSlideClip.removeChannels(); - AnimationChannel* channel = menuSelectorSlideClip.addChannel(0); - channel->insertKeyframe(0.0f, oldTranslation.y); - channel->insertKeyframe(tweenDuration, newTranslation.y); - menuSelectorSlideAnimation.setTimeFrame(menuSelectorSlideClip.getTimeFrame()); - menuSelectorSlideAnimation.rewind(); - menuSelectorSlideAnimation.play(); - } - - // Color animations - { - menuItemSelectClip.removeChannels(); - AnimationChannel* channel = menuItemSelectClip.addChannel(0); - channel->insertKeyframe(0.0f, menuItemInactiveColor); - channel->insertKeyframe(tweenDuration, menuItemActiveColor); - menuItemSelectAnimation.setTimeFrame(menuItemSelectClip.getTimeFrame()); - menuItemSelectAnimation.rewind(); - menuItemSelectAnimation.play(); - - if (previousMenuItem) - { - menuItemDeselectClip.removeChannels(); - channel = menuItemDeselectClip.addChannel(0); - channel->insertKeyframe(0.0f, menuItemActiveColor); - channel->insertKeyframe(tweenDuration, menuItemInactiveColor); - menuItemDeselectAnimation.setTimeFrame(menuItemDeselectClip.getTimeFrame()); - menuItemDeselectAnimation.rewind(); - menuItemDeselectAnimation.play(); - } - } - - menuSelectorImage->setTranslation(Vector2(newTranslation.x, oldTranslation.y)); - } - else if (!tween) - { - menuSelectorImage->setTranslation(translation); - currentMenuItem->getContainer()->setTintColor(menuItemActiveColor); - - if (previousMenuItem) - { - previousMenuItem->getContainer()->setTintColor(menuItemInactiveColor); - } - } -} - -void Game::selectNextMenuItem() -{ - int index = (menuItemIndex + 1) % currentMenu->getItems()->size(); - selectMenuItem(index, true); -} - -void Game::selectPreviousMenuItem() -{ - int index = (menuItemIndex + (currentMenu->getItems()->size() - 1)) % currentMenu->getItems()->size(); - selectMenuItem(index, true); -} - -void Game::activateMenuItem() -{ - currentMenuItem->activate(); -} - -void Game::activateLastMenuItem() -{ - if (currentMenu) - { - (*currentMenu->getItems())[currentMenu->getItems()->size() - 1]->activate(); - } -} - -void Game::toggleFullscreen() -{ - if (!toggleFullscreenDisabled) - { - fullscreen = !(*fullscreen); - //window->setFullscreen(*fullscreen); - - if (!(*fullscreen)) - { - const Display* display = deviceManager->getDisplays()->front(); - int displayWidth = std::get<0>(display->getDimensions()); - int displayHeight = std::get<1>(display->getDimensions()); - - w = (*windowResolution)[0]; - h = (*windowResolution)[1]; - - window->setBordered(true); - window->setResizable(true); - window->setDimensions(w, h); - window->setPosition((*windowPosition)[0], (*windowPosition)[1]); - - } - else - { - const Display* display = deviceManager->getDisplays()->front(); - int displayWidth = std::get<0>(display->getDimensions()); - int displayHeight = std::get<1>(display->getDimensions()); - - w = displayWidth; - h = displayHeight; - - window->setBordered(false); - window->setResizable(false); - window->setPosition(0, 0); - window->setDimensions(w, h); - } - - restringUI(); - - // Disable fullscreen toggles for 500ms - toggleFullscreenDisabled = true; - ScheduledFunctionEvent event; - event.caller = static_cast(this); - event.function = [this]() - { - toggleFullscreenDisabled = false; - }; - eventDispatcher.schedule(event, time + 0.5f); - - // Save settings - saveSettings(); - } -} - -void Game::toggleVSync() -{ - vsync = !(*vsync); - window->setVSync(*vsync); - restringUI(); - - // Save settings - saveSettings(); -} - -void Game::setUpdateRate(double frequency) -{ - stepScheduler.setStepFrequency(frequency); -} - -void Game::disableNonSystemControls() -{ - controls.setCallbacksEnabled(false); - systemControls.setCallbacksEnabled(true); -} - -void Game::setup() -{ - setupDebugging(); - loadSettings(); - setupLocalization(); - setupWindow(); - setupGraphics(); - setupControls(); - setupUI(); - setupGameplay(); - - screenshotQueued = false; - paused = false; - - // Load model resources - try - { - lensModel = resourceManager->load("lens.mdl"); - forcepsModel = resourceManager->load("forceps.mdl"); - brushModel = resourceManager->load("brush.mdl"); - smokeMaterial = resourceManager->load("smoke.mtl"); - } - catch (const std::exception& e) - { - logger->error("Failed to load one or more models: \"" + std::string(e.what()) + "\"\n"); - close(EXIT_FAILURE); - } - - time = 0.0f; - - // Tools - currentTool = nullptr; - - lens = new Lens(lensModel, &animator); - lens->setOrbitCam(orbitCam); - worldScene->addObject(lens->getModelInstance()); - worldScene->addObject(lens->getSpotlight()); - lens->setSunDirection(-sunlightCamera.getForward()); - - // Forceps - forceps = new Forceps(forcepsModel, &animator); - forceps->setOrbitCam(orbitCam); - worldScene->addObject(forceps->getModelInstance()); - - // Brush - brush = new Brush(brushModel, &animator); - brush->setOrbitCam(orbitCam); - worldScene->addObject(brush->getModelInstance()); - - // Initialize component manager - componentManager = new ComponentManager(); - - // Initialize entity manager - entityManager = new EntityManager(componentManager); - - // Initialize systems - soundSystem = new SoundSystem(componentManager); - collisionSystem = new CollisionSystem(componentManager); - constraintSystem = new ConstraintSystem(componentManager); - renderSystem = new RenderSystem(componentManager, worldScene); - toolSystem = new ToolSystem(componentManager); - toolSystem->setPickingCamera(&camera); - toolSystem->setPickingViewport(Vector4(0, 0, w, h)); - eventDispatcher.subscribe(toolSystem); - behaviorSystem = new BehaviorSystem(componentManager); - steeringSystem = new SteeringSystem(componentManager); - locomotionSystem = new LocomotionSystem(componentManager); - terrainSystem = new TerrainSystem(componentManager); - terrainSystem->setPatchSize(500.0f); - particleSystem = new ParticleSystem(componentManager); - particleSystem->resize(1000); - particleSystem->setMaterial(smokeMaterial); - particleSystem->setDirection(Vector3(0, 1, 0)); - lens->setParticleSystem(particleSystem); - particleSystem->getBillboardBatch()->setAlignment(&camera, BillboardAlignmentMode::SPHERICAL); - worldScene->addObject(particleSystem->getBillboardBatch()); - - // Initialize system manager - systemManager = new SystemManager(); - systemManager->addSystem(soundSystem); - systemManager->addSystem(behaviorSystem); - systemManager->addSystem(steeringSystem); - systemManager->addSystem(locomotionSystem); - systemManager->addSystem(collisionSystem); - systemManager->addSystem(toolSystem); - systemManager->addSystem(terrainSystem); - systemManager->addSystem(particleSystem); - systemManager->addSystem(constraintSystem); - systemManager->addSystem(renderSystem); - - // Create focus entity - focusEntity = createInstance(); - componentManager->addComponent(focusEntity, new TransformComponent()); - setTranslation(focusEntity, {0, 0, 0}); - - // Create camera entity - cameraEntity = createInstance(); - CameraComponent* cameraComponent = new CameraComponent(); - cameraComponent->camera = &camera; - TransformComponent* transformComponent = new TransformComponent(); - OrbitConstraintComponent* orbitConstraintComponent = new OrbitConstraintComponent(); - orbitConstraintComponent->target = 0; - orbitConstraintComponent->elevation = 0.0f; - orbitConstraintComponent->azimuth = 0.0f; - orbitConstraintComponent->distance = 0.0f; - componentManager->addComponent(cameraEntity, cameraComponent); - componentManager->addComponent(cameraEntity, transformComponent); - componentManager->addComponent(cameraEntity, orbitConstraintComponent); - - // Load navmesh - TriangleMesh* navmesh = resourceManager->load("sidewalk.mesh"); - - int highResolutionDiameter = 3; - int mediumResolutionDiameter = highResolutionDiameter + 2; - int lowResolutionDiameter = 20; - - float lowResolutionRadius = static_cast(lowResolutionDiameter) / 2.0f; - float mediumResolutionRadius = static_cast(mediumResolutionDiameter) / 2.0f; - float highResolutionRadius = static_cast(highResolutionDiameter) / 2.0f; - - for (int i = 0; i < lowResolutionDiameter; ++i) - { - for (int j = 0; j < lowResolutionDiameter; ++j) - { - EntityID patch; - - int x = i - lowResolutionDiameter / 2; - int z = j - lowResolutionDiameter / 2; - - - if (std::abs(x) < highResolutionRadius && std::abs(z) < highResolutionRadius) - { - patch = createInstanceOf("terrain-patch-high-resolution"); - } - else if (std::abs(x) < mediumResolutionRadius && std::abs(z) < mediumResolutionRadius) - { - patch = createInstanceOf("terrain-patch-medium-resolution"); - } - else - { - patch = createInstanceOf("terrain-patch-low-resolution"); - } - - setTerrainPatchPosition(patch, {x, z}); - } - } - - // Setup state machine states - languageSelectState = - { - std::bind(&Game::enterLanguageSelectState, this), - std::bind(&Game::exitLanguageSelectState, this) - }; - splashState = - { - std::bind(&Game::enterSplashState, this), - std::bind(&Game::exitSplashState, this) - }; - loadingState = - { - std::bind(&Game::enterLoadingState, this), - std::bind(&Game::exitLoadingState, this) - }; - titleState = - { - std::bind(&Game::enterTitleState, this), - std::bind(&Game::exitTitleState, this) - }; - playState = - { - std::bind(&Game::enterPlayState, this), - std::bind(&Game::exitPlayState, this) - }; - - // Initialize state machine - if (firstRun) - { - StateMachine::changeState(&languageSelectState); - } - else - { - #if defined(DEBUG) - StateMachine::changeState(&titleState); - #else - StateMachine::changeState(&splashState); - #endif - } -} - -void Game::update(float t, float dt) -{ - this->time = t; - - // Update systems - systemManager->update(t, dt); - - // Update animations - animator.animate(dt); - - if (fpsLabel->isVisible()) - { - std::stringstream stream; - stream.precision(2); - stream << std::fixed << (performanceSampler.getMeanFrameDuration() * 1000.0f); - fpsLabel->setText(stream.str()); - } - - uiRootElement->update(); -} - -void Game::input() -{ - controls.update(); -} - -void Game::render() -{ - // Perform sub-frame interpolation on UI elements - uiRootElement->interpolate(stepScheduler.getScheduledSubsteps()); - - // Update and batch UI elements - uiBatcher->batch(uiBatch, uiRootElement); - - // Perform sub-frame interpolation particles - particleSystem->getBillboardBatch()->interpolate(stepScheduler.getScheduledSubsteps()); - particleSystem->getBillboardBatch()->batch(); - - // Render scene - renderer.render(*worldScene); - renderer.render(*uiScene); - - if (screenshotQueued) - { - screenshot(); - screenshotQueued = false; - } - - // Swap window framebuffers - window->swapBuffers(); -} - -void Game::exit() -{} - -void Game::handleEvent(const WindowResizedEvent& event) -{ - w = event.width; - h = event.height; - - if (*fullscreen) - { - logger->log("Resized fullscreen window to " + std::to_string(w) + "x" + std::to_string(h)); - fullscreenResolution = {event.width, event.height}; - } - else - { - logger->log("Resized window to " + std::to_string(w) + "x" + std::to_string(h)); - windowResolution = {event.width, event.height}; - } - - // Save resolution settings - saveSettings(); - - defaultRenderTarget.width = event.width; - defaultRenderTarget.height = event.height; - glViewport(0, 0, event.width, event.height); - - camera.setPerspective(glm::radians(40.0f), static_cast(w) / static_cast(h), 0.1, 100.0f); - - - toolSystem->setPickingViewport(Vector4(0, 0, w, h)); - - resizeUI(event.width, event.height); - - skipSplash(); -} - -void Game::handleEvent(const WindowMovedEvent& event) -{ - if (!(*fullscreen)) - { - windowPosition = {event.x, event.y}; - saveSettings(); - } -} - -void Game::handleEvent(const GamepadConnectedEvent& event) -{ - // Unmap all controls - inputRouter->reset(); - - // Reload control profile - loadControlProfile(*controlProfileName); -} - -void Game::handleEvent(const GamepadDisconnectedEvent& event) -{} - -void Game::handleEvent(const ScheduledFunctionEvent& event) -{ - if (event.caller == static_cast(this)) - { - event.function(); - } -} - -void Game::setupDebugging() -{ - // Setup logging - { - logger = new Logger(); - - // Style log format - std::string logPrefix = std::string(); - std::string logPostfix = "\n"; - std::string warningPrefix = "Warning: "; - std::string warningPostfix = std::string(); - std::string errorPrefix = "Error: "; - std::string errorPostfix = std::string(); - std::string successPrefix = std::string(); - std::string successPostfix = std::string(); - - // Enable colored messages on debug builds - #if defined(DEBUG) - warningPrefix = std::string(ANSI_CODE_BOLD) + std::string(ANSI_CODE_YELLOW) + std::string("Warning: ") + std::string(ANSI_CODE_RESET) + std::string(ANSI_CODE_YELLOW); - warningPostfix.append(ANSI_CODE_RESET); - errorPrefix = std::string(ANSI_CODE_BOLD) + std::string(ANSI_CODE_RED) + std::string("Error: ") + std::string(ANSI_CODE_RESET) + std::string(ANSI_CODE_RED); - errorPostfix.append(ANSI_CODE_RESET); - successPrefix.insert(0, ANSI_CODE_GREEN); - successPostfix.append(ANSI_CODE_RESET); - #endif - - logger->setLogPrefix(logPrefix); - logger->setLogPostfix(logPostfix); - logger->setWarningPrefix(warningPrefix); - logger->setWarningPostfix(warningPostfix); - logger->setErrorPrefix(errorPrefix); - logger->setErrorPostfix(errorPostfix); - logger->setSuccessPrefix(successPrefix); - logger->setSuccessPostfix(successPostfix); - - // Redirect logger output to log file on release builds - #if !defined(DEBUG) - std::string logFilename = configPath + "log.txt"; - logFileStream.open(logFilename.c_str()); - logger->redirect(&logFileStream); - #endif - } - - // Create CLI - cli = new CommandInterpreter(); - - // Register CLI commands - std::function setCommand = std::bind(&CommandInterpreter::set, cli, std::placeholders::_1, std::placeholders::_2); - std::function unsetCommand = std::bind(&CommandInterpreter::unset, cli, std::placeholders::_1); - std::function exitCommand = std::bind(std::exit, EXIT_SUCCESS); - std::function setScaleCommand = [this](int id, float x, float y, float z) { - setScale(id, {x, y, z}); - }; - std::function setTranslationCommand = [this](int id, float x, float y, float z) { - setTranslation(id, {x, y, z}); - }; - std::function createInstanceCommand = std::bind(&Game::createInstance, this); - std::function createNamedInstanceCommand = std::bind(&Game::createNamedInstance, this, std::placeholders::_1); - std::function createInstanceOfCommand = std::bind(&Game::createInstanceOf, this, std::placeholders::_1); - std::function createNamedInstanceOfCommand = std::bind(&Game::createNamedInstanceOf, this, std::placeholders::_1, std::placeholders::_2); - - std::function toggleWireframeCommand = [this](float width){ lightingPass->setWireframeLineWidth(width); }; - std::function helpCommand = [this]() - { - auto& helpStrings = cli->help(); - for (auto it = helpStrings.begin(); it != helpStrings.end(); ++it) - { - if (it->second.empty()) - { - continue; - } - - std::cout << it->second << std::endl; - } - }; - std::function variablesCommand = [this]() - { - auto& variables = cli->variables(); - for (auto it = variables.begin(); it != variables.end(); ++it) - { - std::cout << it->first << "=\"" << it->second << "\"" << std::endl; - } - }; - - std::string exitHelp = "exit"; - std::string setHelp = "set "; - std::string unsetHelp = "unset "; - std::string createInstanceHelp = "createinstance