Compare commits
22 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 43bbc9e21d | |||
| 3241eafea0 | |||
| d4711ddd26 | |||
| 5a5e907d88 | |||
| 3437441feb | |||
| 701ac3e151 | |||
| 6ad1272d7d | |||
| cb79147e50 | |||
| 93b0c0ea0d | |||
| d3ad840169 | |||
| dccd19ac80 | |||
| 1b6f5cff5b | |||
| 268e6d6ebf | |||
| 294d75ff59 | |||
| 78540b8ac6 | |||
| 6691d670e2 | |||
| c6a21b24bb | |||
| c4b6c187db | |||
| b10f6884cd | |||
| 6e7bb8397f | |||
| 02403b142a | |||
| ff8bcf3480 |
156 changed files with 15190 additions and 8075 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -32,6 +32,7 @@ bld/
|
|||
[Oo]ut/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
[Bb]uild/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
|
|
@ -364,3 +365,5 @@ FodyWeavers.xsd
|
|||
/Resources/weapons/.bubblegun.xml.swp
|
||||
*.swp
|
||||
/Resources/scenes/.debugScene.xml.swp
|
||||
compile_commands.json
|
||||
compile_flags.txt
|
||||
|
|
|
|||
|
|
@ -12,10 +12,23 @@ endif()
|
|||
include(FetchContent)
|
||||
|
||||
SET(CMAKE_EXPORT_COMPILE_COMMANDS 1)
|
||||
#SET(SOL_LUAJIT 1)
|
||||
|
||||
SET(TRACY_ENABLE 1)
|
||||
SET(TRACY_ON_DEMAND 1)
|
||||
SET(TRACY_ONLY_LOCALHOST 1)
|
||||
#SET(TRACY_ENABLE 1)
|
||||
#SET(TRACY_ON_DEMAND 1)
|
||||
#SET(TRACY_ONLY_LOCALHOST 1)
|
||||
|
||||
SET(FT_DISABLE_BROTLI 1)
|
||||
SET(FT_DISABLE_BZIP2 1)
|
||||
SET(FT_DISABLE_HARFBUZZ 1)
|
||||
SET(FT_DISABLE_PNG 1)
|
||||
SET(FT_DISABLE_ZLIB 1)
|
||||
|
||||
option(GLM_ENABLE_FAST_MATH OFF)
|
||||
|
||||
# -fsanitize=address I will forget, renable this flag no more leaks allowed!
|
||||
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -ffloat-store -fexcess-precision=standard -ffp-contract=off")
|
||||
|
||||
FetchContent_Declare(
|
||||
tracy
|
||||
|
|
@ -25,8 +38,6 @@ FetchContent_Declare(
|
|||
GIT_PROGRESS TRUE
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(tracy)
|
||||
|
||||
project ("YuppleMayham")
|
||||
|
||||
# Include sub-projects.
|
||||
|
|
|
|||
66
README.md
Normal file
66
README.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Personal Game Engine
|
||||
|
||||
A modular, event driven 2D game engine, built from scratch to learn engine architecture and low-level graphics programming.
|
||||
|
||||
Supports sprite-based animation, Lua scripting, XML-driven definitions and Tiled map creation.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- Event-based animation and sound system
|
||||
- Animation and tile-sets supported by texture atlases
|
||||
- Map creation and collision map support in Tiled map editor
|
||||
- Component-based entity structure
|
||||
- Basic audio engine integration via OpenAL (XML definitions)
|
||||
- Basic physics system
|
||||
- Lua scripted enemy and weapon behavior
|
||||
|
||||
---
|
||||
|
||||
## Current Status
|
||||
|
||||
**Work in Progress** - This project is still under active development, I am currently cleaning up my resource management system for better ease of use for future users of this engine.
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
- SDL2, SDL2_image
|
||||
- OpenGL 3.3
|
||||
- sol2 (Lua binding)
|
||||
- TinyXML2
|
||||
- OpenAL
|
||||
- Freetype
|
||||
- GLM
|
||||
- pkg-config
|
||||
- CMake
|
||||
|
||||
## Build Instructions
|
||||
|
||||
1. Clone the repo (git.wienermeister.net/snakert12345/yupplemayham.git)
|
||||
2. Install list of dependencies. Ensure they're available via pkg-config
|
||||
3. Build with CMake
|
||||
|
||||
```bash
|
||||
git clone https://git.wienermeister.net/snakert12345/yupplemayham.git
|
||||
cd yupplemayham
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Screenshot
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Roadmap
|
||||
|
||||
- Save/Load system
|
||||
- Modular and scalable menu system
|
||||
- In-game dialog and conversation systems
|
||||
- Expanded combat system
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
<!--
|
||||
<animations>
|
||||
<animation name="bubble_idle_anim" type="idle">
|
||||
<FPS>2</FPS>
|
||||
<sprite path="sprites/bubbleoAtlas64.png" frameSize="64.0"/>
|
||||
</animation>
|
||||
</animations>
|
||||
</animations>
|
||||
-->
|
||||
|
|
|
|||
22
Resources/animations/character_anims.xml
Normal file
22
Resources/animations/character_anims.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
<!-- Player Animations -->
|
||||
<animation id="character/player/idle" directional="true" FPS="5" frameSize="64.0" atlas="sprites/player3AtlasIdle64.png"/>
|
||||
<animation id="character/player/move" directional="true" FPS="7" frameSize="64.0" atlas="sprites/player3AtlasMove64.png"/>
|
||||
|
||||
<!-- Fallback animations -->
|
||||
<animation id="character/tmp/move" directional="false" FPS="7" frameSize="128.0" atlas="sprites/player2Atlas.png"/>
|
||||
|
||||
|
||||
<!--
|
||||
<animations directional="true">
|
||||
<animation name="player_move_anim" type="move">
|
||||
<FPS>7</FPS>
|
||||
<sprite path="sprites/player3AtlasMove64.png" frameSize="64.0"/>
|
||||
</animation>
|
||||
|
||||
<animation name="player_idle_anim" type="idle">
|
||||
<FPS>5</FPS>
|
||||
<sprite path="sprites/player3AtlasIdle64.png" frameSize="64.0"/>
|
||||
</animation>
|
||||
</animations>
|
||||
-->
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
<!--
|
||||
<animations>
|
||||
<animation name="machine_gun_idle_anim" type="idle">
|
||||
<FPS>4</FPS>
|
||||
|
|
@ -8,3 +9,4 @@
|
|||
<sprite path="sprites/machineGunAtlasReload256.png" frameSize="256.0"/>
|
||||
</animation>
|
||||
</animations>
|
||||
-->
|
||||
|
|
|
|||
2
Resources/animations/object_anims.xml
Normal file
2
Resources/animations/object_anims.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<!-- Bubble -->
|
||||
<animation id="obj/bubble/idle" FPS="2" frameSize="64.0" atlas="sprites/bubbleoAtlas64.png"/>
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
<!--
|
||||
<animations>
|
||||
<animation name="pistol_idle_anim" type="idle">
|
||||
<FPS>4</FPS>
|
||||
|
|
@ -8,3 +9,4 @@
|
|||
<sprite path="sprites/pistolAtlasReload256.png" frameSize="256.0"/>
|
||||
</animation>
|
||||
</animations>
|
||||
-->
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
<animations directional="true">
|
||||
<animation name="player_move_anim" type="move">
|
||||
<FPS>7</FPS>
|
||||
<sprite path="sprites/player3AtlasMove64.png" frameSize="64.0"/>
|
||||
</animation>
|
||||
|
||||
<animation name="player_idle_anim" type="idle">
|
||||
<FPS>5</FPS>
|
||||
<sprite path="sprites/player3AtlasIdle64.png" frameSize="64.0"/>
|
||||
</animation>
|
||||
</animations>
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
<!--
|
||||
<animations>
|
||||
<animation name="shot_gun_idle_anim" type="idle">
|
||||
<FPS>4</FPS>
|
||||
|
|
@ -11,4 +12,5 @@
|
|||
<FPS>8</FPS>
|
||||
<sprite path="sprites/shotGunReload128.png" frameSize="128.0"/>
|
||||
</animation>
|
||||
</animations>
|
||||
</animations>
|
||||
-->
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
<!--
|
||||
<animations>
|
||||
<animation name="tmp_enemy_move_anim" type="move">
|
||||
<FPS>7</FPS>
|
||||
<sprite path="sprites/player2Atlas.png" frameSize="128.0"/>
|
||||
</animation>
|
||||
</animations>
|
||||
-->
|
||||
|
|
|
|||
13
Resources/animations/weapon_anims.xml
Normal file
13
Resources/animations/weapon_anims.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Machine Gun -->
|
||||
<animation id="gun/machine/idle" FPS="4" frameSize="256.0" atlas="sprites/machineGunAtlas256.png"/>
|
||||
<animation id="gun/machine/reload" FPS="6" frameSize="256.0" atlas="sprites/machineGunAtlasReload256.png"/>
|
||||
|
||||
<!-- Pistol -->
|
||||
<animation id="gun/pistol/idle" FPS="4" frameSize="256.0" atlas="sprites/pistolAtlas256.png"/>
|
||||
<animation id="gun/pistol/reload" FPS="8" frameSize="256.0" atlas="sprites/pistolAtlasReload256.png"/>
|
||||
|
||||
<!-- Shotgun -->
|
||||
<animation id="gun/shotgun/idle" FPS="4" frameSize="128.0" atlas="sprites/shotGunIdle128.png"/>
|
||||
<animation id="gun/shotgun/fire" FPS="8" frameSize="128.0" atlas="sprites/shotGunFire128.png"/>
|
||||
<animation id="gun/shotgun/reload" FPS="8" frameSize="128.0" atlas="sprites/shotGunReload128.png"/>
|
||||
|
||||
3
Resources/monsters/shooty_peeps.xml
Normal file
3
Resources/monsters/shooty_peeps.xml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
<monster id="monster/shooty/bighead" anim="character/tmp" weapon="gun/pistol" aggressive="true" behaviour="script/behaviour/grunt" hp="10.0"/>
|
||||
<monster id="monster/shooty/clone" anim="character/player" weapon="gun/shotgun" aggressive="true" behaviour="script/behaviour/grunt" hp="15.0"/>
|
||||
BIN
Resources/music/bright.ogg
Normal file
BIN
Resources/music/bright.ogg
Normal file
Binary file not shown.
BIN
Resources/music/main_song.ogg
Normal file
BIN
Resources/music/main_song.ogg
Normal file
Binary file not shown.
BIN
Resources/music/short_song.ogg
Normal file
BIN
Resources/music/short_song.ogg
Normal file
Binary file not shown.
|
|
@ -3,20 +3,17 @@
|
|||
<scene type="shooter" id="000" bg="backgrounds/blue_sky.png">
|
||||
<map name="newmap"/>
|
||||
<entities>
|
||||
<player x="7" y="5" weapon="shotGun">
|
||||
<animation name="player_anim"/>
|
||||
<player x="7" y="5" weapon="gun/shotgun">
|
||||
<animation id="character/player"/>
|
||||
</player>
|
||||
<entity x="10" y="3" weapon="pistolGun">
|
||||
<animation name="player_anim"/>
|
||||
<script file="scripts/ai/grunt_behaviour.lua"/>
|
||||
<entity x="10" y="3" weapon="gun/pistol">
|
||||
<animation id="character/player"/>
|
||||
<script id="script/behaviour/grunt"/>
|
||||
</entity>
|
||||
<entity x="6" y="3" weapon="pistolGun">
|
||||
<animation name="tmp_enemy_anim"/>
|
||||
<script file="scripts/ai/grunt_behaviour.lua"/>
|
||||
</entity>
|
||||
<entity x="5" y="3" weapon="pistolGun">
|
||||
<animation name="tmp_enemy_anim"/>
|
||||
<script file="scripts/ai/grunt_behaviour.lua"/>
|
||||
<entity x="6" y="3" weapon="gun/pistol">
|
||||
<animation id="character/tmp"/>
|
||||
<script id="script/behaviour/grunt"/>
|
||||
</entity>
|
||||
<entity x="5" y="3" weapon="gun/pistol" monster_id="monster/shooty/bighead"/>
|
||||
</entities>
|
||||
</scene>
|
||||
|
|
|
|||
|
|
@ -1,42 +1,30 @@
|
|||
-- global vars
|
||||
patrolDestination = { x=500.0, y=500.0, z=0.0 }
|
||||
moveLeft = true
|
||||
MoveLeft = true
|
||||
-- helper functions
|
||||
function watchPosition(actor, pos)
|
||||
local function watchPosition(actor, pos)
|
||||
local y = pos.y - actor.position.y
|
||||
local x = pos.x - actor.position.x
|
||||
local rotation = math.atan(y, x)
|
||||
local rotation = math.atan2(y, x)
|
||||
actor.rotation = math.deg(rotation)
|
||||
end
|
||||
|
||||
function distance(a, b)
|
||||
local function distance(a, b)
|
||||
local dx = a.x - b.x
|
||||
local dy = a.y - b.y
|
||||
return math.sqrt(dx * dx + dy * dy)
|
||||
end
|
||||
|
||||
function moveTo(actor, pos)
|
||||
local a = actor
|
||||
local p = pos
|
||||
watchPosition(a, p)
|
||||
if distance(a.position, p) < 50 then
|
||||
return true
|
||||
end
|
||||
a:moveForward()
|
||||
return false
|
||||
end
|
||||
|
||||
-- Behaviour Functions called on AI
|
||||
|
||||
-- These functions are ai behaviour functions called in the game
|
||||
-- The AI will be spawned in idle mode, so if you want to put the bot into patrol mode
|
||||
-- The AI will be spawned in idle mode, so if you want to put the bot into patrol mode
|
||||
-- It's on you to do that in this function.
|
||||
function idle(actor, target)
|
||||
local a = actor
|
||||
local t = target
|
||||
if t ~= nil then
|
||||
-- print("target is at " .. target.position.x)
|
||||
-- watchPosition(actor, target.position)
|
||||
-- print("target is at " .. target.position.x)
|
||||
-- watchPosition(actor, target.position)
|
||||
ai.state = AIState.Patrol
|
||||
a.rotation = 180
|
||||
end
|
||||
|
|
@ -48,24 +36,12 @@ end
|
|||
function patrol(actor, target)
|
||||
local a = actor
|
||||
local t = target
|
||||
if t ~= nil then
|
||||
-- print("target is at " .. target.position.x)
|
||||
end
|
||||
--if moveTo(actor, patrolDestination) == true then
|
||||
-- patrolDestination = { x=math.random(400.0, 750.0), y=math.random(400.0, 750.0), z=0.0 }
|
||||
--end
|
||||
-- performRaycast returns if true if the raycast hits the target position it also sets the getter function
|
||||
-- distFromWall, at bot creation distFromWall is and infinite float value.
|
||||
-- This performRaycast function is highly discourage due to slow down
|
||||
--if raycaster:performRaycast(actor.position, actor.rotation, target.position) == true then
|
||||
--ai.state = AIState.Alert
|
||||
--end
|
||||
if raycaster:bresenhamRaycast(a.position, a.rotation, t.position) == true then
|
||||
--target hit!
|
||||
ai.state = AIState.Alert
|
||||
end
|
||||
if raycaster:distFromWall() < 3 then
|
||||
upOrDown = math.random(2)
|
||||
local upOrDown = math.random(2)
|
||||
if moveLeft == true then
|
||||
if upOrDown == 1 then
|
||||
a.rotation = 180
|
||||
|
|
@ -73,7 +49,7 @@ function patrol(actor, target)
|
|||
a.rotation = 270
|
||||
end
|
||||
moveLeft = false
|
||||
else
|
||||
else
|
||||
if upOrDown == 1 then
|
||||
a.rotation = 0
|
||||
else
|
||||
|
|
@ -90,10 +66,8 @@ function alert(actor, target)
|
|||
local a = actor
|
||||
local t = target
|
||||
if target ~= nil then
|
||||
-- print("target is at " .. target.position.x)
|
||||
watchPosition(a, t.position)
|
||||
end
|
||||
--print("actor is alert at " .. actor.position.x)
|
||||
if distance(a.position, t.position) > 300 then
|
||||
a:moveForward()
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
-- helper functions
|
||||
|
||||
function lookAway(actor, pos)
|
||||
y = actor.position.y - pos.y
|
||||
x = actor.position.x - pos.x
|
||||
rotation = math.atan(y, x)
|
||||
local function lookAway(actor, pos)
|
||||
local y = actor.position.y - pos.y
|
||||
local x = actor.position.x - pos.x
|
||||
local rotation = math.atan(y, x)
|
||||
actor.rotation = math.deg(rotation)
|
||||
end
|
||||
|
||||
function distance(a, b)
|
||||
return math.sqrt((math.abs(a.x - b.x)^2) + (math.abs(a.y - b.y)^2))
|
||||
local function distance(a, b)
|
||||
return math.sqrt((math.abs(a.x - b.x) ^ 2) + (math.abs(a.y - b.y) ^ 2))
|
||||
end
|
||||
|
||||
function idle(actor, target)
|
||||
-- do nothing here for now
|
||||
-- do nothing here for now
|
||||
end
|
||||
|
||||
function patrol(actor, target)
|
||||
|
|
|
|||
4
Resources/scripts/ai_scripts.xml
Normal file
4
Resources/scripts/ai_scripts.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
<!-- Shooter Scripts -->
|
||||
<script id="script/behaviour/grunt" file="scripts/ai/grunt_behaviour.lua"/>
|
||||
<script id="script/behaviour/scared" file="scripts/ai/scared_behaviour.lua"/>
|
||||
3
Resources/scripts/weapon_scripts.xml
Normal file
3
Resources/scripts/weapon_scripts.xml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
<!-- Ranged weapon scripts -->
|
||||
<script id="script/weapon/shotgun" file="scripts/weapons/shotgun_script.lua"/>
|
||||
|
|
@ -17,5 +17,5 @@ end
|
|||
|
||||
-- How the bullet, target or wielder respond when the bullet hits a target.
|
||||
function onHit(target, bullet, normal)
|
||||
target.physics.rigidBody:applyForce(vec3.new(normal.x, normal.y, 0.0), 5000.0);
|
||||
target.physics.rigidBody:applyForce(normal, 5000.0);
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec2 aTexCoord;
|
||||
layout (std140) uniform Matrices
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
};
|
||||
|
||||
out vec2 texCoord;
|
||||
|
||||
uniform mat4 MVP;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 projection;
|
||||
uniform mat4 view;
|
||||
|
||||
void main()
|
||||
{
|
||||
texCoord = aTexCoord;
|
||||
gl_Position = projection * view * model * vec4(aPos, 1.0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec2 aTexCoord;
|
||||
layout (std140) uniform Matrices
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
};
|
||||
|
||||
out vec2 texCoord;
|
||||
|
||||
uniform mat4 projection;
|
||||
uniform mat4 view;
|
||||
|
||||
void main()
|
||||
{
|
||||
texCoord = aTexCoord;
|
||||
gl_Position = projection * vec4(aPos.xyz, 1.0);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec2 aTexCoord;
|
||||
layout (std140) uniform Matrices
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
};
|
||||
|
||||
out vec2 texCoord;
|
||||
|
||||
uniform mat4 MVP;
|
||||
|
||||
uniform mat4 projection;
|
||||
uniform mat4 view;
|
||||
uniform mat4 model;
|
||||
uniform bool flip;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec2 aTexPos;
|
||||
layout (std140) uniform Matrices
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
};
|
||||
|
||||
out vec3 pos;
|
||||
out vec2 texPos;
|
||||
|
||||
uniform mat4 MVP;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 projection;
|
||||
uniform mat4 view;
|
||||
|
||||
void main()
|
||||
{
|
||||
pos = aPos;
|
||||
texPos = aTexPos;
|
||||
gl_Position = projection * view * model * vec4(aPos, 1.0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,13 @@ layout (location = 4) in int aTilesPerRow;
|
|||
layout (location = 5) in int aStartIndex;
|
||||
layout (location = 6) in vec2 aOriginalSize;
|
||||
layout (location = 7) in mat4 aModel;
|
||||
layout (std140) uniform Matrices
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
};
|
||||
|
||||
uniform vec2 uCanvasSize;
|
||||
uniform mat4 projection;
|
||||
uniform mat4 view;
|
||||
|
||||
out vec2 texCoord;
|
||||
flat out int textureIndex;
|
||||
|
|
@ -43,4 +46,4 @@ void main()
|
|||
texCoord.x = 0.0;
|
||||
texCoord.y = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
Resources/sounds/gun/big_boom.ogg
Normal file
BIN
Resources/sounds/gun/big_boom.ogg
Normal file
Binary file not shown.
BIN
Resources/sounds/gun/mach_pew.ogg
Normal file
BIN
Resources/sounds/gun/mach_pew.ogg
Normal file
Binary file not shown.
BIN
Resources/sounds/gun/reload.ogg
Normal file
BIN
Resources/sounds/gun/reload.ogg
Normal file
Binary file not shown.
BIN
Resources/sounds/gun/small_pew.ogg
Normal file
BIN
Resources/sounds/gun/small_pew.ogg
Normal file
Binary file not shown.
14
Resources/sounds/gun_effects.xml
Normal file
14
Resources/sounds/gun_effects.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!-- Shotgun Sounds -->
|
||||
<sound id="gun/shotgun/shoot" spatial="true" path="sounds/gun/big_boom.ogg"/>
|
||||
|
||||
<!-- Pistol Sounds -->
|
||||
<sound id="gun/pistol/shoot" spatial="true" path="sounds/gun/small_pew.ogg"/>
|
||||
|
||||
<!-- Machine Gun Sounds -->
|
||||
<sound id="gun/machine/shoot" spatial="true" path="sounds/gun/mach_pew.ogg"/>
|
||||
|
||||
<!-- Generic Sounds -->
|
||||
<sound id="gun/generic/shoot" spatial="true" path="sounds/gun/small_pew.ogg"/>
|
||||
<sound id="gun/generic/reload" spatial="true" path="sounds/gun/reload.ogg"/>
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<weapons>
|
||||
<weapon name="bubbleGun" fireSpeed="100.0" maxAmmo="40000" clipSize="1000">
|
||||
<bullet anim="bubble_anim">
|
||||
<weapon id="gun/machine" fireSpeed="100.0" maxAmmo="40000" clipSize="1000">
|
||||
<bullet anim="obj/bubble">
|
||||
<spread>60</spread>
|
||||
<speed>20.0</speed>
|
||||
<drop>250.0</drop>
|
||||
|
|
@ -11,9 +11,9 @@
|
|||
</bullet>
|
||||
</weapon>
|
||||
|
||||
<weapon name="shotGun" fireSpeed="1750.0" maxAmmo="64" clipSize="4">
|
||||
<script file="scripts/weapons/shotgun_script.lua"/>
|
||||
<animation name="shot_gun_anim">
|
||||
<weapon id="gun/shotgun" fireSpeed="1750.0" maxAmmo="64" clipSize="4">
|
||||
<script id="script/weapon/shotgun"/>
|
||||
<animation>
|
||||
<size x="55.0" y="55.0"/>
|
||||
<offset x="-30.0" y="0.0"/>
|
||||
</animation>
|
||||
|
|
@ -26,8 +26,8 @@
|
|||
</bullet>
|
||||
</weapon>
|
||||
|
||||
<weapon name="machineGun" fireSpeed="50.0" maxAmmo="512" clipSize="64">
|
||||
<animation name="machine_gun_anim">
|
||||
<weapon id="gun/machine" fireSpeed="50.0" maxAmmo="512" clipSize="64">
|
||||
<animation>
|
||||
<size x="55.0" y="55.0"/>
|
||||
<offset x="-30.0" y="0.0"/>
|
||||
</animation>
|
||||
|
|
@ -40,8 +40,8 @@
|
|||
</bullet>
|
||||
</weapon>
|
||||
|
||||
<weapon name="pistolGun" fireSpeed="750.0" maxAmmo="512" clipSize="21">
|
||||
<animation name="pistol_anim">
|
||||
<weapon id="gun/pistol" fireSpeed="750.0" maxAmmo="512" clipSize="21">
|
||||
<animation>
|
||||
<size x="55.0" y="55.0"/>
|
||||
<offset x="-30.0" y="0.0"/>
|
||||
</animation>
|
||||
|
|
@ -2,32 +2,47 @@
|
|||
# project specific logic here.
|
||||
#
|
||||
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(SDL2_IMAGE REQUIRED)
|
||||
find_package(SDL2 2.30.2 REQUIRED)
|
||||
find_package(SDL2_image 2.8.2 REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LuaJIT REQUIRED IMPORTED_TARGET GLOBAL luajit)
|
||||
find_package(sol2 REQUIRED)
|
||||
find_package(tinyxml2 REQUIRED)
|
||||
find_package(Freetype REQUIRED)
|
||||
find_package(OpenAL REQUIRED)
|
||||
find_package(glm CONFIG REQUIRED)
|
||||
|
||||
include_directories(SYSTEM "${PROJECT_SOURCE_DIR}/YuppleMayham/include")
|
||||
include_directories(SYSTEM "c:/sdks/glad/include")
|
||||
include_directories(SYSTEM "c:/sdks/tinyxml2-10.0.0/debug/include")
|
||||
include_directories(SYSTEM "c:/sdks/glm")
|
||||
include_directories(SYSTEM "C:/sdks/sol2-3.3.0/single/single/include")
|
||||
include_directories(SYSTEM "c:/sdks/lua-5.4.6/include")
|
||||
include_directories(SYSTEM "C:/sdks/freetype-2.13.2/include")
|
||||
link_directories(SYSTEM "C:/sdks/freetype-2.13.2/objs")
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
link_directories(SYSTEM "c:/sdks/tinyxml2-10.0.0/debug/lib")
|
||||
else()
|
||||
link_directories(SYSTEM "c:/sdks/tinyxml2-10.0.0/release/lib")
|
||||
endif()
|
||||
link_directories(SYSTEM "c:/sdks/lua-5.4.6/lib")
|
||||
|
||||
include_directories("${PROJECT_SOURCE_DIR}/YuppleMayham/include")
|
||||
#include_directories(SYSTEM "c:/sdks/glad/include")
|
||||
#include_directories(SYSTEM "c:/sdks/tinyxml2-10.0.0/debug/include")
|
||||
#include_directories(SYSTEM "c:/sdks/glm")
|
||||
#include_directories(SYSTEM "C:/sdks/sol2-3.3.0/single/single/include")
|
||||
#include_directories(SYSTEM "c:/sdks/lua-5.4.6/include")
|
||||
#include_directories(SYSTEM "C:/sdks/freetype-2.13.2/include")
|
||||
#link_directories(SYSTEM "C:/sdks/freetype-2.13.2/objs")
|
||||
#if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
# link_directories(SYSTEM "c:/sdks/tinyxml2-10.0.0/debug/lib")
|
||||
#else()
|
||||
# link_directories(SYSTEM "c:/sdks/tinyxml2-10.0.0/release/lib")
|
||||
#endif()
|
||||
#link_directories(SYSTEM "c:/sdks/lua-5.4.6/lib")
|
||||
|
||||
#include_directories("${PROJECT_SOURCE_DIR}/YuppleMayham/include")
|
||||
|
||||
# Add source to this project's executable.
|
||||
add_executable (YuppleMayham
|
||||
"src/main.cpp"
|
||||
"c:/sdks/glad/src/glad.c"
|
||||
"src/thirdparty/glad.c"
|
||||
"src/sound/engine.cpp"
|
||||
"src/sound/audiostream.cpp"
|
||||
"src/sound/soundeffect.cpp"
|
||||
"src/sound/soundmanager.cpp"
|
||||
"include/thirdparty/stb_vorbis.c"
|
||||
"src/utility/data/font_data.c"
|
||||
"src/utility/ftfont.cpp"
|
||||
"src/graphics/sprite.cpp"
|
||||
"src/graphics/mesh.cpp"
|
||||
"src/graphics/glwindow.cpp"
|
||||
"src/gameplay/entity.cpp"
|
||||
"src/gameplay/gameactor.cpp"
|
||||
"src/graphics/shader.cpp"
|
||||
|
|
@ -39,6 +54,7 @@ add_executable (YuppleMayham
|
|||
"src/gameplay/weapons/bulletmanager.cpp"
|
||||
"src/gameplay/scene.cpp"
|
||||
"src/graphics/texture.cpp"
|
||||
"src/graphics/background.cpp"
|
||||
|
||||
"src/utility/resourcemanager.cpp"
|
||||
"src/utility/xmlloader.cpp"
|
||||
|
|
@ -50,7 +66,6 @@ add_executable (YuppleMayham
|
|||
"src/utility/debugdraw.cpp"
|
||||
"src/utility/script.cpp"
|
||||
"src/gameplay/ai.cpp"
|
||||
"src/utility/ftfont.cpp"
|
||||
"include/graphics/glwindow.h"
|
||||
"include/gameplay/camera.h"
|
||||
"include/utility/mousestate.h"
|
||||
|
|
@ -85,8 +100,11 @@ add_custom_command(TARGET YuppleMayham PRE_BUILD COMMAND ${CMAKE_COMMAND} -E cop
|
|||
|
||||
if (CMAKE_VERSION VERSION_GREATER 3.12)
|
||||
set_property(TARGET YuppleMayham PROPERTY CXX_STANDARD 20)
|
||||
message("this is freetype include dirs: ${FREETYPE_INCLUDE_DIR_ft2build}")
|
||||
endif()
|
||||
|
||||
target_link_libraries(YuppleMayham SDL2::SDL2main SDL2::SDL2 SDL2_image::SDL2_image tinyxml2 lua_static freetype Tracy::TracyClient)
|
||||
target_include_directories(YuppleMayham PRIVATE "${PROJECT_SOURCE_DIR}/YuppleMayham/include" ${LuaJIT_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIR_ft2build})
|
||||
|
||||
target_link_libraries(YuppleMayham SDL2::SDL2main SDL2::SDL2 SDL2_image::SDL2_image openal glm::glm tinyxml2 freetype ${LuaJIT_LINK_LIBRARIES})
|
||||
|
||||
# TODO: Add tests and install targets if needed.
|
||||
|
|
|
|||
|
|
@ -17,25 +17,25 @@ enum class AIState {
|
|||
Patrol
|
||||
};
|
||||
|
||||
class AI : public std::enable_shared_from_this<AI> {
|
||||
class AI {
|
||||
public:
|
||||
AI(const std::shared_ptr<GameActor>& actor, const std::shared_ptr<Raycaster>& raycaster);
|
||||
AI(GameActor* actor, std::unique_ptr<Raycaster> raycaster);
|
||||
void update();
|
||||
|
||||
void setTarget(const std::shared_ptr<GameActor>& target) { this->target = target; }
|
||||
void attachBehaviourScript(const std::shared_ptr<AIScript>& behaviour);
|
||||
void setTarget(GameActor* target) { this->target = target; }
|
||||
void attachBehaviourScript(std::unique_ptr<AIScript> behaviour);
|
||||
|
||||
AIState getState() const { return state; }
|
||||
void setState(AIState state) { this->state = state; }
|
||||
|
||||
~AI() {}
|
||||
~AI();
|
||||
|
||||
private:
|
||||
AIState state;
|
||||
std::shared_ptr<Raycaster> raycaster;
|
||||
std::shared_ptr<AIScript> behaviour;
|
||||
std::shared_ptr<GameActor> actor;
|
||||
std::shared_ptr<GameActor> target;
|
||||
std::unique_ptr<Raycaster> raycaster;
|
||||
std::unique_ptr<AIScript> behaviour;
|
||||
GameActor* actor;
|
||||
GameActor* target;
|
||||
|
||||
// cache our lua function calls
|
||||
sol::function idleFunc;
|
||||
|
|
@ -46,4 +46,4 @@ private:
|
|||
std::chrono::minutes GCTimeout;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,42 +3,43 @@
|
|||
|
||||
#include "gameplay/entity.h"
|
||||
|
||||
class Camera
|
||||
{
|
||||
class Camera {
|
||||
public:
|
||||
Camera(float viewPortW, float viewPortH) :
|
||||
position(glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||
front(glm::vec3(0.0f, 0.0f, -1.0f)),
|
||||
up(glm::vec3(0.0f, 1.0f, 0.0f)),
|
||||
viewPortW(viewPortW),
|
||||
viewPortH(viewPortH)
|
||||
{};
|
||||
Camera(float viewPortW, float viewPortH)
|
||||
: position(glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||
front(glm::vec3(0.0f, 0.0f, -1.0f)), up(glm::vec3(0.0f, 1.0f, 0.0f)),
|
||||
viewPortW(viewPortW), viewPortH(viewPortH) {};
|
||||
|
||||
void setPosition(const glm::vec3& position) { this->position = position; }
|
||||
void setPosition(const glm::vec3 &position) { this->position = position; }
|
||||
|
||||
const Entity* getTarget() const { return target; }
|
||||
const glm::vec3 getCenterPos() const { return glm::vec3(position.x + (viewPortW / 2.f), position.y + (viewPortH / 2.f), 0.0f); }
|
||||
void setTarget(Entity* target) { this->target = target; }
|
||||
void unsetTarget() { target = nullptr; }
|
||||
bool isTargeting() { return (target != nullptr); }
|
||||
const Entity *getTarget() const { return target; }
|
||||
const glm::vec3 getCenterPos() const {
|
||||
return glm::vec3(position.x + (viewPortW / 2.f),
|
||||
position.y + (viewPortH / 2.f), 0.0f);
|
||||
}
|
||||
void setTarget(Entity *target) { this->target = target; }
|
||||
void unsetTarget() { target = nullptr; }
|
||||
bool isTargeting() { return (target != nullptr); }
|
||||
|
||||
void update(double deltaTime);
|
||||
void update(double deltaTime);
|
||||
void setViewportSize(float width, float height);
|
||||
|
||||
const glm::vec3 worldToLocal(const glm::vec3& worldCoordinates);
|
||||
const glm::vec3 worldToLocal(const glm::vec3 &worldCoordinates);
|
||||
|
||||
glm::mat4 getViewMatrix();
|
||||
glm::mat4 getProjectionMatrix();
|
||||
const glm::vec3 getPosition() const { return position; }
|
||||
|
||||
glm::mat4 getViewMatrix();
|
||||
glm::mat4 getProjectionMatrix();
|
||||
const glm::vec3 getPosition() const { return position; }
|
||||
private:
|
||||
Entity* target = nullptr;
|
||||
Entity *target = nullptr;
|
||||
|
||||
glm::vec3 position;
|
||||
glm::vec3 front;
|
||||
glm::vec3 up;
|
||||
glm::vec3 position;
|
||||
glm::vec3 front;
|
||||
glm::vec3 up;
|
||||
|
||||
float viewPortW, viewPortH;
|
||||
float viewPortW, viewPortH;
|
||||
|
||||
float speed = 300.0f;
|
||||
float speed = 300.0f;
|
||||
};
|
||||
|
||||
#endif // _H_CAMERA_H
|
||||
#endif // _H_CAMERA_H
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <SDL_timer.h>
|
||||
|
||||
#include "graphics/renderer.h"
|
||||
#include "util.h"
|
||||
|
||||
class Camera;
|
||||
struct PhysicsComponent;
|
||||
|
|
@ -74,4 +75,4 @@ protected:
|
|||
};
|
||||
|
||||
|
||||
#endif //_H_ENTITY_H
|
||||
#endif //_H_ENTITY_H
|
||||
|
|
|
|||
|
|
@ -1,55 +1,56 @@
|
|||
#ifndef _H_GAME_H
|
||||
#define _H_GAME_H
|
||||
|
||||
#include <memory>
|
||||
#include <SDL_events.h>
|
||||
#include <memory>
|
||||
#include <utility/events.h>
|
||||
|
||||
#include "gameplay/camera.h"
|
||||
|
||||
class InputHandler;
|
||||
class Scene;
|
||||
class Text;
|
||||
class ResourceManager;
|
||||
class Renderer;
|
||||
class AudioEngine;
|
||||
class GLWindow;
|
||||
|
||||
enum {
|
||||
GAME_QUITTING = 0,
|
||||
GAME_RUNNING = 1,
|
||||
GAME_MENU = 2,
|
||||
GAME_PLAYING = 4
|
||||
};
|
||||
enum { GAME_QUITTING = 0, GAME_RUNNING = 1, GAME_MENU = 2, GAME_PLAYING = 4 };
|
||||
|
||||
class Game
|
||||
{
|
||||
class Game {
|
||||
public:
|
||||
Game() {}
|
||||
bool init();
|
||||
Game() {}
|
||||
bool init();
|
||||
|
||||
bool loadDebugScene();
|
||||
|
||||
void handleInput(SDL_Event& e);
|
||||
bool loadDebugScene();
|
||||
|
||||
void update(double deltaTime);
|
||||
void render();
|
||||
void captureInput(SDL_Event &e);
|
||||
void executeInputs();
|
||||
|
||||
const unsigned getGameState() const { return game_state; }
|
||||
void update(double deltaTime);
|
||||
void render();
|
||||
|
||||
const unsigned getWindowWidth() const;
|
||||
const unsigned getWindowHeight() const;
|
||||
const unsigned getGameState() const { return game_state; }
|
||||
|
||||
void quit() { game_state = GAME_QUITTING; }
|
||||
const unsigned getWindowWidth() const;
|
||||
const unsigned getWindowHeight() const;
|
||||
|
||||
void quit();
|
||||
~Game();
|
||||
|
||||
private:
|
||||
unsigned game_state = GAME_QUITTING;
|
||||
unsigned game_state = GAME_QUITTING;
|
||||
|
||||
std::shared_ptr<GLWindow> window;
|
||||
std::shared_ptr<GLWindow> window;
|
||||
|
||||
std::shared_ptr<Scene> currentScene;
|
||||
std::shared_ptr<InputHandler> inputHandler;
|
||||
std::shared_ptr<ResourceManager> resourceManager;
|
||||
std::shared_ptr<Renderer> renderer;
|
||||
std::shared_ptr<Text> textHandler;
|
||||
std::shared_ptr<EventManager> globalEventManager;
|
||||
std::unique_ptr<Camera> camera;
|
||||
std::shared_ptr<Scene> currentScene;
|
||||
std::shared_ptr<InputHandler> inputHandler;
|
||||
std::shared_ptr<ResourceManager> resourceManager;
|
||||
std::shared_ptr<Renderer> renderer;
|
||||
std::shared_ptr<Text> textHandler;
|
||||
std::shared_ptr<AudioEngine> audioEngine;
|
||||
std::shared_ptr<EventManager> globalEventManager;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@
|
|||
#include <unordered_map>
|
||||
#include <SDL_timer.h>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
#include "gameplay/entity.h"
|
||||
#include "gameplay/weapons/weapon.h"
|
||||
#include "utility/mousestate.h"
|
||||
|
||||
#include <utility/component.h>
|
||||
|
||||
class AI;
|
||||
class Weapon;
|
||||
class ISceneContext;
|
||||
|
||||
// TODO: Finish weapon cycling code and add default weapon to every actor
|
||||
// TODO: Add ammo system, then work on some basic UI design
|
||||
|
||||
class GameActor : public Entity
|
||||
|
|
@ -25,11 +25,12 @@ public:
|
|||
~GameActor();
|
||||
|
||||
void addComponent(std::unique_ptr<Component> component);
|
||||
void pickupWeapon(std::shared_ptr<Weapon> weapon);
|
||||
void pickupWeapon(std::unique_ptr<Weapon> weapon);
|
||||
void update(double deltaTime) override;
|
||||
void draw() override;
|
||||
|
||||
const std::shared_ptr<Weapon> getHeldWeapon() const;
|
||||
Weapon* getHeldWeapon() const;
|
||||
std::span<Weapon*> getAllWeapons();
|
||||
|
||||
void setRotation(const float& rotation) override;
|
||||
|
||||
|
|
@ -49,14 +50,12 @@ public:
|
|||
void cycleWeapons(const MouseState&);
|
||||
void followMouse(const MouseState&);
|
||||
private:
|
||||
using component_vector_t = std::vector<std::unique_ptr<Component>>;
|
||||
using weapon_vector_t = std::vector<std::shared_ptr<Weapon>>;
|
||||
|
||||
component_vector_t components;
|
||||
weapon_vector_t weapons;
|
||||
std::vector<std::unique_ptr<Component>> components;
|
||||
std::vector<std::unique_ptr<Weapon>> weapons;
|
||||
std::vector<Weapon*> weaponCache;
|
||||
size_t currentWeaponIndex = 0;
|
||||
|
||||
ISceneContext* sceneContext;
|
||||
};
|
||||
|
||||
#endif //_H_GAMEACTOR_H
|
||||
#endif //_H_GAMEACTOR_H
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef _H_INPUT_H
|
||||
#define _H_INPUT_H
|
||||
|
||||
#include "utility/logger.h"
|
||||
#include <SDL_mouse.h>
|
||||
#include <unordered_map>
|
||||
#include <SDL_keycode.h>
|
||||
#include <SDL_events.h>
|
||||
|
|
@ -45,13 +47,12 @@ protected:
|
|||
mouseButtons[MOUSE_BUTTON_LEFT] = true;
|
||||
if (e.type == SDL_MOUSEBUTTONUP)
|
||||
mouseButtons[MOUSE_BUTTON_LEFT] = false;
|
||||
if (e.type == SDL_MOUSEMOTION)
|
||||
{
|
||||
if (e.type == SDL_MOUSEWHEEL)
|
||||
mouse_state.scroll = e.wheel.preciseY;
|
||||
if (e.type == SDL_MOUSEMOTION) {
|
||||
mouse_state.x = static_cast<float>(e.motion.x);
|
||||
mouse_state.y = static_cast<float>(e.motion.y);
|
||||
}
|
||||
if (e.type == SDL_MOUSEWHEEL)
|
||||
mouse_state.scroll = e.wheel.preciseY;
|
||||
}
|
||||
MouseState mouse_state;
|
||||
std::unordered_map<Uint8, bool> mouseButtons;
|
||||
|
|
@ -61,12 +62,12 @@ class InputHandler : public Keyboard, public Mouse
|
|||
{
|
||||
private:
|
||||
struct CommandWithDelay{
|
||||
Command* cmd;
|
||||
std::unique_ptr<Command> cmd;
|
||||
Uint32 lastExecution;
|
||||
Uint32 delay;
|
||||
};
|
||||
struct MouseCommandWithDelay{
|
||||
MouseCommand* cmd;
|
||||
std::unique_ptr<MouseCommand> cmd;
|
||||
Uint32 lastExecution;
|
||||
Uint32 delay;
|
||||
};
|
||||
|
|
@ -79,26 +80,31 @@ public:
|
|||
this->actor = actor;
|
||||
}
|
||||
|
||||
void bindKeyCommand(SDL_Keycode key, Uint32 delay, Command* command) {
|
||||
keyCommands[key] = { command, 0, delay };
|
||||
void bindKeyCommand(SDL_Keycode key, Uint32 delay, std::unique_ptr<Command> command) {
|
||||
keyCommands[key] = { std::move(command), 0, delay };
|
||||
}
|
||||
void bindMouseCommand(Uint8 mouse, Uint32 delay, MouseCommand* command) {
|
||||
mouseCommands[mouse] = { command, 0, delay };
|
||||
void bindMouseCommand(Uint8 mouse, Uint32 delay, std::unique_ptr<MouseCommand> command) {
|
||||
mouseCommands[mouse] = { std::move(command), 0, delay };
|
||||
}
|
||||
void bindMouseMotion(MouseCommand* command) {
|
||||
mouseMotionCommand = command;
|
||||
void bindMouseMotion(std::unique_ptr<MouseCommand> command) {
|
||||
mouseMotionCommand = std::move(command);
|
||||
}
|
||||
void bindMouseScroll(MouseCommand* command) {
|
||||
mouseScrollCommand = command;
|
||||
void bindMouseScroll(std::unique_ptr<MouseCommand> command) {
|
||||
mouseScrollCommand = std::move(command);
|
||||
}
|
||||
void handleInput();
|
||||
void executeCommands();
|
||||
|
||||
~InputHandler();
|
||||
|
||||
private:
|
||||
GameActor* actor = nullptr;
|
||||
std::unordered_map<SDL_Keycode, CommandWithDelay> keyCommands;
|
||||
std::unordered_map<Uint8, MouseCommandWithDelay> mouseCommands;
|
||||
MouseCommand* mouseMotionCommand = nullptr;
|
||||
MouseCommand* mouseScrollCommand = nullptr;
|
||||
std::vector<Command*> commandQueue;
|
||||
std::vector<MouseCommand*> mouseQueue;
|
||||
std::unique_ptr<MouseCommand> mouseMotionCommand = nullptr;
|
||||
std::unique_ptr<MouseCommand> mouseScrollCommand = nullptr;
|
||||
};
|
||||
|
||||
#endif // _H_INPUT_H
|
||||
#endif // _H_INPUT_H
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ struct TileSetData;
|
|||
class Map : public Drawable
|
||||
{
|
||||
public:
|
||||
Map(std::shared_ptr<MapData> mapData, const unsigned shaderID, std::shared_ptr<ResourceManager> resourceManager);
|
||||
Map(const MapData* mapData, const unsigned shaderID, std::shared_ptr<ResourceManager> resourceManager);
|
||||
|
||||
const std::vector<std::vector<int>> getCollisionMap() const { return collisionMap; }
|
||||
|
||||
|
|
@ -26,10 +26,10 @@ private:
|
|||
void loadMap();
|
||||
void createCollisionMap();
|
||||
|
||||
int getTileSetIndex(int id) const;
|
||||
size_t getTileSetIndex(int id) const;
|
||||
|
||||
std::shared_ptr<MapData> mapData;
|
||||
std::vector<std::shared_ptr<TileSetData>> tileSetData;
|
||||
const MapData* mapData;
|
||||
std::vector<const TileSetData*> tileSetData;
|
||||
|
||||
std::vector<std::shared_ptr<TileTextureInstance>> instanceHandles;
|
||||
std::vector<std::vector<std::vector<int>>> tileIds;
|
||||
|
|
@ -37,4 +37,4 @@ private:
|
|||
std::vector<std::vector<InstanceData>> tileData;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -12,20 +12,20 @@ struct PhysicsComponent {
|
|||
// This ID holds the ID of the gameactor that owns this object, that being themselves or a bullet.
|
||||
unsigned int ID = 0; // If the ID stays 0 then that object has no owner and will be uncollidable
|
||||
struct RigidBody {
|
||||
glm::vec3 position;
|
||||
glm::vec3 velocity;
|
||||
glm::vec3 acceleration;
|
||||
glm::vec2 position;
|
||||
glm::vec2 velocity = glm::vec2(0.f);
|
||||
glm::vec2 acceleration = glm::vec2(0.f);
|
||||
float elasticity = 0.7f;
|
||||
float mass = 1.0f;
|
||||
void applyForce(glm::vec3 dir, float mag) {
|
||||
void applyForce(glm::vec2 dir, float mag) {
|
||||
acceleration += ((dir * mag) / mass);
|
||||
}
|
||||
}rigidBody;
|
||||
|
||||
struct Collider {
|
||||
enum class Shape { Square, Circle } shape = Shape::Circle;
|
||||
glm::vec3 offset = glm::vec3(0.f);
|
||||
glm::vec3 dimensions;
|
||||
glm::vec2 offset = glm::vec2(0.f);
|
||||
glm::vec2 dimensions;
|
||||
}collider;
|
||||
};
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ public:
|
|||
p.ID = ID;
|
||||
p.rigidBody.position = pos;
|
||||
p.rigidBody.mass = mass;
|
||||
p.collider = { PhysicsComponent::Collider::Shape::Circle, glm::vec3(radius, radius, 0.f), glm::vec3(radius) };
|
||||
p.collider = { PhysicsComponent::Collider::Shape::Circle, glm::vec2(0.f, 0.f), glm::vec2(radius) };
|
||||
p.isBullet = true;
|
||||
return p;
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ private:
|
|||
void resolveWorldCollision(const std::shared_ptr<PhysicsComponent>&);
|
||||
void resolvePossibleCollisions();
|
||||
void getPossibleCollisions();
|
||||
int getTileCollider(const glm::vec3& position);
|
||||
int getTileCollider(const glm::vec2& position);
|
||||
std::vector<std::shared_ptr<PhysicsComponent>> objects;
|
||||
std::vector<CollisionPair> objCollisions;
|
||||
std::vector<std::vector<int>> collisionMap;
|
||||
|
|
@ -67,4 +67,4 @@ private:
|
|||
std::shared_ptr<EventManager> eventManager;
|
||||
};
|
||||
|
||||
#endif //_H_PHYSICS_H
|
||||
#endif //_H_PHYSICS_H
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <gameplay/gameactor.h>
|
||||
|
||||
class Background;
|
||||
class Entity;
|
||||
class Camera;
|
||||
class Map;
|
||||
|
|
@ -61,6 +62,9 @@ private:
|
|||
void hookSceneEvents();
|
||||
GameActor* getGameActorByID(const unsigned int ID);
|
||||
|
||||
// Non-owning pointer DO NOT DELETE, resource manager will handle that!
|
||||
Background *background;
|
||||
|
||||
SceneType type;
|
||||
std::shared_ptr<Map> map;
|
||||
//std::shared_ptr<TileSet> tileSet;
|
||||
|
|
@ -74,7 +78,7 @@ private:
|
|||
std::shared_ptr<ResourceManager> resourceManager;
|
||||
std::weak_ptr<EventManager> globalEventManager;
|
||||
std::shared_ptr<EventManager> eventManager;
|
||||
std::shared_ptr<SceneData> sceneData;
|
||||
const SceneData* sceneData;
|
||||
};
|
||||
|
||||
#endif //_H_SCENE_H
|
||||
#endif //_H_SCENE_H
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@ class WeaponScript;
|
|||
struct PhysicsComponent;
|
||||
struct WeaponData;
|
||||
|
||||
class Weapon : public Entity, public std::enable_shared_from_this<Weapon>
|
||||
class Weapon : public Entity
|
||||
{
|
||||
public:
|
||||
Weapon(std::shared_ptr<WeaponData> data, const unsigned weaponShaderID, const unsigned bulletShaderID, ResourceManager* resourceManager);
|
||||
Weapon(const WeaponData* data, const unsigned weaponShaderID, const unsigned bulletShaderID, ResourceManager* resourceManager);
|
||||
|
||||
void setWielder(GameActor* wielder) { this->wielder = wielder; }
|
||||
void toggleInfiniteAmmo() { infiniteAmmo = !infiniteAmmo; }
|
||||
|
|
@ -35,13 +35,15 @@ public:
|
|||
void putaway() { wielded = false; }
|
||||
|
||||
void addComponent(std::unique_ptr<Component> component);
|
||||
void attachScript(const std::shared_ptr<WeaponScript>& script);
|
||||
void attachScript(std::unique_ptr<WeaponScript> script);
|
||||
void hookEventManager(std::weak_ptr<EventManager> eventManager);
|
||||
bool shoot();
|
||||
void reload();
|
||||
void update(double deltaTime);
|
||||
void draw();
|
||||
|
||||
const std::string getType() const { return weaponType; }
|
||||
|
||||
struct BulletData {
|
||||
glm::vec3 origin;
|
||||
glm::vec2 direction;
|
||||
|
|
@ -67,9 +69,13 @@ public:
|
|||
|
||||
const BulletManager* getBulletManager() { return bulletManager.get(); }
|
||||
|
||||
~Weapon();
|
||||
|
||||
private:
|
||||
void adjustWeapon();
|
||||
|
||||
std::string weaponType;
|
||||
|
||||
glm::vec2 weaponSize;
|
||||
glm::vec2 weaponOffset;
|
||||
// TODO: Add reloading
|
||||
|
|
@ -100,8 +106,8 @@ private:
|
|||
Direction lastDir;
|
||||
|
||||
std::vector <std::unique_ptr<Component>> components;
|
||||
std::shared_ptr<WeaponScript> weaponScript;
|
||||
std::unique_ptr<WeaponScript> weaponScript;
|
||||
|
||||
};
|
||||
|
||||
#endif // _H_WEAPON_H
|
||||
#endif // _H_WEAPON_H
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <SDL_timer.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
|
|
@ -22,10 +23,11 @@ struct AnimationData;
|
|||
class Animation
|
||||
{
|
||||
public:
|
||||
Animation(const std::shared_ptr<AnimationData>& animData, ResourceManager* resourceManager);
|
||||
Animation(const AnimationData* animData, ResourceManager* resourceManager);
|
||||
|
||||
std::string getName() const { return animName; }
|
||||
std::string getType() const { return animType; }
|
||||
std::string getPrefix() const { return prefix; }
|
||||
std::string getType() const { return type; }
|
||||
std::string getID() const { return prefix + "/" + type; }
|
||||
|
||||
void bind();
|
||||
void draw();
|
||||
|
|
@ -33,7 +35,7 @@ public:
|
|||
|
||||
void play() { isPlaying = true; }
|
||||
void stop() { isPlaying = false; }
|
||||
void reset() { currentFrame = 0; }
|
||||
void reset() { currentFrame = 0; lastFrameTick = 0; elapsedTime = 0; }
|
||||
|
||||
const bool getPlaying() const { return isPlaying; }
|
||||
const bool getDirectional() const { return isDirectional; }
|
||||
|
|
@ -42,8 +44,8 @@ public:
|
|||
void setFPS(const float fps) { FPS = fps; }
|
||||
|
||||
private:
|
||||
std::string animName;
|
||||
std::string animType;
|
||||
std::string prefix;
|
||||
std::string type;
|
||||
|
||||
SpriteAtlas* spriteAtlas;
|
||||
|
||||
|
|
@ -69,7 +71,7 @@ class AnimationSet : public std::enable_shared_from_this<AnimationSet>
|
|||
{
|
||||
public:
|
||||
AnimationSet(const int& entityid);
|
||||
AnimationSet(const int& entityid, ResourceManager* resourceManager, std::vector<std::shared_ptr<AnimationData>> animSet);
|
||||
AnimationSet(const int& entityid, ResourceManager* resourceManager, std::vector<AnimationData*> animSet);
|
||||
AnimationSet(const int& entityid, std::unordered_map<std::string, std::unique_ptr<Animation>> animations);
|
||||
|
||||
Animation* operator [](std::string animType) { return anims[animType].get(); }
|
||||
|
|
@ -102,4 +104,4 @@ private:
|
|||
std::weak_ptr<EventManager> eventManager;
|
||||
};
|
||||
|
||||
#endif // _H_ANIMATION_H
|
||||
#endif // _H_ANIMATION_H
|
||||
|
|
|
|||
24
YuppleMayham/include/graphics/background.h
Normal file
24
YuppleMayham/include/graphics/background.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef _H_BACKGROUND_H
|
||||
#define _H_BACKGROUND_H
|
||||
|
||||
#include "graphics/quad.h"
|
||||
#include "graphics/drawable.h"
|
||||
#include "graphics/texture.h"
|
||||
|
||||
#include "utility/logger.h"
|
||||
|
||||
class Background : public Drawable
|
||||
{
|
||||
public:
|
||||
Background(const std::string& fileName);
|
||||
|
||||
void draw() override;
|
||||
|
||||
~Background();
|
||||
|
||||
private:
|
||||
ScreenQuad *quad;
|
||||
Texture *texture;
|
||||
};
|
||||
|
||||
#endif // _H_BACKGROUND_H
|
||||
50
YuppleMayham/include/graphics/drawable.h
Normal file
50
YuppleMayham/include/graphics/drawable.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef _H_DRAWABLE_H
|
||||
#define _H_DRAWABLE_H
|
||||
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <glm/glm.hpp>
|
||||
#include <string>
|
||||
|
||||
using UniformValue = std::variant<
|
||||
int,
|
||||
bool,
|
||||
float,
|
||||
double,
|
||||
glm::mat4,
|
||||
glm::vec2
|
||||
>;
|
||||
|
||||
struct Uniform {
|
||||
std::string name;
|
||||
UniformValue value;
|
||||
};
|
||||
|
||||
class Drawable
|
||||
{
|
||||
public:
|
||||
virtual void draw() = 0;
|
||||
const unsigned getShaderID() const { return shaderID; }
|
||||
const std::vector<Uniform>& getUniforms() { return uniforms; }
|
||||
const std::vector<Uniform>& getOneShotUniforms() { return oneShotUniforms; }
|
||||
void clearOneShot() { oneShotUniforms.clear(); }
|
||||
void clearUniforms() { uniforms.clear(); }
|
||||
protected:
|
||||
unsigned shaderID;
|
||||
template <typename T>
|
||||
void editUniform(const std::string& name, T value)
|
||||
{
|
||||
uniforms.emplace_back(Uniform {name, value});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void editUniformOnce(const std::string& name, T value)
|
||||
{
|
||||
oneShotUniforms.emplace_back(Uniform {name, value});
|
||||
}
|
||||
private:
|
||||
std::vector<Uniform> uniforms;
|
||||
std::vector<Uniform> oneShotUniforms;
|
||||
};
|
||||
|
||||
#endif // _H_DRAWABLE_H
|
||||
|
|
@ -2,52 +2,35 @@
|
|||
#define _H_GLWINDOW_H
|
||||
|
||||
#include <SDL_video.h>
|
||||
//#include <SDL_opengl.h>
|
||||
#include <thirdparty/glad/glad.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include "utility/logger.h"
|
||||
|
||||
class GLWindow
|
||||
{
|
||||
class GLWindow {
|
||||
public:
|
||||
GLWindow(const char* windowName, int width, int height) :
|
||||
name(windowName),
|
||||
w(width),
|
||||
h(height) {};
|
||||
~GLWindow();
|
||||
GLWindow(const char *windowName, int width, int height)
|
||||
: w(width), h(height), name(windowName) {};
|
||||
~GLWindow();
|
||||
|
||||
bool Init();
|
||||
bool Init();
|
||||
|
||||
void swap() { SDL_GL_SwapWindow(window); }
|
||||
void makeCurrent() { SDL_GL_MakeCurrent(window, glContext); }
|
||||
void swap() { SDL_GL_SwapWindow(window); }
|
||||
void makeCurrent() { SDL_GL_MakeCurrent(window, glContext); }
|
||||
|
||||
unsigned width() const { return w; }
|
||||
unsigned height() const { return h; }
|
||||
unsigned Width() const { return w; }
|
||||
unsigned Height() const { return h; }
|
||||
|
||||
void resizeWindow(size_t w, size_t h);
|
||||
|
||||
SDL_Window *getWindow() const { return window; }
|
||||
const SDL_GLContext &getContext() const { return glContext; }
|
||||
|
||||
SDL_Window* getWindow() const { return window; }
|
||||
const SDL_GLContext& getContext() const { return glContext; }
|
||||
private:
|
||||
SDL_Window* window = nullptr;
|
||||
SDL_GLContext glContext = NULL;
|
||||
SDL_Window *window = nullptr;
|
||||
SDL_GLContext glContext = NULL;
|
||||
|
||||
unsigned w, h;
|
||||
const char* name;
|
||||
unsigned w, h;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
bool GLWindow::Init()
|
||||
{
|
||||
window = SDL_CreateWindow(name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, SDL_WINDOW_OPENGL);
|
||||
if (!window)
|
||||
return false;
|
||||
glContext = SDL_GL_CreateContext(window);
|
||||
if (!glContext)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
GLWindow::~GLWindow()
|
||||
{
|
||||
SDL_GL_DeleteContext(glContext);
|
||||
SDL_DestroyWindow(window);
|
||||
}
|
||||
|
||||
#endif // _H_GLWINDOW_H
|
||||
#endif // _H_GLWINDOW_H
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
#include <thirdparty/glad/glad.h>
|
||||
#include <cstring>
|
||||
|
||||
#define MAX_INSTANCES 1000
|
||||
#define MAX_TEXTURES 31
|
||||
|
|
@ -50,6 +52,8 @@ public:
|
|||
const TextureArray* getTextureArray() { return textures; }
|
||||
const Texture* getTexture() { return texture; }
|
||||
|
||||
~TileTextureInstance();
|
||||
|
||||
private:
|
||||
void setup() override;
|
||||
TextureArray* textures = nullptr;
|
||||
|
|
@ -62,4 +66,4 @@ private:
|
|||
};
|
||||
};
|
||||
|
||||
#endif // _H_INSTANCEDRAW_H
|
||||
#endif // _H_INSTANCEDRAW_H
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <thirdparty/glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -28,4 +28,4 @@ private:
|
|||
size_t indexSize;
|
||||
};
|
||||
|
||||
#endif // _H_MESH_H
|
||||
#endif // _H_MESH_H
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#ifndef _H_POSTPROCESS_H
|
||||
#define _H_POSTPROCESS_H
|
||||
|
||||
#include <SDL_Timer.h>
|
||||
#include <glad/glad.h>
|
||||
#include <SDL_timer.h>
|
||||
#include <thirdparty/glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
|
@ -70,4 +70,4 @@ private:
|
|||
Shader* postProcessShader;
|
||||
};
|
||||
|
||||
#endif // _H_POSTPROCESS_H
|
||||
#endif // _H_POSTPROCESS_H
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
#ifndef _H_QUAD_H
|
||||
#define _H_QUAD_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <thirdparty/glad/glad.h>
|
||||
#include <cstring>
|
||||
|
||||
class Quad
|
||||
{
|
||||
public:
|
||||
void draw();
|
||||
~Quad();
|
||||
virtual ~Quad();
|
||||
protected:
|
||||
Quad(const float* vertexData);
|
||||
private:
|
||||
|
|
@ -52,4 +53,4 @@ private:
|
|||
};
|
||||
};
|
||||
|
||||
#endif // _H_QUAD_H
|
||||
#endif // _H_QUAD_H
|
||||
|
|
|
|||
|
|
@ -1,116 +1,67 @@
|
|||
#ifndef _H_RENDERER_H
|
||||
#define _H_RENDERER_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
#include <string>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "graphics/quad.h"
|
||||
#include "graphics/drawable.h"
|
||||
#include "graphics/glwindow.h"
|
||||
#include "graphics/postprocess.h"
|
||||
#include "graphics/quad.h"
|
||||
|
||||
class ResourceManager;
|
||||
class Shader;
|
||||
|
||||
enum class RenderLayer {
|
||||
Background,
|
||||
Map,
|
||||
GameObjects,
|
||||
Effects,
|
||||
HUD,
|
||||
Menu
|
||||
};
|
||||
enum class RenderLayer { Background, Map, GameObjects, Effects, HUD, Menu };
|
||||
|
||||
using UniformValue = std::variant<
|
||||
int,
|
||||
bool,
|
||||
float,
|
||||
double,
|
||||
glm::mat4,
|
||||
glm::vec2
|
||||
>;
|
||||
|
||||
struct Uniform {
|
||||
std::string name;
|
||||
UniformValue value;
|
||||
};
|
||||
|
||||
class Drawable
|
||||
{
|
||||
class Renderer {
|
||||
public:
|
||||
virtual void draw() = 0;
|
||||
const unsigned getShaderID() const { return shaderID; }
|
||||
const std::vector<Uniform>& getUniforms() { return uniforms; }
|
||||
const std::vector<Uniform>& getOneShotUniforms() { return oneShotUniforms; }
|
||||
void clearOneShot() { oneShotUniforms.clear(); }
|
||||
void clearUniforms() { uniforms.clear(); }
|
||||
protected:
|
||||
unsigned shaderID;
|
||||
template <typename T>
|
||||
void editUniform(const std::string& name, T value)
|
||||
{
|
||||
uniforms.emplace_back(Uniform {name, value});
|
||||
}
|
||||
Renderer(const std::shared_ptr<ResourceManager> &,
|
||||
const std::shared_ptr<GLWindow> &);
|
||||
|
||||
template <typename T>
|
||||
void editUniformOnce(const std::string& name, T value)
|
||||
{
|
||||
oneShotUniforms.emplace_back(Uniform {name, value});
|
||||
}
|
||||
private:
|
||||
std::vector<Uniform> uniforms;
|
||||
std::vector<Uniform> oneShotUniforms;
|
||||
};
|
||||
void clear();
|
||||
|
||||
class Renderer
|
||||
{
|
||||
public:
|
||||
Renderer(const std::shared_ptr<ResourceManager>&);
|
||||
void hookEventManager(const std::weak_ptr<EventManager> eventManager);
|
||||
|
||||
void clear();
|
||||
void setProjAndViewMatrix(const glm::mat4 &proj, const glm::mat4 &view);
|
||||
void addDrawable(RenderLayer renderLayer, Drawable *drawable);
|
||||
void removeDrawable(RenderLayer renderLayer, Drawable *drawable);
|
||||
|
||||
void hookEventManager(const std::weak_ptr<EventManager> eventManager);
|
||||
|
||||
void setProjAndViewMatrix(const glm::mat4& proj, const glm::mat4& view);
|
||||
void addDrawable(RenderLayer renderLayer, std::shared_ptr<Drawable> drawable);
|
||||
void removeDrawable(RenderLayer renderLayer, std::shared_ptr<Drawable> drawable);
|
||||
|
||||
void render();
|
||||
void render();
|
||||
|
||||
private:
|
||||
std::unordered_map<RenderLayer, std::vector<std::shared_ptr<Drawable>>> worldLayerPool;
|
||||
std::unordered_map<RenderLayer, std::vector<std::shared_ptr<Drawable>>> HUDLayerPool;
|
||||
std::vector<RenderLayer> renderingOrder = {
|
||||
RenderLayer::Background,
|
||||
RenderLayer::Map,
|
||||
RenderLayer::GameObjects,
|
||||
RenderLayer::Effects,
|
||||
RenderLayer::HUD,
|
||||
RenderLayer::Menu
|
||||
};
|
||||
std::unordered_map<RenderLayer, std::vector<Drawable *>> worldLayerPool;
|
||||
std::unordered_map<RenderLayer, std::vector<Drawable *>> HUDLayerPool;
|
||||
std::vector<RenderLayer> renderingOrder = {
|
||||
RenderLayer::Background, RenderLayer::Map, RenderLayer::GameObjects,
|
||||
RenderLayer::Effects, RenderLayer::HUD, RenderLayer::Menu};
|
||||
|
||||
void uploadUniforms(const unsigned shaderID, const std::vector<Uniform>& uniforms);
|
||||
void uploadUniforms(const unsigned shaderID,
|
||||
const std::vector<Uniform> &uniforms);
|
||||
|
||||
glm::mat4 projMat;
|
||||
glm::mat4 viewMat;
|
||||
unsigned uboMatrices;
|
||||
|
||||
union FrameBuffer {
|
||||
unsigned int frame;
|
||||
unsigned int texture;
|
||||
}worldBuffer, hudBuffer;
|
||||
union FrameBuffer {
|
||||
unsigned int frame;
|
||||
unsigned int texture;
|
||||
} worldBuffer, hudBuffer;
|
||||
|
||||
std::unique_ptr<ScreenQuad> screenQuad;
|
||||
std::unique_ptr<ScreenQuad> screenQuad;
|
||||
|
||||
std::shared_ptr<ResourceManager> resourceManager;
|
||||
std::shared_ptr<ResourceManager> resourceManager;
|
||||
std::shared_ptr<GLWindow> glWindow;
|
||||
|
||||
void initFrameBuffers();
|
||||
void initFrameBuffers();
|
||||
void initUniformBuffers();
|
||||
|
||||
std::unique_ptr<Postprocessor> postProcessor;
|
||||
void resizeFrameBuffers();
|
||||
|
||||
void sortLayerPool(auto& layerPool);
|
||||
void renderPool(auto& layerPool);
|
||||
std::unique_ptr<Postprocessor> postProcessor;
|
||||
|
||||
void sortLayerPool(auto &layerPool);
|
||||
void renderPool(auto &layerPool);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,16 +1,21 @@
|
|||
#ifndef _H_SHADER_H
|
||||
#define _H_SHADER_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <thirdparty/glad/glad.h>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
class Shader
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
bool compileAndLink(const char *vSource, const char *fSource);
|
||||
std::string vertexPath;
|
||||
std::string fragmentPath;
|
||||
bool valid;
|
||||
Shader();
|
||||
public:
|
||||
unsigned int ID;
|
||||
Shader(const char* vertexPath, const char* fragmentPath);
|
||||
Shader(const char* vertPath, const char* fragPath);
|
||||
|
||||
void use() { glUseProgram(ID); }
|
||||
void setFloat(const std::string& name, float value);
|
||||
|
|
@ -21,7 +26,15 @@ public:
|
|||
void setVec2(const std::string& name, const float* value);
|
||||
void setMatrix4f(const std::string& name, const float* value);
|
||||
|
||||
~Shader();
|
||||
bool isValid() { return valid; }
|
||||
|
||||
virtual ~Shader();
|
||||
};
|
||||
|
||||
#endif // _H_SHADER_H
|
||||
class GenericShader : public Shader
|
||||
{
|
||||
public:
|
||||
GenericShader() : Shader() {};
|
||||
};
|
||||
|
||||
#endif // _H_SHADER_H
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
#define _H_SPRITE_H
|
||||
|
||||
#include <SDL_image.h>
|
||||
#include <glad/glad.h>
|
||||
#include <thirdparty/glad/glad.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#include "utility/direction.h"
|
||||
|
||||
|
|
@ -102,4 +103,4 @@ private:
|
|||
std::unordered_map<Direction, std::vector<VertexIDs>> directionalIDs;
|
||||
};
|
||||
|
||||
#endif // _H_SPRITE_H
|
||||
#endif // _H_SPRITE_H
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
#define _H_TEXTURE_H
|
||||
|
||||
#include <vector>
|
||||
#include <thirdparty/glad/glad.h>
|
||||
#include <SDL_image.h>
|
||||
|
||||
struct SDL_Surface;
|
||||
|
||||
|
|
@ -36,14 +38,14 @@ public:
|
|||
|
||||
void bind();
|
||||
|
||||
TextureData* operator[](const size_t index) {
|
||||
TextureData operator[](const size_t index) {
|
||||
try
|
||||
{
|
||||
return textures.at(index);
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
return nullptr;
|
||||
{
|
||||
return { 0, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -57,9 +59,9 @@ private:
|
|||
|
||||
size_t numOfLayers;
|
||||
unsigned ID = 0;
|
||||
std::vector<TextureData*> textures;
|
||||
std::vector<TextureData> textures;
|
||||
int canvasWidth = 0;
|
||||
int canvasHeight = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
59
YuppleMayham/include/sound/audiostream.h
Normal file
59
YuppleMayham/include/sound/audiostream.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef _H_AUDIO_STREAM_H
|
||||
#define _H_AUDIO_STREAM_H
|
||||
|
||||
#define STB_VORBIS_HEADER_ONLY
|
||||
#include "thirdparty/stb_vorbis.c"
|
||||
|
||||
#include "utility/logger.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
class AudioStream
|
||||
{
|
||||
public:
|
||||
AudioStream();
|
||||
|
||||
bool PlayStream();
|
||||
void AddToQueue(std::string songName);
|
||||
void PauseStream() { paused = !paused; }
|
||||
|
||||
void kill() { stopThread = true; }
|
||||
~AudioStream();
|
||||
private:
|
||||
std::queue<std::string> songNames;
|
||||
std::mutex mutex;
|
||||
std::atomic<bool> stopThread;
|
||||
std::atomic<bool> paused;
|
||||
std::string CurStreamName();
|
||||
bool eof;
|
||||
std::thread streamThread;
|
||||
|
||||
int loadInitalChunk();
|
||||
int loadNextChunk();
|
||||
|
||||
int loadChunk(ALuint buffer);
|
||||
|
||||
void popQueue();
|
||||
void cycleQueue();
|
||||
|
||||
bool openFile(std::string fileName);
|
||||
|
||||
void stream();
|
||||
|
||||
short *data;
|
||||
stb_vorbis *file;
|
||||
stb_vorbis_info info;
|
||||
ALuint buffers[4];
|
||||
ALuint source;
|
||||
ALint state;
|
||||
ALint format;
|
||||
size_t last_buffer;
|
||||
};
|
||||
|
||||
#endif // _H_AUDIO_STREAM_H
|
||||
42
YuppleMayham/include/sound/engine.h
Normal file
42
YuppleMayham/include/sound/engine.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef _H_AUDIO_ENGINE_H
|
||||
#define _H_AUDIO_ENGINE_H
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "utility/events.h"
|
||||
#include "sound/audiostream.h"
|
||||
#include "sound/soundmanager.h"
|
||||
#include "utility/resourcemanager.h"
|
||||
#include "utility/logger.h"
|
||||
|
||||
class AudioEngine : public std::enable_shared_from_this<AudioEngine>
|
||||
{
|
||||
public:
|
||||
AudioEngine(std::weak_ptr<ResourceManager> _resource);
|
||||
|
||||
void hookEventManager(std::weak_ptr<EventManager> _events);
|
||||
void hookSceneManager(std::weak_ptr<EventManager> _events);
|
||||
void pushMusic(std::string _songName);
|
||||
void playMusic();
|
||||
void pauseMusic();
|
||||
void killMusic();
|
||||
|
||||
void poll();
|
||||
void updateListener(const glm::vec3& pos);
|
||||
|
||||
~AudioEngine();
|
||||
private:
|
||||
std::unique_ptr<AudioStream> musicPlayer;
|
||||
std::unique_ptr<SoundManager> soundManager;
|
||||
std::weak_ptr<EventManager> globalEventManager;
|
||||
std::weak_ptr<EventManager> sceneEventManager;
|
||||
std::weak_ptr<ResourceManager> resourceManager;
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
};
|
||||
|
||||
|
||||
#endif // _H_AUDIO_ENGINE_H
|
||||
40
YuppleMayham/include/sound/soundeffect.h
Normal file
40
YuppleMayham/include/sound/soundeffect.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* Resource manager is going to hold a list of soundeffects for each item and the state that item is in.
|
||||
* ie. A shotgun is reloading, so the shotgun is the item, and the state (reloading) has a list of different reloading sounds
|
||||
* this is so we don't have annoying repeats.
|
||||
*
|
||||
* The sound engine is going to handle global events that are passed from these objects which will contain their entity ID
|
||||
* and event specific information, such as event location and in special cases the type of item that is triggering the event
|
||||
* such as my shotgun has fired so I will have my audio engine look through a list of shotgun firing sound effects to play,
|
||||
* Once the engine knows what sound to play, it will look for the next available source, load the buffer onto that source and play.
|
||||
* Every frame the engine is responsible for checking every source and pull off buffers that are no longer in use
|
||||
*/
|
||||
|
||||
#ifndef _H_SOUNDEFFECT_H
|
||||
#define _H_SOUNDEFFECT_H
|
||||
|
||||
#define STB_VORBIS_HEADER_ONLY
|
||||
#include "thirdparty/stb_vorbis.c"
|
||||
|
||||
#include "utility/logger.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <string>
|
||||
|
||||
class SoundEffect
|
||||
{
|
||||
public:
|
||||
SoundEffect(const std::string& filename);
|
||||
|
||||
ALuint getBuffer() const { return buffer; }
|
||||
bool isValid() const { return valid; }
|
||||
|
||||
~SoundEffect();
|
||||
private:
|
||||
bool loadFile(const std::string &filename);
|
||||
|
||||
ALuint buffer;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
#endif // _H_SOUNDEFFECT_H
|
||||
26
YuppleMayham/include/sound/soundmanager.h
Normal file
26
YuppleMayham/include/sound/soundmanager.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef _H_SOUNDMANAGER_H
|
||||
#define _H_SOUNDMANAGER_H
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <array>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class SoundManager
|
||||
{
|
||||
public:
|
||||
SoundManager();
|
||||
void playSound(ALuint buffer, int priority=10, const glm::vec3& pos = glm::vec3(0));
|
||||
void pollSources();
|
||||
~SoundManager();
|
||||
private:
|
||||
struct AudioSource {
|
||||
ALuint source;
|
||||
ALuint buffer;
|
||||
bool inUse;
|
||||
int priority;
|
||||
};
|
||||
std::array<AudioSource, 10> sources;
|
||||
std::size_t lastUsed = 0;
|
||||
};
|
||||
|
||||
#endif // _H_SOUNDMANAGER_H
|
||||
311
YuppleMayham/include/thirdparty/KHR/khrplatform.h
vendored
Normal file
311
YuppleMayham/include/thirdparty/KHR/khrplatform.h
vendored
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
#ifndef __khrplatform_h_
|
||||
#define __khrplatform_h_
|
||||
|
||||
/*
|
||||
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included
|
||||
** in all copies or substantial portions of the Materials.
|
||||
**
|
||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||
* The last semantic modification to khrplatform.h was at commit ID:
|
||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
* group so that they can be included in future versions of this file.
|
||||
* Please submit changes by filing pull requests or issues on
|
||||
* the EGL Registry repository linked above.
|
||||
*
|
||||
*
|
||||
* See the Implementer's Guidelines for information about where this file
|
||||
* should be located on your system and for more details of its use:
|
||||
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||
*
|
||||
* This file should be included as
|
||||
* #include <KHR/khrplatform.h>
|
||||
* by Khronos client API header files that use its types and defines.
|
||||
*
|
||||
* The types in khrplatform.h should only be used to define API-specific types.
|
||||
*
|
||||
* Types defined in khrplatform.h:
|
||||
* khronos_int8_t signed 8 bit
|
||||
* khronos_uint8_t unsigned 8 bit
|
||||
* khronos_int16_t signed 16 bit
|
||||
* khronos_uint16_t unsigned 16 bit
|
||||
* khronos_int32_t signed 32 bit
|
||||
* khronos_uint32_t unsigned 32 bit
|
||||
* khronos_int64_t signed 64 bit
|
||||
* khronos_uint64_t unsigned 64 bit
|
||||
* khronos_intptr_t signed same number of bits as a pointer
|
||||
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||
* khronos_ssize_t signed size
|
||||
* khronos_usize_t unsigned size
|
||||
* khronos_float_t signed 32 bit floating point
|
||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||
* nanoseconds
|
||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||
* only be used as a base type when a client API's boolean type is
|
||||
* an enum. Client APIs which use an integer or other type for
|
||||
* booleans cannot use this as the base type for their boolean.
|
||||
*
|
||||
* Tokens defined in khrplatform.h:
|
||||
*
|
||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||
*
|
||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||
*
|
||||
* Calling convention macros defined in this file:
|
||||
* KHRONOS_APICALL
|
||||
* KHRONOS_APIENTRY
|
||||
* KHRONOS_APIATTRIBUTES
|
||||
*
|
||||
* These may be used in function prototypes as:
|
||||
*
|
||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||
* int arg1,
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined (__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
/*
|
||||
* To support platform where unsigned long cannot be used interchangeably with
|
||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||
* unsigned long long or similar (this results in different C++ name mangling).
|
||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||
* platforms where the size of a pointer is larger than the size of long.
|
||||
*/
|
||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||
#define KHRONOS_USE_INTPTR_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
typedef __int32 khronos_int32_t;
|
||||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
typedef long int khronos_int64_t;
|
||||
typedef unsigned long int khronos_uint64_t;
|
||||
#else
|
||||
typedef long long int khronos_int64_t;
|
||||
typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Types that are (so far) the same on all platforms
|
||||
*/
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef KHRONOS_USE_INTPTR_T
|
||||
typedef intptr_t khronos_intptr_t;
|
||||
typedef uintptr_t khronos_uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_FLOAT
|
||||
/*
|
||||
* Float type
|
||||
*/
|
||||
typedef float khronos_float_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_INT64
|
||||
/* Time types
|
||||
*
|
||||
* These types can be used to represent a time interval in nanoseconds or
|
||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||
* time the system booted). The Unadjusted System Time is an unsigned
|
||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||
* may be either signed or unsigned.
|
||||
*/
|
||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
typedef enum {
|
||||
KHRONOS_FALSE = 0,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
||||
#endif /* __khrplatform_h_ */
|
||||
2129
YuppleMayham/include/thirdparty/glad/glad.h
vendored
Normal file
2129
YuppleMayham/include/thirdparty/glad/glad.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
5584
YuppleMayham/include/thirdparty/stb_vorbis.c
vendored
Normal file
5584
YuppleMayham/include/thirdparty/stb_vorbis.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -5,10 +5,41 @@
|
|||
#include <random>
|
||||
|
||||
namespace UTIL
|
||||
{
|
||||
{
|
||||
namespace AUDIO {
|
||||
constexpr size_t CHUNK_SIZE = 4096;
|
||||
constexpr int SAMPLE_RATE = 44100;
|
||||
}
|
||||
|
||||
constexpr float INF_TIME = -99.6875f;
|
||||
constexpr auto split = [](const std::string& s, size_t *left, size_t *right, std::string *out) {
|
||||
if ((*right = s.find("/", *left)) != std::string::npos) {
|
||||
if (out) {
|
||||
*out = s.substr(*left, *right - *left);
|
||||
}
|
||||
*left = *right + 1;
|
||||
} else {
|
||||
if (out) {
|
||||
out->append(s.substr(*left));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constexpr auto get_type = [](const std::string& id) {
|
||||
auto pos = id.find_last_of("/");
|
||||
return (pos != std::string::npos) ? id.substr(pos + 1) : id;
|
||||
};
|
||||
|
||||
constexpr auto get_generic = [](const std::string& id) {
|
||||
size_t left = 0;
|
||||
size_t right = 0;
|
||||
std::string out;
|
||||
split(id, &left, &right, &out);
|
||||
return (out + "/generic/" + get_type(id));
|
||||
};
|
||||
|
||||
void flip_surface(SDL_Surface* surface);
|
||||
std::string parsePrefix(const std::string& id);
|
||||
|
||||
class RandomGenerator
|
||||
{
|
||||
|
|
@ -25,4 +56,4 @@ namespace UTIL
|
|||
};
|
||||
}
|
||||
|
||||
#endif // _H_UTIL_H
|
||||
#endif // _H_UTIL_H
|
||||
|
|
|
|||
|
|
@ -145,8 +145,10 @@ public:
|
|||
void idle() override { /*unused*/ }
|
||||
TYPE getType() const override { return TYPE::AI; }
|
||||
|
||||
~AIComponent() { }
|
||||
|
||||
private:
|
||||
std::shared_ptr<AI> ai;
|
||||
};
|
||||
|
||||
#endif // _H_COMPONENT_H
|
||||
#endif // _H_COMPONENT_H
|
||||
|
|
|
|||
9
YuppleMayham/include/utility/data/font_data.h
Normal file
9
YuppleMayham/include/utility/data/font_data.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/* C array of file 'Px437_IBM_VGA_9x8.ttf' */
|
||||
|
||||
#ifndef _H_FONT_DATA_H
|
||||
#define _H_FONT_DATA_H
|
||||
|
||||
extern const unsigned char debug_font_data[];
|
||||
extern const unsigned int debug_font_len;
|
||||
|
||||
#endif // _H_FONT_DATA_H
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
#ifndef _H_DEBUGDRAW_H
|
||||
#define _H_DEBUGDRAW_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <thirdparty/glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <utility/ftfont.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
struct ShaderSource {
|
||||
const char* vertexShader;
|
||||
|
|
@ -178,4 +179,4 @@ private:
|
|||
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
#ifndef _H_EVENTS_H
|
||||
#define _H_EVENTS_H
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "utility/direction.h"
|
||||
|
|
@ -12,124 +13,91 @@
|
|||
class Bullet;
|
||||
struct PhysicsComponent;
|
||||
|
||||
class Event {
|
||||
public:
|
||||
virtual ~Event() { };
|
||||
virtual std::string getType() const = 0;
|
||||
struct BulletFiredEvent {
|
||||
std::weak_ptr<Bullet> bullet;
|
||||
};
|
||||
|
||||
class BulletFiredEvent : public Event {
|
||||
public:
|
||||
BulletFiredEvent(const std::shared_ptr<Bullet>& bullet) : bullet(bullet) {};
|
||||
std::string getType() const override { return "OnBulletFired"; }
|
||||
std::shared_ptr<Bullet> bullet;
|
||||
struct BulletDiedEvent {
|
||||
std::shared_ptr<PhysicsComponent> physObj;
|
||||
};
|
||||
|
||||
class BulletDiedEvent : public Event {
|
||||
public:
|
||||
BulletDiedEvent(const std::shared_ptr<PhysicsComponent>& physObj) : physObj(physObj) {};
|
||||
std::string getType() const override { return "OnBulletDied"; }
|
||||
std::shared_ptr<PhysicsComponent> physObj;
|
||||
struct BulletCollideEvent {
|
||||
unsigned int ownerID;
|
||||
unsigned int victimID;
|
||||
std::shared_ptr<PhysicsComponent> bullet;
|
||||
glm::vec2 normal;
|
||||
};
|
||||
|
||||
class BulletCollideEvent : public Event {
|
||||
public:
|
||||
BulletCollideEvent(const unsigned int ownerID, const unsigned int victimID, const std::shared_ptr<PhysicsComponent>& bullet, const glm::vec2& normal)
|
||||
: ownerID(ownerID), victimID(victimID), bullet(bullet), normal(normal){}
|
||||
std::string getType() const override { return "OnBulletCollide"; }
|
||||
unsigned int ownerID;
|
||||
unsigned int victimID;
|
||||
std::shared_ptr<PhysicsComponent> bullet;
|
||||
glm::vec2 normal;
|
||||
struct DirectionChangeEvent {
|
||||
int entityid;
|
||||
Direction direction;
|
||||
};
|
||||
|
||||
class DirectionChangeEvent : public Event {
|
||||
public:
|
||||
DirectionChangeEvent(const int entityid, Direction direction) : direction(direction), entityid(entityid) {}
|
||||
std::string getType() const override { return "OnDirectionChange"; }
|
||||
Direction direction;
|
||||
int entityid;
|
||||
struct EntityMoveEvent {
|
||||
int entityid;
|
||||
};
|
||||
|
||||
class EntityMoveEvent : public Event {
|
||||
public:
|
||||
EntityMoveEvent(const int entityid) : entityid(entityid) {}
|
||||
std::string getType() const override { return "OnEntityMove"; }
|
||||
int entityid;
|
||||
struct EntityStopEvent {
|
||||
int entityid;
|
||||
};
|
||||
|
||||
class EntityStopEvent : public Event {
|
||||
public:
|
||||
EntityStopEvent(const int entityid) : entityid(entityid) {}
|
||||
std::string getType() const override { return "OnEntityStop"; }
|
||||
int entityid;
|
||||
struct EntityReloadEvent {
|
||||
int entityid;
|
||||
glm::vec3 position;
|
||||
std::string weaponType;
|
||||
};
|
||||
|
||||
class EntityReloadEvent : public Event {
|
||||
public:
|
||||
EntityReloadEvent(const int entityid) : entityid(entityid) {}
|
||||
std::string getType() const override { return "OnEntityReload"; }
|
||||
int entityid;
|
||||
struct EntityFinishReloadEvent {
|
||||
int entityid;
|
||||
};
|
||||
|
||||
class EntityFinishReloadEvent : public Event {
|
||||
public:
|
||||
EntityFinishReloadEvent(const int entityid) : entityid(entityid) {}
|
||||
std::string getType() const override { return "OnEntityFinishReload"; }
|
||||
int entityid;
|
||||
struct EntityFireEvent {
|
||||
int entityid;
|
||||
float fireDelay;
|
||||
glm::vec3 firePosition;
|
||||
std::string weaponType;
|
||||
};
|
||||
|
||||
class EntityFireEvent : public Event {
|
||||
public:
|
||||
EntityFireEvent(const int entityid, const float fireDelay) : entityid(entityid), fireDelay(fireDelay) {}
|
||||
std::string getType() const override { return "OnEntityFire"; }
|
||||
int entityid;
|
||||
float fireDelay;
|
||||
struct AnimationFinishedEvent {
|
||||
int entityid;
|
||||
std::string animType;
|
||||
};
|
||||
|
||||
class AnimationFinishedEvent : public Event {
|
||||
public:
|
||||
AnimationFinishedEvent(const int entityid, const std::string animType) : animType(animType), entityid(entityid) {}
|
||||
std::string getType() const override { return "OnAnimationFinished"; }
|
||||
std::string animType;
|
||||
int entityid;
|
||||
struct ScreenShakeEvent {
|
||||
float intensity;
|
||||
float duration;
|
||||
};
|
||||
|
||||
class ScreenShakeEvent : public Event {
|
||||
public:
|
||||
ScreenShakeEvent(float intensity, float duration) : intensity(intensity), duration(duration) {}
|
||||
std::string getType() const override { return "OnScreenShake"; }
|
||||
float intensity;
|
||||
float duration;
|
||||
struct ScreenBlurEvent {
|
||||
float intensity;
|
||||
float duration;
|
||||
};
|
||||
|
||||
class ScreenBlurEvent : public Event {
|
||||
public:
|
||||
ScreenBlurEvent(float intensity, float duration) : intensity(intensity), duration(duration) {}
|
||||
std::string getType() const override { return "OnScreenBlur"; }
|
||||
float intensity;
|
||||
float duration;
|
||||
struct WindowResizeEvent {
|
||||
size_t width;
|
||||
size_t height;
|
||||
};
|
||||
|
||||
class EventManager {
|
||||
public:
|
||||
using EventCallback = std::function<void(std::shared_ptr<Event>)>;
|
||||
template <typename T> using EventCallback = std::function<void(const T &)>;
|
||||
|
||||
template <typename T> void subscribe(EventCallback<T> cb) {
|
||||
auto wrapper = [cb](void *ev) { cb(*static_cast<T *>(ev)); };
|
||||
callbacks[typeid(T)].push_back(wrapper);
|
||||
}
|
||||
template <typename T> void notify(const T &event) {
|
||||
auto iterator = callbacks.find(typeid(T));
|
||||
if (iterator != callbacks.end()) {
|
||||
for (auto &cb : iterator->second) {
|
||||
cb((void *)&event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void subscribe(std::string eventType, EventCallback callback) {
|
||||
listeners[eventType].push_back(callback);
|
||||
}
|
||||
void notify(const std::shared_ptr<Event>& event) {
|
||||
auto iterator = listeners.find(event->getType());
|
||||
if (iterator != listeners.end())
|
||||
{
|
||||
for (auto& callback : iterator->second)
|
||||
{
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::unordered_map <std::string, std::vector <EventCallback>> listeners;
|
||||
std::unordered_map<std::type_index, std::vector<std::function<void(void *)>>>
|
||||
callbacks;
|
||||
};
|
||||
|
||||
#endif //_H_EVENTS_H
|
||||
#endif //_H_EVENTS_H
|
||||
|
|
|
|||
|
|
@ -4,14 +4,19 @@
|
|||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#include <iostream>
|
||||
#include <thirdparty/glad/glad.h>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#define DEBUG_TEXT(pos_vec2, col_vec4, scale, text, ...) DebugText::getInstance()->DrawDebugText(pos_vec2, col_vec4, scale, text, __VA_ARGS__)
|
||||
|
||||
class Text {
|
||||
private:
|
||||
protected:
|
||||
struct Font {
|
||||
struct Character {
|
||||
unsigned int TextureID;
|
||||
|
|
@ -26,15 +31,11 @@ private:
|
|||
unsigned int programID = 0;
|
||||
unsigned int VAO = 0, VBO = 0;
|
||||
unsigned int colorPos = 0;
|
||||
unsigned int projPos = 0;
|
||||
glm::mat4 projMatrix = glm::mat4(1.f);
|
||||
|
||||
public:
|
||||
Text();
|
||||
bool loadFonts(const std::string& font_folder);
|
||||
|
||||
void setProjectionMatrix(const glm::mat4& proj) { projMatrix = proj; }
|
||||
|
||||
void DrawText(
|
||||
const std::string& fontName,
|
||||
const std::string& text,
|
||||
|
|
@ -49,6 +50,45 @@ public:
|
|||
float scale,
|
||||
const glm::vec4& color = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)
|
||||
);
|
||||
~Text() { };
|
||||
};
|
||||
|
||||
class DebugText : public Text {
|
||||
public:
|
||||
static DebugText* getInstance();
|
||||
bool loadFonts(const std::string &font_folder) = delete;
|
||||
DebugText(DebugText const&) = delete;
|
||||
void operator=(DebugText const&) = delete;
|
||||
|
||||
template<typename... Args>
|
||||
void DrawDebugText(const glm::vec2& pos,
|
||||
const glm::vec4& color,
|
||||
const float scale,
|
||||
const std::string& text,
|
||||
Args... args)
|
||||
{
|
||||
DrawText(pos, color, scale, formatString(text, args...));
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
static std::mutex mutex;
|
||||
static DebugText *instance;
|
||||
Font font;
|
||||
DebugText();
|
||||
void loadDebugFont();
|
||||
~DebugText();
|
||||
|
||||
template <typename... Args>
|
||||
std::string formatString(const std::string& str, Args... args) const
|
||||
{
|
||||
return std::vformat(str, std::make_format_args(args...));
|
||||
}
|
||||
void DrawText(const glm::vec2& pos,
|
||||
const glm::vec4& color,
|
||||
const float scale,
|
||||
const std::string& text);
|
||||
|
||||
};
|
||||
|
||||
#endif // _H_FTFONT_H
|
||||
|
|
|
|||
|
|
@ -76,4 +76,4 @@ private:
|
|||
static std::mutex mutex;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,66 +1,90 @@
|
|||
#ifndef _H_RESOURCEMANAGER_H
|
||||
#define _H_RESOURCEMANAGER_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "utility/xmlloader.h"
|
||||
#include "graphics/background.h"
|
||||
#include "graphics/shader.h"
|
||||
#include "graphics/sprite.h"
|
||||
#include "sound/soundeffect.h"
|
||||
#include "utility/script.h"
|
||||
#include "utility/xmlloader.h"
|
||||
#include <cassert>
|
||||
|
||||
class Weapon;
|
||||
class Script;
|
||||
class AnimationSet;
|
||||
class AIScript;
|
||||
class WeaponScript;
|
||||
class TileSetData;
|
||||
class SpriteComponent;
|
||||
|
||||
class ResourceManager
|
||||
{
|
||||
class ResourceManager {
|
||||
public:
|
||||
ResourceManager() :
|
||||
xmlLoader(std::make_shared<XMLLoader>())
|
||||
{
|
||||
xmlLoader->loadWeapons("weapons");
|
||||
xmlLoader->loadAnimations("animations");
|
||||
xmlLoader->loadTileSets("maps/tilesets");
|
||||
xmlLoader->loadMaps("maps");
|
||||
xmlLoader->loadScenes("scenes");
|
||||
};
|
||||
ResourceManager() : xmlLoader(std::make_shared<XMLLoader>()) {
|
||||
xmlLoader->loadWeapons("weapons");
|
||||
xmlLoader->loadAnimations("animations");
|
||||
xmlLoader->loadMonsters("monsters");
|
||||
xmlLoader->loadTileSets("maps/tilesets");
|
||||
xmlLoader->loadMaps("maps");
|
||||
xmlLoader->loadScripts("scripts", loadLuaString);
|
||||
xmlLoader->loadScenes("scenes");
|
||||
xmlLoader->loadSoundEffects("sounds");
|
||||
shaders["__fallback__"] = std::make_unique<GenericShader>();
|
||||
};
|
||||
|
||||
SpriteAtlas* loadSpriteAtlas (const std::string& path, float frameSize, bool isDirectional = false);
|
||||
Sprite* loadSpriteStatic (const std::string& path);
|
||||
std::shared_ptr<AIScript> loadAIScript (const std::string& path);
|
||||
std::shared_ptr<WeaponScript> loadWeaponScript (const std::string& path);
|
||||
// Returns a NON-OWNING pointer to a sprite atlas
|
||||
SpriteAtlas *loadSpriteAtlas(const std::string &path, float frameSize,
|
||||
bool isDirectional = false);
|
||||
// Returns a NON-OWNING pointer to a static sprite
|
||||
Sprite *loadSpriteStatic(const std::string &path);
|
||||
// Returns a NON-OWNING pointer to a background
|
||||
Background *loadBackground(const std::string &path);
|
||||
const unsigned loadSoundEffect(const std::string &id);
|
||||
template <typename T = Script>
|
||||
std::unique_ptr<T> loadScript(const std::string &id);
|
||||
std::unique_ptr<Weapon> loadWeapon(const std::string &name,
|
||||
const unsigned weaponShaderID,
|
||||
const unsigned bulletShaderID);
|
||||
std::shared_ptr<AnimationSet> loadAnimationSet(const std::string &name,
|
||||
int entityid = 0);
|
||||
|
||||
const unsigned loadShader (const std::string& name, const std::string& vertexPath, const std::string& fragPath);
|
||||
std::shared_ptr<Weapon> loadWeapon (const std::string& name, const unsigned weaponShaderID, const unsigned bulletShaderID);
|
||||
std::shared_ptr<SceneData> loadScene (const std::string& id);
|
||||
std::shared_ptr<AnimationSet> loadAnimationSet(const std::string& name, int entityid = 0);
|
||||
std::shared_ptr<TileSetData> loadTileSet (const std::string& name);
|
||||
const unsigned loadShader(const std::string &name,
|
||||
const std::string &vertexPath,
|
||||
const std::string &fragPath);
|
||||
const SceneData *loadScene(const std::string &id);
|
||||
const TileSetData *loadTileSet(const std::string &name);
|
||||
|
||||
Shader* getShaderByID(unsigned int ID);
|
||||
// Returns a NON-OWNING pointer to a shader by ID
|
||||
Shader *getShaderByID(unsigned int ID);
|
||||
|
||||
void clearResources();
|
||||
void clearResources();
|
||||
|
||||
~ResourceManager();
|
||||
~ResourceManager();
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::unique_ptr<Shader>> shaders;
|
||||
std::unordered_map<unsigned, Shader*> shaderIDs;
|
||||
std::unordered_map<std::string, std::unique_ptr<Sprite>> sprites;
|
||||
std::unordered_map<std::string, std::shared_ptr<Weapon>> weapons;
|
||||
std::unordered_map<std::string, std::shared_ptr<Script>> scripts;
|
||||
std::unordered_map<std::string, std::shared_ptr<TileSetData>>tileSets;
|
||||
//std::unordered_map<std::string, std::shared_ptr<EntityData>> entityData;
|
||||
//std::unordered_map<std::string, std::shared_ptr<SceneData>> scenes;
|
||||
//std::unordered_map<std::string, std::shared_ptr<MapData>> maps;
|
||||
//std::unordered_map<std::string, std::shared_ptr<TileType>> tiles;
|
||||
std::unordered_map<std::string, std::unique_ptr<Shader>> shaders;
|
||||
std::unordered_map<unsigned, Shader *> shaderIDs;
|
||||
std::unordered_map<std::string, std::unique_ptr<SoundEffect>> sounds;
|
||||
std::unordered_map<std::string, std::unique_ptr<Sprite>> sprites;
|
||||
std::unordered_map<std::string, std::unique_ptr<Background>> backgrounds;
|
||||
std::unordered_map<std::string, std::shared_ptr<TileSetData>> tileSets;
|
||||
|
||||
std::shared_ptr<XMLLoader> xmlLoader;
|
||||
std::shared_ptr<XMLLoader> xmlLoader;
|
||||
|
||||
static bool loadLuaString(ScriptData *script);
|
||||
};
|
||||
|
||||
#endif // _H_RESOURCEMANAGER_H
|
||||
template <typename T>
|
||||
std::unique_ptr<T> ResourceManager::loadScript(const std::string &id) {
|
||||
const ScriptData *data = xmlLoader->getScriptData(id);
|
||||
if (data == nullptr)
|
||||
return nullptr;
|
||||
try {
|
||||
std::unique_ptr<T> script = std::make_unique<T>(data->script);
|
||||
return std::move(script);
|
||||
} catch (std::exception &e) {
|
||||
LOG(ERROR, "Failed to load script '{}'", data->fileName);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // _H_RESOURCEMANAGER_H
|
||||
|
|
|
|||
|
|
@ -3,42 +3,45 @@
|
|||
|
||||
#define SOL_ALL_SAFETIES_ON 1
|
||||
|
||||
#include <sol/sol.hpp>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
class Script {
|
||||
public:
|
||||
sol::state lua;
|
||||
Script(const std::string& path);
|
||||
virtual ~Script() {};
|
||||
sol::state lua;
|
||||
Script(const std::string &str);
|
||||
~Script();
|
||||
|
||||
private:
|
||||
bool loadScript(const std::string& path) {
|
||||
auto result = lua.script_file(path);
|
||||
if (!result.valid())
|
||||
{
|
||||
sol::error err = result;
|
||||
std::cerr << "Failed to load script ( " << path << " ) Error: " << err.what() << std::endl;
|
||||
}
|
||||
return result.valid();
|
||||
}
|
||||
void registerGlobalUserTypes();
|
||||
bool loadScript(const std::string &str) {
|
||||
auto result = lua.script(str);
|
||||
if (!result.valid()) {
|
||||
sol::error err = result;
|
||||
std::cerr << "Failed to load script. Error: " << err.what() << std::endl;
|
||||
}
|
||||
return result.valid();
|
||||
}
|
||||
void registerGlobalUserTypes();
|
||||
};
|
||||
|
||||
class AIScript : public Script {
|
||||
public:
|
||||
AIScript(const std::string& path) : Script(path) { registerUserTypes(); }
|
||||
AIScript(const std::string &str) : Script(str) { registerUserTypes(); }
|
||||
|
||||
~AIScript();
|
||||
|
||||
private:
|
||||
|
||||
void registerUserTypes() ;
|
||||
void registerUserTypes();
|
||||
};
|
||||
|
||||
class WeaponScript : public Script {
|
||||
public:
|
||||
WeaponScript(const std::string& path) : Script(path) { registerUserTypes(); }
|
||||
WeaponScript(const std::string &str) : Script(str) { registerUserTypes(); }
|
||||
~WeaponScript();
|
||||
|
||||
private:
|
||||
void registerUserTypes() ;
|
||||
void registerUserTypes();
|
||||
};
|
||||
|
||||
#endif // _H_SCRIPT_H
|
||||
#endif // _H_SCRIPT_H
|
||||
|
|
|
|||
|
|
@ -1,190 +1,246 @@
|
|||
#ifndef _H_XMLLOADER_H
|
||||
#define _H_XMLLOADER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <tinyxml2.h>
|
||||
|
||||
struct Tile;
|
||||
|
||||
// Except for the player, these definitions are mostly overrides of the monster
|
||||
// data
|
||||
struct EntityData {
|
||||
bool isPlayer;
|
||||
bool animated;
|
||||
int x = 0, y = 0;
|
||||
std::string graphic;
|
||||
std::string weapon = "pistolGun";
|
||||
std::string script;
|
||||
bool isPlayer;
|
||||
bool animated;
|
||||
int x = 0, y = 0;
|
||||
float hp;
|
||||
std::string monsterDef;
|
||||
std::string graphic;
|
||||
std::string weapon = "gun/pistol";
|
||||
std::string script;
|
||||
};
|
||||
|
||||
struct MapData {
|
||||
std::string name;
|
||||
struct TileSet {
|
||||
int startID = 1;
|
||||
std::string path;
|
||||
};
|
||||
std::vector<TileSet> tileSets;
|
||||
int width = 0, height = 0;
|
||||
float tileSize = 32.f;
|
||||
// 3D array, 0: layer, 1: y, 2: x
|
||||
// Holding tile startID + ids, 0 is an empty tile
|
||||
std::vector<std::vector<std::vector<int>>> tiles;
|
||||
std::string name;
|
||||
struct TileSet {
|
||||
int startID = 1;
|
||||
std::string path;
|
||||
};
|
||||
std::vector<TileSet> tileSets;
|
||||
int width = 0, height = 0;
|
||||
float tileSize = 32.f;
|
||||
// 3D array, 0: layer, 1: y, 2: x
|
||||
// Holding tile startID + ids, 0 is an empty tile
|
||||
std::vector<std::vector<std::vector<int>>> tiles;
|
||||
};
|
||||
|
||||
struct TileSetData {
|
||||
std::string name;
|
||||
std::string type;
|
||||
std::string file;
|
||||
int width = 0, height = 0;
|
||||
int columns = 0;
|
||||
int tileCount = 0;
|
||||
float tileSize = 64.f;
|
||||
struct TileData {
|
||||
int id = 0;
|
||||
std::string type;
|
||||
bool walkable = true;
|
||||
struct ObjectData {
|
||||
int id = 0;
|
||||
std::string name;
|
||||
glm::vec2 pos;
|
||||
glm::vec2 size;
|
||||
bool collidable = false;
|
||||
bool pickup = false;
|
||||
};
|
||||
std::vector<std::shared_ptr<ObjectData>> objects;
|
||||
};
|
||||
std::vector<std::shared_ptr<TileData>> tiles;
|
||||
std::string name;
|
||||
std::string type;
|
||||
std::string file;
|
||||
int width = 0, height = 0;
|
||||
int columns = 0;
|
||||
int tileCount = 0;
|
||||
float tileSize = 64.f;
|
||||
struct TileData {
|
||||
int id = 0;
|
||||
std::string type;
|
||||
bool walkable = true;
|
||||
struct ObjectData {
|
||||
int id = 0;
|
||||
std::string name;
|
||||
glm::vec2 pos;
|
||||
glm::vec2 size;
|
||||
bool collidable = false;
|
||||
bool pickup = false;
|
||||
};
|
||||
std::vector<std::shared_ptr<ObjectData>> objects;
|
||||
};
|
||||
std::vector<std::shared_ptr<TileData>> tiles;
|
||||
};
|
||||
|
||||
struct SceneData {
|
||||
std::string id;
|
||||
std::string type;
|
||||
std::string bgFile;
|
||||
std::string id;
|
||||
std::string type;
|
||||
std::string bgFile;
|
||||
|
||||
std::shared_ptr<MapData> map;
|
||||
std::vector<EntityData> entities;
|
||||
const MapData *map;
|
||||
std::vector<EntityData> entities;
|
||||
};
|
||||
|
||||
// Store our script as a string so we only need to read the file at boot
|
||||
struct ScriptData {
|
||||
std::string id;
|
||||
std::string fileName;
|
||||
std::string script;
|
||||
};
|
||||
|
||||
struct WeaponData {
|
||||
std::string name;
|
||||
float fireSpeed = 250.0f;
|
||||
int clipSize = 21;
|
||||
int maxAmmo = 512;
|
||||
std::string script = "";
|
||||
std::string graphic;
|
||||
std::string animSet;
|
||||
bool animated = false;
|
||||
float sizeX = 50.f, sizeY = 50.f;
|
||||
float offsetX = 0.f, offsetY = 0.f;
|
||||
float bulletSizeX = 50.f, bulletSizeY = 50.f;
|
||||
std::string bulletGraphic;
|
||||
bool bulletAnimated = false;
|
||||
float bulletSpread = 1.0f, bulletSpeed = 3.0f, bulletDrop = 500.f;
|
||||
float modMin = 0.5f, modMax = 1.0f;
|
||||
std::string id;
|
||||
float fireSpeed = 250.0f;
|
||||
int clipSize = 21;
|
||||
int maxAmmo = 512;
|
||||
std::string script = "";
|
||||
std::string graphic;
|
||||
bool animated = false;
|
||||
float sizeX = 50.f, sizeY = 50.f;
|
||||
float offsetX = 0.f, offsetY = 0.f;
|
||||
float bulletSizeX = 50.f, bulletSizeY = 50.f;
|
||||
std::string bulletGraphic;
|
||||
bool bulletAnimated = false;
|
||||
float bulletSpread = 1.0f, bulletSpeed = 3.0f, bulletDrop = 500.f;
|
||||
float modMin = 0.5f, modMax = 1.0f;
|
||||
};
|
||||
|
||||
// ID is the new tactic I've decided on.
|
||||
// Each animationable object will be given a prefix
|
||||
// denoting their type and object so <type>/<object>
|
||||
// This prefix will be used to look up the animation on
|
||||
// the animation map
|
||||
struct AnimationData {
|
||||
std::string name;
|
||||
std::string type;
|
||||
std::string spriteAtlas;
|
||||
// Each entity will have a set of animations,
|
||||
// this animation set is meant to keep each group
|
||||
// of animations within their set.
|
||||
// The actual set name is based on the file the animation
|
||||
// is held in.
|
||||
std::string animSet;
|
||||
bool directional = false;
|
||||
bool oneShot;
|
||||
float FPS = 1.f;
|
||||
float frameSize;
|
||||
std::string id;
|
||||
std::string spriteAtlas;
|
||||
bool directional = false;
|
||||
bool oneShot;
|
||||
float FPS = 1.f;
|
||||
float frameSize;
|
||||
};
|
||||
|
||||
class XMLLoader
|
||||
{
|
||||
// This holds a lot of defaults, but the scene can
|
||||
// override a large portion of the definitions here
|
||||
struct MonsterData {
|
||||
std::string id;
|
||||
std::string anim;
|
||||
std::string weapon;
|
||||
bool aggressive = false;
|
||||
std::string behaviour;
|
||||
float hp;
|
||||
};
|
||||
|
||||
struct SoundData {
|
||||
std::string id; // <type>/<object>/<state>
|
||||
std::string path;
|
||||
bool spatial;
|
||||
};
|
||||
|
||||
class XMLLoader {
|
||||
public:
|
||||
XMLLoader() {}
|
||||
bool loadScenes(const char* sceneFolder);
|
||||
bool loadWeapons(const char* weaponFolder);
|
||||
bool loadAnimations(const char* animationFolder);
|
||||
bool loadTileSets(const char* tileSetFolder);
|
||||
bool loadMaps(const char* mapFolder);
|
||||
XMLLoader() {}
|
||||
bool loadScenes(const char *sceneFolder);
|
||||
bool loadWeapons(const char *weaponFolder);
|
||||
bool loadAnimations(const char *animationFolder);
|
||||
bool loadTileSets(const char *tileSetFolder);
|
||||
bool loadMaps(const char *mapFolder);
|
||||
bool loadMonsters(const char *monsterFolder);
|
||||
// luaLoader is the function we use to load the lua file.
|
||||
// This will be defined in our resource manager
|
||||
bool loadScripts(const char *scriptFolder,
|
||||
std::function<bool(ScriptData *)> luaLoader);
|
||||
bool loadSoundEffects(const char *soundFolder);
|
||||
|
||||
const std::shared_ptr<SceneData> getSceneData(const std::string& id) const {
|
||||
try {
|
||||
return scenes.at(id);
|
||||
}
|
||||
catch (std::exception&) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
const SceneData *getSceneData(const std::string &id) const {
|
||||
try {
|
||||
return scenes.at(id).get();
|
||||
} catch (std::exception &) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const std::shared_ptr<MapData> getMapData(const std::string& name) const {
|
||||
try {
|
||||
return maps.at(name);
|
||||
}
|
||||
catch (std::exception&) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
const ScriptData *getScriptData(const std::string &id) const {
|
||||
try {
|
||||
return scripts.at(id).get();
|
||||
} catch (std::exception &) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const std::shared_ptr<WeaponData> getWeaponData(const std::string& name) const {
|
||||
try {
|
||||
return weapons.at(name);
|
||||
}
|
||||
catch (std::exception&) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
const MapData *getMapData(const std::string &name) const {
|
||||
try {
|
||||
return maps.at(name).get();
|
||||
} catch (std::exception &) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const std::shared_ptr<AnimationData> getAnimationData(const std::string& name) const {
|
||||
try {
|
||||
return animations.at(name);
|
||||
}
|
||||
catch (std::exception&) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
const WeaponData *getWeaponData(const std::string &id) const {
|
||||
try {
|
||||
return weapons.at(id).get();
|
||||
} catch (std::exception &) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const std::shared_ptr<TileSetData> getTileSetData(const std::string& name) const {
|
||||
try {
|
||||
return tileSets.at(name);
|
||||
}
|
||||
catch (std::exception&) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
const AnimationData *getAnimationData(const std::string &id) const {
|
||||
try {
|
||||
return animations.at(id).get();
|
||||
} catch (std::exception &) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// return a full set of animations, may need further optimization.
|
||||
// one idea is when loading animations we create a seperate map that holds each set by their reference, so we can just do a simple,
|
||||
// hash table lookup.
|
||||
std::vector<std::shared_ptr<AnimationData>> getAnimationSet(const std::string& set) const {
|
||||
std::vector<std::shared_ptr<AnimationData>> animSet;
|
||||
animSet.reserve(animations.size());
|
||||
for (const auto& [name, anim] : animations) {
|
||||
if (anim->animSet == set) animSet.push_back(anim);
|
||||
}
|
||||
animSet.shrink_to_fit();
|
||||
return animSet;
|
||||
}
|
||||
const TileSetData *getTileSetData(const std::string &name) const {
|
||||
try {
|
||||
return tileSets.at(name).get();
|
||||
} catch (std::exception &) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const SoundData *getSoundData(const std::string &id) const {
|
||||
try {
|
||||
return sounds.at(id).get();
|
||||
} catch (std::exception &) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// return a full set of animations, may need further optimization.
|
||||
// one idea is when loading animations we create a seperate map that holds
|
||||
// each set by their reference, so we can just do a simple, hash table lookup.
|
||||
std::vector<AnimationData *>
|
||||
getAnimationSet(const std::string &prefix) const {
|
||||
std::vector<AnimationData *> animSet;
|
||||
animSet.reserve(animations.size());
|
||||
for (const auto &[id, anim] : animations) {
|
||||
if (id.starts_with(prefix))
|
||||
animSet.push_back(anim.get());
|
||||
}
|
||||
animSet.shrink_to_fit();
|
||||
return animSet;
|
||||
}
|
||||
|
||||
void clearData() {
|
||||
scenes.clear();
|
||||
weapons.clear();
|
||||
animations.clear();
|
||||
maps.clear();
|
||||
tileSets.clear();
|
||||
}
|
||||
|
||||
void clearData() { scenes.clear(); weapons.clear(); animations.clear(); maps.clear(); tileSets.clear(); }
|
||||
protected:
|
||||
bool loadXmlScene(const char* xmlFile, SceneData* out);
|
||||
bool loadEntityData(const char* xmlFile, SceneData* out);
|
||||
bool loadTile(tinyxml2::XMLElement* tileElement, TileSetData::TileData* out);
|
||||
bool loadObject(tinyxml2::XMLElement* objectElement, TileSetData::TileData::ObjectData* out);
|
||||
bool loadXmlScene(const char *xmlFile, SceneData *out);
|
||||
bool loadEntityData(const char *xmlFile, SceneData *out);
|
||||
bool loadTile(tinyxml2::XMLElement *tileElement, TileSetData::TileData *out);
|
||||
bool loadObject(tinyxml2::XMLElement *objectElement,
|
||||
TileSetData::TileData::ObjectData *out);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::shared_ptr<SceneData>> scenes;
|
||||
std::unordered_map<std::string, std::shared_ptr<WeaponData>> weapons;
|
||||
std::unordered_map<std::string, std::shared_ptr<AnimationData>> animations;
|
||||
std::unordered_map<std::string, std::shared_ptr<MapData>> maps;
|
||||
std::unordered_map<std::string, std::shared_ptr<TileSetData>> tileSets;
|
||||
std::unordered_map<std::string, std::unique_ptr<SceneData>> scenes;
|
||||
std::unordered_map<std::string, std::unique_ptr<ScriptData>> scripts;
|
||||
std::unordered_map<std::string, std::unique_ptr<WeaponData>> weapons;
|
||||
std::unordered_map<std::string, std::unique_ptr<AnimationData>> animations;
|
||||
std::unordered_map<std::string, std::unique_ptr<MonsterData>> monsters;
|
||||
std::unordered_map<std::string, std::unique_ptr<MapData>> maps;
|
||||
std::unordered_map<std::string, std::unique_ptr<TileSetData>> tileSets;
|
||||
std::unordered_map<std::string, std::unique_ptr<SoundData>> sounds;
|
||||
};
|
||||
|
||||
#endif // _H_XMLLOADER_H
|
||||
#endif // _H_XMLLOADER_H
|
||||
|
|
|
|||
|
|
@ -4,78 +4,91 @@
|
|||
#include "utility/raycaster.h"
|
||||
#include "utility/script.h"
|
||||
|
||||
#include <tracy/Tracy.hpp>
|
||||
AI::AI(GameActor *actor, std::unique_ptr<Raycaster> raycaster)
|
||||
: state(AIState::Idle), raycaster(std::move(raycaster)), actor(actor),
|
||||
lastGCTime(std::chrono::high_resolution_clock::now()), GCTimeout(3) {}
|
||||
|
||||
/*
|
||||
* The behavior script works off of three different states, idle, patrol and
|
||||
* alert. When the script is idle, this is */
|
||||
void AI::attachBehaviourScript(std::unique_ptr<AIScript> behaviour) {
|
||||
// passing out instance of raycaster and this AI into our scripting api
|
||||
// pay special attention each ai script has control of only their own instance
|
||||
// of ai!
|
||||
this->behaviour = std::move(behaviour);
|
||||
this->behaviour->lua["raycaster"] =
|
||||
sol::make_reference(this->behaviour->lua, raycaster.get());
|
||||
this->behaviour->lua["ai"] = sol::make_reference(this->behaviour->lua, this);
|
||||
|
||||
AI::AI(const std::shared_ptr<GameActor>& actor, const std::shared_ptr<Raycaster>& raycaster)
|
||||
: actor(actor), raycaster(raycaster), state(AIState::Idle),
|
||||
lastGCTime(std::chrono::high_resolution_clock::now()), GCTimeout(3)
|
||||
{}
|
||||
|
||||
void AI::attachBehaviourScript(const std::shared_ptr<AIScript>& behaviour)
|
||||
{
|
||||
// passing out instance of raycaster and this AI into our scripting api
|
||||
// pay special attention each ai script has control of only their own instance of ai!
|
||||
this->behaviour = behaviour;
|
||||
this->behaviour->lua["raycaster"] = raycaster;
|
||||
this->behaviour->lua["ai"] = shared_from_this();
|
||||
|
||||
if (this->behaviour->lua["idle"].valid())
|
||||
idleFunc = this->behaviour->lua["idle"];
|
||||
if (this->behaviour->lua["patrol"].valid())
|
||||
patrolFunc = this->behaviour->lua["patrol"];
|
||||
if (this->behaviour->lua["alert"].valid())
|
||||
alertFunc = this->behaviour->lua["alert"];
|
||||
idleFunc = this->behaviour->lua["idle"];
|
||||
patrolFunc = this->behaviour->lua["patrol"];
|
||||
alertFunc = this->behaviour->lua["alert"];
|
||||
}
|
||||
|
||||
void AI::update()
|
||||
{
|
||||
try {
|
||||
switch (state)
|
||||
{
|
||||
case AIState::Idle:
|
||||
if (idleFunc.valid())
|
||||
{
|
||||
auto result = idleFunc(actor, target);
|
||||
if (!result.valid())
|
||||
{
|
||||
sol::error err = result;
|
||||
std::cerr << "lua error: " << err.what() << std::endl;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AIState::Patrol:
|
||||
if (patrolFunc.valid())
|
||||
{
|
||||
auto result = patrolFunc(actor, target);
|
||||
if (!result.valid())
|
||||
{
|
||||
sol::error err = result;
|
||||
std::cerr << "lua error: " << err.what() << std::endl;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AIState::Alert:
|
||||
if (alertFunc.valid())
|
||||
{
|
||||
auto result = alertFunc(actor, target);
|
||||
if (!result.valid())
|
||||
{
|
||||
sol::error err = result;
|
||||
std::cerr << "lua error: " << err.what() << std::endl;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
std::chrono::high_resolution_clock::time_point curTime = std::chrono::high_resolution_clock::now();
|
||||
if (curTime - lastGCTime >= GCTimeout)
|
||||
{
|
||||
behaviour->lua.collect_gc();
|
||||
lastGCTime = curTime;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Error during AI update: " << e.what() << std::endl;
|
||||
state = AIState::Idle;
|
||||
}
|
||||
}
|
||||
void AI::update() {
|
||||
try {
|
||||
// If our ai doesn't have a reference to the actor it's controlling it will
|
||||
// do nothing. If our ai script does have an actor but no target, it will
|
||||
// default to its idle state.
|
||||
if (actor && target) {
|
||||
switch (state) {
|
||||
case AIState::Idle:
|
||||
if (idleFunc.valid()) {
|
||||
auto result = idleFunc(actor, target);
|
||||
if (!result.valid()) {
|
||||
sol::error err = result;
|
||||
LOG(ERROR, "Lua error: {}", err.what(), NULL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AIState::Patrol:
|
||||
if (patrolFunc.valid()) {
|
||||
auto result = patrolFunc(actor, target);
|
||||
if (!result.valid()) {
|
||||
sol::error err = result;
|
||||
LOG(ERROR, "Lua error: {}", err.what(), NULL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AIState::Alert:
|
||||
if (alertFunc.valid()) {
|
||||
auto result = alertFunc(actor, target);
|
||||
if (!result.valid()) {
|
||||
sol::error err = result;
|
||||
LOG(ERROR, "Lua error: {}", err.what(), NULL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (actor) {
|
||||
if (idleFunc.valid()) {
|
||||
auto result = idleFunc(actor, nullptr);
|
||||
if (!result.valid()) {
|
||||
sol::error err = result;
|
||||
LOG(ERROR, "Lua Error: {}", err.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Collect garbage just to be sure.
|
||||
std::chrono::high_resolution_clock::time_point curTime =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
if (curTime - lastGCTime >= GCTimeout) {
|
||||
behaviour->lua.collect_gc();
|
||||
lastGCTime = curTime;
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
LOG(ERROR, "Problem occured during AI update: {}", e.what());
|
||||
state = AIState::Idle;
|
||||
}
|
||||
}
|
||||
|
||||
AI::~AI() {
|
||||
// Just set all of the references to null, given they are smart pointers, they
|
||||
// are freed when no longer used.
|
||||
behaviour->lua["raycaster"] = sol::nil;
|
||||
behaviour->lua["ai"] = sol::nil;
|
||||
behaviour->lua["idle"] = sol::nil;
|
||||
behaviour->lua["patrol"] = sol::nil;
|
||||
behaviour->lua["alert"] = sol::nil;
|
||||
behaviour->lua.collect_gc();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,31 @@
|
|||
#include "gameplay/camera.h"
|
||||
|
||||
void Camera::update(double deltaTime)
|
||||
{
|
||||
if (target == nullptr)
|
||||
return;
|
||||
float smoothingFactor = 5.0f;
|
||||
//if (glm::distance(target->getCenter(), getCenterPos()) > 20.f)
|
||||
position += (target->getCenter() - getCenterPos()) * smoothingFactor * static_cast<float>(deltaTime);
|
||||
// follow our target set using the setTarget. If there is no target set the
|
||||
// camera will not move.
|
||||
void Camera::update(double deltaTime) {
|
||||
if (target == nullptr)
|
||||
return;
|
||||
float smoothingFactor = 5.0f;
|
||||
// if (glm::distance(target->getCenter(), getCenterPos()) > 20.f)
|
||||
position += (target->getCenter() - getCenterPos()) * smoothingFactor *
|
||||
static_cast<float>(deltaTime);
|
||||
}
|
||||
|
||||
glm::mat4 Camera::getViewMatrix()
|
||||
{
|
||||
return glm::lookAt(position, position + front, up);
|
||||
glm::mat4 Camera::getViewMatrix() {
|
||||
return glm::lookAt(position, position + front, up);
|
||||
}
|
||||
|
||||
glm::mat4 Camera::getProjectionMatrix()
|
||||
{
|
||||
return glm::ortho(0.f, viewPortW, viewPortH, 0.f);
|
||||
glm::mat4 Camera::getProjectionMatrix() {
|
||||
return glm::ortho(0.f, viewPortW, viewPortH, 0.f);
|
||||
}
|
||||
|
||||
const glm::vec3 Camera::worldToLocal(const glm::vec3& worldCoordinates)
|
||||
{
|
||||
//return worldCoordinates - position;
|
||||
return glm::vec3(getViewMatrix() * glm::vec4(worldCoordinates, 1.0f));
|
||||
void Camera::setViewportSize(float width, float height) {
|
||||
viewPortW = width;
|
||||
viewPortH = height;
|
||||
}
|
||||
|
||||
// The local coordinates are the corrdinates relative to the camera.
|
||||
const glm::vec3 Camera::worldToLocal(const glm::vec3 &worldCoordinates) {
|
||||
// return worldCoordinates - position;
|
||||
return glm::vec3(getViewMatrix() * glm::vec4(worldCoordinates, 1.0f));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "gameplay/entity.h"
|
||||
#include "gameplay/camera.h"
|
||||
#include "gameplay/physics.h"
|
||||
|
||||
void Entity::setPosition(const glm::vec3& position)
|
||||
|
|
@ -33,6 +32,7 @@ void Entity::flip()
|
|||
flipped = !flipped;
|
||||
}
|
||||
|
||||
// Add physics component implies more than one can be attached to an entity...
|
||||
void Entity::addPhysicsComponent(const std::shared_ptr<PhysicsComponent>& physics)
|
||||
{
|
||||
this->physics = physics;
|
||||
|
|
@ -40,29 +40,35 @@ void Entity::addPhysicsComponent(const std::shared_ptr<PhysicsComponent>& physic
|
|||
|
||||
void Entity::update(double deltaTime)
|
||||
{
|
||||
if (physics && physics->rigidBody.velocity != glm::vec3(0.0f))
|
||||
// If our entity has a physics component attached and that they're moving,
|
||||
// we will update the entity position to match the rigidBody position.
|
||||
if (physics && physics->rigidBody.velocity != glm::vec2(0.0f))
|
||||
{
|
||||
position = physics->rigidBody.position;
|
||||
position = glm::vec3(physics->rigidBody.position, 0.f);
|
||||
updateModelMatrix();
|
||||
deltaPosition = glm::vec3(0.0f);
|
||||
}
|
||||
else if (!physics)
|
||||
{
|
||||
// In the case that the entity does not have a physics component we will handle movement on the entity itself.
|
||||
position += deltaPosition * 1.f;
|
||||
updateModelMatrix();
|
||||
deltaPosition = glm::vec3(0.f);
|
||||
}
|
||||
}
|
||||
|
||||
// The entity will only need to update its model matrix and the flipped flag on its attached shader.
|
||||
// The shader and sprite component will handle the drawing.
|
||||
void Entity::draw()
|
||||
{
|
||||
//glm::mat4 mvp = camera->getProjectionMatrix() * camera->getViewMatrix() * modelMatrix;
|
||||
editUniform("model", modelMatrix);
|
||||
editUniform("flip", flipped);
|
||||
}
|
||||
|
||||
void Entity::updateModelMatrix()
|
||||
{
|
||||
// Quick sanity check to make sure our Z position is zero. Since we are in 2D
|
||||
position.z = 0.f;
|
||||
glm::mat4 rotationMat = (isRotatable) ? glm::rotate(glm::mat4(1.f), glm::radians(rotation), glm::vec3(0.0f, 0.0f, 1.0f)) : glm::mat4(1.0f);
|
||||
glm::mat4 translation = glm::translate(glm::mat4(1.f), position);
|
||||
modelMatrix =
|
||||
|
|
|
|||
|
|
@ -1,105 +1,138 @@
|
|||
#include "gameplay/game.h"
|
||||
#include "gameplay/input.h"
|
||||
#include "gameplay/scene.h"
|
||||
/*due for possible removal!*/
|
||||
#include "gameplay/gameactor.h"
|
||||
#include "gameplay/weapons/weapon.h"
|
||||
/*-------------------------*/
|
||||
|
||||
#include "utility/command.h"
|
||||
#include "utility/resourcemanager.h"
|
||||
#include "utility/ftfont.h"
|
||||
#include "utility/logger.h"
|
||||
#include "utility/resourcemanager.h"
|
||||
|
||||
#include "graphics/glwindow.h"
|
||||
|
||||
#include <iostream>
|
||||
#include "sound/engine.h"
|
||||
|
||||
#include <SDL_video.h>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <memory>
|
||||
#include <utility/events.h>
|
||||
|
||||
bool Game::init()
|
||||
{
|
||||
window = std::make_shared<GLWindow>("Yupple Mayham", 800, 600);
|
||||
bool Game::init() {
|
||||
window = std::make_shared<GLWindow>("Yupple Mayham", 1024, 768);
|
||||
|
||||
if (!window->Init())
|
||||
ERROR_LOG("Failed to init GLWindow: {}", SDL_GetError());
|
||||
if (!window->Init())
|
||||
ERROR_LOG("Failed to init GLWindow: {}", SDL_GetError());
|
||||
|
||||
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
|
||||
ERROR_LOG("Failed to load GLLoader");
|
||||
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
|
||||
ERROR_LOG("Failed to load GLLoader", NULL);
|
||||
|
||||
#if _DEBUG
|
||||
LOG_LEVEL(DEBUG);
|
||||
LOG_LEVEL(DEBUG);
|
||||
#else
|
||||
LOG_LEVEL(INFO);
|
||||
LOG_LEVEL(INFO);
|
||||
#endif
|
||||
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
glViewport(0, 0, window->width(), window->height());
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
glViewport(0, 0, window->Width(), window->Height());
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// For now we'll init default bindings, but as we move forward controls will be set by the player and saved in controls.xml
|
||||
inputHandler = std::make_shared<InputHandler>();
|
||||
inputHandler->bindKeyCommand(SDLK_w, 0, new MoveUpCommand());
|
||||
inputHandler->bindKeyCommand(SDLK_a, 0, new MoveLeftCommand());
|
||||
inputHandler->bindKeyCommand(SDLK_s, 0, new MoveDownCommand());
|
||||
inputHandler->bindKeyCommand(SDLK_d, 0, new MoveRightCommand());
|
||||
// For now we'll init default bindings, but as we move forward controls will
|
||||
// be set by the player and saved in controls.xml
|
||||
inputHandler = std::make_shared<InputHandler>();
|
||||
inputHandler->bindKeyCommand(SDLK_w, 0, std::make_unique<MoveUpCommand>());
|
||||
inputHandler->bindKeyCommand(SDLK_a, 0, std::make_unique<MoveLeftCommand>());
|
||||
inputHandler->bindKeyCommand(SDLK_s, 0, std::make_unique<MoveDownCommand>());
|
||||
inputHandler->bindKeyCommand(SDLK_d, 0, std::make_unique<MoveRightCommand>());
|
||||
|
||||
inputHandler->bindMouseCommand(MOUSE_BUTTON_LEFT, 100, new ShootCommand());
|
||||
inputHandler->bindMouseMotion(new FollowMouseCommand());
|
||||
inputHandler->bindMouseScroll(new CycleCommand());
|
||||
inputHandler->bindMouseCommand(MOUSE_BUTTON_LEFT, 100,
|
||||
std::make_unique<ShootCommand>());
|
||||
inputHandler->bindMouseMotion(std::make_unique<FollowMouseCommand>());
|
||||
inputHandler->bindMouseScroll(std::make_unique<CycleCommand>());
|
||||
|
||||
game_state |= GAME_RUNNING;
|
||||
globalEventManager = std::make_shared<EventManager>();
|
||||
resourceManager = std::make_shared<ResourceManager>();
|
||||
renderer = std::make_shared<Renderer>(resourceManager);
|
||||
renderer->hookEventManager(globalEventManager);
|
||||
textHandler = std::make_shared<Text>();
|
||||
if (!textHandler->loadFonts("fonts"))
|
||||
return false;
|
||||
textHandler->setProjectionMatrix(glm::ortho(0.f, static_cast<float>(window->width()), 0.f, static_cast<float>(window->height())));
|
||||
return true;
|
||||
game_state |= GAME_RUNNING;
|
||||
globalEventManager = std::make_shared<EventManager>();
|
||||
resourceManager = std::make_shared<ResourceManager>();
|
||||
renderer = std::make_shared<Renderer>(resourceManager, window);
|
||||
audioEngine = std::make_shared<AudioEngine>(resourceManager);
|
||||
camera = std::make_unique<Camera>(static_cast<float>(window->Width()),
|
||||
static_cast<float>(window->Height()));
|
||||
audioEngine->hookEventManager(globalEventManager);
|
||||
/* Testing */
|
||||
audioEngine->pushMusic("music/short_song.ogg");
|
||||
audioEngine->pushMusic("music/bright.ogg");
|
||||
audioEngine->pushMusic("music/main_song.ogg");
|
||||
audioEngine->playMusic();
|
||||
/* */
|
||||
renderer->hookEventManager(globalEventManager);
|
||||
textHandler = std::make_shared<Text>();
|
||||
if (!textHandler->loadFonts("fonts"))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const unsigned Game::getWindowWidth() const { return window->width(); }
|
||||
const unsigned Game::getWindowHeight() const { return window->height(); }
|
||||
const unsigned Game::getWindowWidth() const { return window->Width(); }
|
||||
const unsigned Game::getWindowHeight() const { return window->Height(); }
|
||||
|
||||
bool Game::loadDebugScene()
|
||||
{
|
||||
currentScene = std::make_shared<Scene>(SCENE_SHOOTER, resourceManager, globalEventManager);
|
||||
currentScene->init();
|
||||
if (currentScene->getPlayer() == nullptr)
|
||||
return false;
|
||||
inputHandler->setActor(currentScene->getPlayer().get());
|
||||
return true;
|
||||
bool Game::loadDebugScene() {
|
||||
currentScene = std::make_shared<Scene>(SCENE_SHOOTER, resourceManager,
|
||||
globalEventManager);
|
||||
currentScene->init();
|
||||
audioEngine->hookSceneManager(currentScene->getEventManager());
|
||||
if (currentScene->getPlayer() == nullptr)
|
||||
return false;
|
||||
inputHandler->setActor(currentScene->getPlayer().get());
|
||||
camera->setTarget(currentScene->getPlayer().get());
|
||||
return true;
|
||||
}
|
||||
|
||||
void Game::handleInput(SDL_Event& e)
|
||||
{
|
||||
inputHandler->captureInput(e);
|
||||
inputHandler->handleInput();
|
||||
void Game::captureInput(SDL_Event &e) {
|
||||
inputHandler->captureInput(e);
|
||||
if (e.type == SDL_WINDOWEVENT_RESIZED) {
|
||||
size_t width = static_cast<size_t>(e.window.data1);
|
||||
size_t height = static_cast<size_t>(e.window.data2);
|
||||
globalEventManager->notify<WindowResizeEvent>({width, height});
|
||||
window->resizeWindow(width, height);
|
||||
camera->setViewportSize(static_cast<float>(e.window.data1),
|
||||
static_cast<float>(e.window.data2));
|
||||
}
|
||||
}
|
||||
|
||||
void Game::update(double deltaTime)
|
||||
{
|
||||
if (currentScene)
|
||||
currentScene->update(deltaTime);
|
||||
void Game::executeInputs() {
|
||||
inputHandler->handleInput();
|
||||
inputHandler->executeCommands();
|
||||
}
|
||||
|
||||
void Game::render()
|
||||
{
|
||||
glClearColor(0.05f, 0.25f, 0.05f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (currentScene)
|
||||
{
|
||||
currentScene->render(renderer);
|
||||
/*Debug ammo indicator*/
|
||||
textHandler->DrawText("comic.ttf", std::to_string(currentScene->getPlayer()->getHeldWeapon()->getMagazine()), glm::vec2(10, 10), 0.5f);
|
||||
textHandler->DrawText("comic.ttf", "/", glm::vec2(50, 10), 0.5f);
|
||||
textHandler->DrawText("comic.ttf", std::to_string(currentScene->getPlayer()->getHeldWeapon()->getAmmo()), glm::vec2(90, 10), 0.5f);
|
||||
}
|
||||
|
||||
window->swap();
|
||||
void Game::update(double deltaTime) {
|
||||
if (currentScene) {
|
||||
currentScene->update(deltaTime);
|
||||
if (auto player = currentScene->getPlayer()) {
|
||||
audioEngine->updateListener(player->getPosition());
|
||||
player->setLocalPosition(camera->worldToLocal(player->getPosition()));
|
||||
}
|
||||
}
|
||||
camera->update(deltaTime);
|
||||
audioEngine->poll();
|
||||
}
|
||||
|
||||
void Game::render() {
|
||||
glClearColor(0.05f, 0.25f, 0.05f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
renderer->setProjAndViewMatrix(camera->getProjectionMatrix(),
|
||||
camera->getViewMatrix());
|
||||
|
||||
if (currentScene)
|
||||
currentScene->render(renderer);
|
||||
|
||||
window->swap();
|
||||
}
|
||||
|
||||
void Game::quit() { game_state = GAME_QUITTING; }
|
||||
|
||||
Game::~Game() {
|
||||
if (audioEngine) {
|
||||
audioEngine->killMusic();
|
||||
}
|
||||
resourceManager->clearResources();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,150 +3,181 @@
|
|||
#include "gameplay/physics.h"
|
||||
#include "gameplay/scene.h"
|
||||
#include "gameplay/weapons/weapon.h"
|
||||
#include "utility/events.h"
|
||||
#include "utility/direction.h"
|
||||
#include "utility/component.h"
|
||||
#include "utility/direction.h"
|
||||
#include "utility/events.h"
|
||||
#include <memory>
|
||||
|
||||
GameActor::~GameActor() { }
|
||||
// The components are smart pointer references to resources owned by the
|
||||
// resource manager. So the GameActor is not responsible for cleaning resources.
|
||||
GameActor::~GameActor() {}
|
||||
|
||||
void GameActor::addComponent(std::unique_ptr<Component> component)
|
||||
{
|
||||
component->ownerid = entityid;
|
||||
components.push_back(std::move(component));
|
||||
// Adding a component also means attaching the id of the entity to the
|
||||
// component, so it's aware of who owns it. This is important for event handling
|
||||
void GameActor::addComponent(std::unique_ptr<Component> component) {
|
||||
component->ownerid = entityid;
|
||||
components.push_back(std::move(component));
|
||||
}
|
||||
|
||||
const std::shared_ptr<Weapon> GameActor::getHeldWeapon() const
|
||||
{
|
||||
return (weapons.empty() || currentWeaponIndex >= weapons.size()) ? nullptr : weapons[currentWeaponIndex];
|
||||
Weapon *GameActor::getHeldWeapon() const {
|
||||
return (weapons.empty() || currentWeaponIndex >= weapons.size())
|
||||
? nullptr
|
||||
: weapons[currentWeaponIndex].get();
|
||||
}
|
||||
|
||||
void GameActor::pickupWeapon(std::shared_ptr<Weapon> weapon)
|
||||
{
|
||||
weapon->setWielder(this);
|
||||
if (auto eventManager = sceneContext->getEventManager().lock()) {
|
||||
weapon->hookEventManager(eventManager);
|
||||
}
|
||||
weapons.push_back(weapon);
|
||||
// wield the newly picked up weapon.
|
||||
getHeldWeapon()->putaway();
|
||||
currentWeaponIndex = weapons.size() - 1;
|
||||
getHeldWeapon()->wield();
|
||||
std::span<Weapon *> GameActor::getAllWeapons() { return weaponCache; }
|
||||
|
||||
// Keep a seperate vector that is used as the cache for external calls to
|
||||
// getAllWeapons()
|
||||
void GameActor::pickupWeapon(std::unique_ptr<Weapon> weapon) {
|
||||
weapon->setWielder(this);
|
||||
if (auto eventManager = sceneContext->getEventManager().lock()) {
|
||||
weapon->hookEventManager(eventManager);
|
||||
}
|
||||
weaponCache.push_back(weapon.get());
|
||||
weapons.push_back(std::move(weapon));
|
||||
// wield the newly picked up weapon.
|
||||
getHeldWeapon()->putaway();
|
||||
currentWeaponIndex = weapons.size() - 1;
|
||||
getHeldWeapon()->wield();
|
||||
}
|
||||
|
||||
void GameActor::setRotation(const float& rotation)
|
||||
{
|
||||
if (!isRotatable) {
|
||||
if (auto eventManager = sceneContext->getEventManager().lock()) {
|
||||
Direction newDir = getDirectionFromRotation(rotation);
|
||||
if (getDirectionFromRotation(this->rotation) != newDir)
|
||||
eventManager->notify(std::make_shared<DirectionChangeEvent>(entityid, newDir));
|
||||
}
|
||||
}
|
||||
this->rotation = rotation;
|
||||
updateModelMatrix();
|
||||
void GameActor::setRotation(const float &rotation) {
|
||||
// Any attached animation component would be interested if their owner needs
|
||||
// their sprite swapped
|
||||
if (!isRotatable) {
|
||||
if (auto eventManager = sceneContext->getEventManager().lock()) {
|
||||
Direction newDir = getDirectionFromRotation(rotation);
|
||||
if (getDirectionFromRotation(this->rotation) != newDir)
|
||||
eventManager->notify<DirectionChangeEvent>({entityid, newDir});
|
||||
}
|
||||
}
|
||||
this->rotation = rotation;
|
||||
updateModelMatrix();
|
||||
}
|
||||
|
||||
void GameActor::update(double deltaTime)
|
||||
{
|
||||
Entity::update(deltaTime);
|
||||
void GameActor::update(double deltaTime) {
|
||||
Entity::update(deltaTime);
|
||||
|
||||
for (const auto& component : components)
|
||||
component->update();
|
||||
for (const auto& weapon : weapons)
|
||||
weapon->update(deltaTime);
|
||||
for (const auto &component : components)
|
||||
component->update();
|
||||
for (const auto &weapon : weapons)
|
||||
weapon->update(deltaTime);
|
||||
|
||||
if (isMoving && !wasMoving)
|
||||
{
|
||||
if (auto event = sceneContext->getEventManager().lock()) {
|
||||
event->notify(std::make_shared<EntityMoveEvent>(entityid));
|
||||
}
|
||||
wasMoving = true;
|
||||
}
|
||||
else if (!isMoving && wasMoving)
|
||||
{
|
||||
if (auto event = sceneContext->getEventManager().lock()) {
|
||||
event->notify(std::make_shared<EntityStopEvent>(entityid));
|
||||
}
|
||||
wasMoving = false;
|
||||
}
|
||||
isMoving = false;
|
||||
// Not the cleanest solution, but this is to make sure the animation isn't
|
||||
// starting to move over and over.
|
||||
if (isMoving && !wasMoving) {
|
||||
if (auto event = sceneContext->getEventManager().lock()) {
|
||||
event->notify<EntityMoveEvent>({entityid});
|
||||
}
|
||||
wasMoving = true;
|
||||
} else if (!isMoving && wasMoving) {
|
||||
if (auto event = sceneContext->getEventManager().lock()) {
|
||||
event->notify<EntityStopEvent>({entityid});
|
||||
}
|
||||
wasMoving = false;
|
||||
}
|
||||
isMoving = false;
|
||||
}
|
||||
|
||||
void GameActor::draw()
|
||||
{
|
||||
Entity::draw();
|
||||
|
||||
// regular loop through components, but if the component returns true to kill, we erase it.
|
||||
// Components are always assumed to be smart pointers!
|
||||
for (const auto& component : components)
|
||||
{
|
||||
component->bind();
|
||||
component->render();
|
||||
}
|
||||
//for (auto& weapon : weapons)
|
||||
// weapon->draw();
|
||||
void GameActor::draw() {
|
||||
Entity::draw();
|
||||
for (const auto &component : components) {
|
||||
component->bind();
|
||||
component->render();
|
||||
}
|
||||
}
|
||||
|
||||
void GameActor::moveUp() { if (physics) physics->rigidBody.applyForce(glm::vec3( 0.f,-1.f, 0.f), 1500.25f); isMoving = true; }
|
||||
void GameActor::moveDown() { if (physics) physics->rigidBody.applyForce(glm::vec3( 0.f, 1.f, 0.f), 1500.25f); isMoving = true; }
|
||||
void GameActor::moveLeft() { if (physics) physics->rigidBody.applyForce(glm::vec3(-1.f, 0.f, 0.f), 1500.25f); isMoving = true; }
|
||||
void GameActor::moveRight(){ if (physics) physics->rigidBody.applyForce(glm::vec3( 1.f, 0.f, 0.f), 1500.25f); isMoving = true; }
|
||||
void GameActor::moveUp() {
|
||||
if (physics)
|
||||
physics->rigidBody.applyForce(glm::vec3(0.f, -1.f, 0.f), 1500.25f);
|
||||
isMoving = true;
|
||||
}
|
||||
void GameActor::moveDown() {
|
||||
if (physics)
|
||||
physics->rigidBody.applyForce(glm::vec3(0.f, 1.f, 0.f), 1500.25f);
|
||||
isMoving = true;
|
||||
}
|
||||
void GameActor::moveLeft() {
|
||||
if (physics)
|
||||
physics->rigidBody.applyForce(glm::vec3(-1.f, 0.f, 0.f), 1500.25f);
|
||||
isMoving = true;
|
||||
}
|
||||
void GameActor::moveRight() {
|
||||
if (physics)
|
||||
physics->rigidBody.applyForce(glm::vec3(1.f, 0.f, 0.f), 1500.25f);
|
||||
isMoving = true;
|
||||
}
|
||||
|
||||
// top-down shooter mode controls
|
||||
void GameActor::fireWeapon()const {
|
||||
if (auto& weapon = getHeldWeapon()) {
|
||||
if (weapon->shoot()) {
|
||||
if (sceneContext->getPlayerID() == entityid) {
|
||||
if (auto gEvent = sceneContext->getGlobalEventManager().lock()) {
|
||||
gEvent->notify(std::make_shared<ScreenShakeEvent>(0.01f, 0.8f));
|
||||
gEvent->notify(std::make_shared<ScreenBlurEvent>(1.f, 0.8f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void GameActor::fireWeapon() const {
|
||||
if (auto weapon = getHeldWeapon()) {
|
||||
if (weapon->shoot()) {
|
||||
// If it's the player that is shooting apply a shake and blur effect to
|
||||
// give the shots some weight
|
||||
if (sceneContext->getPlayerID() == entityid) {
|
||||
if (auto gEvent = sceneContext->getGlobalEventManager().lock()) {
|
||||
gEvent->notify<ScreenShakeEvent>({0.01f, 0.8f});
|
||||
gEvent->notify<ScreenBlurEvent>({1.f, 0.8f});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameActor::cycleUpWeapons() {
|
||||
if (!weapons.empty()) {
|
||||
weapons[currentWeaponIndex]->putaway();
|
||||
currentWeaponIndex = (currentWeaponIndex + 1) % weapons.size();
|
||||
weapons[currentWeaponIndex]->wield();
|
||||
}
|
||||
void GameActor::cycleUpWeapons() {
|
||||
if (!weapons.empty()) {
|
||||
weapons[currentWeaponIndex]->putaway();
|
||||
currentWeaponIndex = (currentWeaponIndex + 1) % weapons.size();
|
||||
weapons[currentWeaponIndex]->wield();
|
||||
}
|
||||
}
|
||||
void GameActor::cycleDownWeapons() {
|
||||
if (!weapons.empty()) {
|
||||
weapons[currentWeaponIndex]->putaway();
|
||||
currentWeaponIndex = (currentWeaponIndex + weapons.size() - 1) % weapons.size();
|
||||
weapons[currentWeaponIndex]->wield();
|
||||
}
|
||||
if (!weapons.empty()) {
|
||||
weapons[currentWeaponIndex]->putaway();
|
||||
currentWeaponIndex =
|
||||
(currentWeaponIndex + weapons.size() - 1) % weapons.size();
|
||||
weapons[currentWeaponIndex]->wield();
|
||||
}
|
||||
}
|
||||
void GameActor::cycleWeapons(const MouseState& mouse_state)
|
||||
{
|
||||
if (mouse_state.scroll < 0)
|
||||
cycleUpWeapons();
|
||||
else if (mouse_state.scroll > 0)
|
||||
cycleDownWeapons();
|
||||
void GameActor::cycleWeapons(const MouseState &mouse_state) {
|
||||
if (mouse_state.scroll < 0)
|
||||
cycleUpWeapons();
|
||||
else if (mouse_state.scroll > 0)
|
||||
cycleDownWeapons();
|
||||
}
|
||||
void GameActor::followMouse(const MouseState& mouse_state)
|
||||
{
|
||||
glm::vec2 direction = glm::vec2(mouse_state.x, mouse_state.y) - glm::vec2(localPosition.x + 0.5f * scale.x, localPosition.y + 0.5f * scale.y);
|
||||
float newRotation = glm::degrees(glm::atan(direction.y, direction.x));
|
||||
if (getDirectionFromRotation(rotation) != getDirectionFromRotation(newRotation)) {
|
||||
if (auto event = sceneContext->getEventManager().lock()) {
|
||||
event->notify(std::make_shared<DirectionChangeEvent>(entityid, getDirectionFromRotation(newRotation)));
|
||||
}
|
||||
}
|
||||
//setRotation(glm::degrees(glm::atan(direction.y, direction.x)));
|
||||
this->rotation = newRotation;
|
||||
void GameActor::followMouse(const MouseState &mouse_state) {
|
||||
glm::vec2 direction = glm::vec2(mouse_state.x, mouse_state.y) -
|
||||
glm::vec2(localPosition.x, localPosition.y);
|
||||
float newRotation = glm::degrees(glm::atan(direction.y, direction.x));
|
||||
if (getDirectionFromRotation(rotation) !=
|
||||
getDirectionFromRotation(newRotation)) {
|
||||
if (auto event = sceneContext->getEventManager().lock()) {
|
||||
event->notify<DirectionChangeEvent>(
|
||||
{entityid, getDirectionFromRotation(newRotation)});
|
||||
}
|
||||
}
|
||||
// setRotation(glm::degrees(glm::atan(direction.y, direction.x)));
|
||||
this->rotation = newRotation;
|
||||
}
|
||||
void GameActor::strafeLeft() { position.x += sin(glm::radians(rotation)) * speed; position.y -= cos(glm::radians(rotation)) * speed; }
|
||||
void GameActor::strafeRight() { position.x -= sin(glm::radians(rotation)) * speed; position.y += cos(glm::radians(rotation)) * speed; }
|
||||
void GameActor::moveBackward() { position.x -= cos(glm::radians(rotation)) * speed; position.y -= sin(glm::radians(rotation)) * speed; }
|
||||
void GameActor::moveForward() {
|
||||
if (physics) {
|
||||
physics->rigidBody.velocity.x += cos(glm::radians(rotation)) * speed;
|
||||
physics->rigidBody.velocity.y += sin(glm::radians(rotation)) * speed;
|
||||
}
|
||||
isMoving = true;
|
||||
void GameActor::strafeLeft() {
|
||||
position.x += sin(glm::radians(rotation)) * speed;
|
||||
position.y -= cos(glm::radians(rotation)) * speed;
|
||||
}
|
||||
void GameActor::strafeRight() {
|
||||
position.x -= sin(glm::radians(rotation)) * speed;
|
||||
position.y += cos(glm::radians(rotation)) * speed;
|
||||
}
|
||||
void GameActor::moveBackward() {
|
||||
position.x -= cos(glm::radians(rotation)) * speed;
|
||||
position.y -= sin(glm::radians(rotation)) * speed;
|
||||
}
|
||||
void GameActor::moveForward() { // More than likely the most useful of the
|
||||
// movement commands so far. But this could
|
||||
// change in the future
|
||||
if (physics) {
|
||||
physics->rigidBody.velocity.x += cos(glm::radians(rotation)) * speed;
|
||||
physics->rigidBody.velocity.y += sin(glm::radians(rotation)) * speed;
|
||||
}
|
||||
isMoving = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,53 @@
|
|||
#include "gameplay/input.h"
|
||||
#include "utility/command.h"
|
||||
#include "gameplay/gameactor.h"
|
||||
|
||||
#include <SDL_timer.h>
|
||||
|
||||
void InputHandler::handleInput()
|
||||
{
|
||||
if (!actor) return;
|
||||
Uint32 currentTime = SDL_GetTicks();
|
||||
// check for bound keys that were pressed,
|
||||
// next check if the hasn't been executed within the amount specified in delay
|
||||
// if not execute the command and set lastExecution to currentTime
|
||||
for (auto& [key, command] : keyCommands)
|
||||
{
|
||||
if (keys[key] == true)
|
||||
{
|
||||
if (currentTime - command.lastExecution >= command.delay)
|
||||
{
|
||||
command.cmd->execute(*actor);
|
||||
command.lastExecution = currentTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Same with the mouse, for this context we'll be checking for motion events and for click events
|
||||
for (auto& [button, command] : mouseCommands)
|
||||
{
|
||||
if (mouseButtons[button] == true)
|
||||
{
|
||||
if (currentTime - command.lastExecution >= command.delay)
|
||||
{
|
||||
command.cmd->execute(*actor, mouse_state);
|
||||
command.lastExecution = currentTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mouseMotionCommand)
|
||||
mouseMotionCommand->execute(*actor, mouse_state);
|
||||
if (mouseScrollCommand)
|
||||
mouseScrollCommand->execute(*actor, mouse_state);
|
||||
mouse_state.scroll = 0.0f; // clear mouse scroll since we have handled the event.
|
||||
void InputHandler::handleInput() {
|
||||
// If our input handler is not attached to an actor we return.
|
||||
// This will be due for change given we will need to handle input during menus
|
||||
if (!actor)
|
||||
return;
|
||||
Uint32 currentTime = SDL_GetTicks();
|
||||
// check for bound keys that were pressed,
|
||||
// next check if the hasn't been executed within the amount specified in delay
|
||||
// if not execute the command and set lastExecution to currentTime
|
||||
for (auto &[key, command] : keyCommands) {
|
||||
if (keys[key] == true) {
|
||||
if (currentTime - command.lastExecution >= command.delay) {
|
||||
commandQueue.push_back(command.cmd.get());
|
||||
command.lastExecution = currentTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Same with the mouse, for this context we'll be checking for motion events
|
||||
// and for click events
|
||||
for (auto &[button, command] : mouseCommands) {
|
||||
if (mouseButtons[button] == true) {
|
||||
if (currentTime - command.lastExecution >= command.delay) {
|
||||
mouseQueue.push_back(command.cmd.get());
|
||||
command.lastExecution = currentTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mouseMotionCommand)
|
||||
mouseMotionCommand->execute(*actor, mouse_state);
|
||||
if (mouseScrollCommand)
|
||||
mouseScrollCommand->execute(*actor, mouse_state);
|
||||
mouse_state.scroll =
|
||||
0.0f; // clear mouse scroll since we have handled the event.
|
||||
}
|
||||
|
||||
// Executes every captured command during the frame
|
||||
void InputHandler::executeCommands() {
|
||||
for (auto &command : commandQueue) {
|
||||
command->execute(*actor);
|
||||
}
|
||||
for (auto &mouse : mouseQueue) {
|
||||
mouse->execute(*actor, mouse_state);
|
||||
}
|
||||
commandQueue.clear();
|
||||
mouseQueue.clear();
|
||||
}
|
||||
|
||||
InputHandler::~InputHandler() {}
|
||||
|
|
|
|||
|
|
@ -1,125 +1,129 @@
|
|||
#include "gameplay/map.h"
|
||||
#include "gameplay/camera.h"
|
||||
#include "graphics/shader.h"
|
||||
#include "graphics/texture.h"
|
||||
#include "utility/xmlloader.h"
|
||||
#include "utility/resourcemanager.h"
|
||||
#include "utility/logger.h"
|
||||
#include "utility/resourcemanager.h"
|
||||
#include "utility/xmlloader.h"
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
Map::Map(std::shared_ptr<MapData> mapData, const unsigned shaderID, std::shared_ptr<ResourceManager> resourceManager) :
|
||||
mapData(mapData),
|
||||
tileIds(mapData->tiles)
|
||||
{
|
||||
this->shaderID = shaderID;
|
||||
Map::Map(const MapData *mapData, const unsigned shaderID,
|
||||
std::shared_ptr<ResourceManager> resourceManager)
|
||||
: mapData(mapData), tileIds(mapData->tiles) {
|
||||
this->shaderID = shaderID;
|
||||
|
||||
for (auto& tileSet : mapData->tileSets)
|
||||
tileSetData.push_back(resourceManager->loadTileSet(tileSet.path));
|
||||
// Tiles are held on layers, drawn back to front
|
||||
for (auto &tileSet : mapData->tileSets)
|
||||
tileSetData.push_back(resourceManager->loadTileSet(tileSet.path));
|
||||
|
||||
if (!tileSetData.empty())
|
||||
{
|
||||
std::vector<const char*> buffer;
|
||||
for (int layer = 0; layer < tileIds.size(); layer++)
|
||||
{
|
||||
buffer.clear();
|
||||
for (auto& set : tileSetData)
|
||||
buffer.push_back(set->file.c_str());
|
||||
if (!buffer.empty())
|
||||
instanceHandles.push_back(std::make_shared<TileTextureInstance>(buffer));
|
||||
}
|
||||
if (!tileSetData.empty()) {
|
||||
// Storing all of the tilesets we will need to load and sending it to our
|
||||
// texture instance handle
|
||||
std::vector<const char *> buffer;
|
||||
for (int layer = 0; layer < tileIds.size(); layer++) {
|
||||
buffer.clear();
|
||||
for (auto &set : tileSetData)
|
||||
buffer.push_back(set->file.c_str());
|
||||
if (!buffer.empty())
|
||||
instanceHandles.push_back(
|
||||
std::make_shared<TileTextureInstance>(buffer));
|
||||
}
|
||||
|
||||
loadMap();
|
||||
createCollisionMap();
|
||||
}
|
||||
|
||||
}
|
||||
#include <glm/ext.hpp>
|
||||
void Map::loadMap()
|
||||
{
|
||||
tileData.resize(tileIds.size());
|
||||
for (int layer = 0; layer < tileIds.size(); layer++)
|
||||
{
|
||||
for (int y = 0; y < tileIds[layer].size(); y++)
|
||||
{
|
||||
for (int x = 0; x < tileIds[layer][y].size(); x++)
|
||||
{
|
||||
glm::mat4 modelMatrix =
|
||||
glm::translate(glm::mat4(1.f), glm::vec3(x * mapData->tileSize, y * mapData->tileSize, 0.0f)) *
|
||||
glm::scale(glm::mat4(1.f), glm::vec3(mapData->tileSize, mapData->tileSize, 1.0f));
|
||||
|
||||
int textureIndex = getTileSetIndex(tileIds[layer][y][x]);
|
||||
glm::vec2 originalSize = (textureIndex != -1) ?
|
||||
glm::vec2(tileSetData[textureIndex]->width, tileSetData[textureIndex]->height) :
|
||||
glm::vec2(0.0f);
|
||||
int tilesPerRow = (textureIndex != -1) ? tileSetData[textureIndex]->columns : 0;
|
||||
int startID = (textureIndex != -1) ? mapData->tileSets[textureIndex].startID : 0;
|
||||
int tileIndex = tileIds[layer][y][x];
|
||||
|
||||
tileData[layer].push_back({modelMatrix, originalSize, tileIndex, textureIndex, tilesPerRow, startID});
|
||||
}
|
||||
}
|
||||
instanceHandles[layer]->updateInstanceData(tileData[layer]);
|
||||
}
|
||||
glm::vec2 canvasSize = glm::vec2(instanceHandles[0]->getTextureArray()->getWidth(), instanceHandles[0]->getTextureArray()->getHeight());
|
||||
editUniformOnce("uCanvasSize", canvasSize);
|
||||
loadMap();
|
||||
createCollisionMap();
|
||||
}
|
||||
}
|
||||
|
||||
void Map::createCollisionMap()
|
||||
{
|
||||
// Match collisionMap to map size
|
||||
collisionMap.resize(tileIds[0].size());
|
||||
for (int y = 0; y < tileIds[0].size(); ++y)
|
||||
{
|
||||
collisionMap[y].resize(tileIds[0][y].size(), 0);
|
||||
}
|
||||
void Map::loadMap() {
|
||||
tileData.resize(tileIds.size());
|
||||
// Tiles are drawn back to front, we track the tileset the tile comes from and
|
||||
// additional information about the tilesets themselves so the shader can
|
||||
// properly draw the right tile. Each layer is its own instance
|
||||
for (int layer = 0; layer < tileIds.size(); layer++) {
|
||||
for (int y = 0; y < tileIds[layer].size(); y++) {
|
||||
for (int x = 0; x < tileIds[layer][y].size(); x++) {
|
||||
glm::mat4 modelMatrix =
|
||||
glm::translate(
|
||||
glm::mat4(1.f),
|
||||
glm::vec3(x * mapData->tileSize, y * mapData->tileSize, 0.0f)) *
|
||||
glm::scale(glm::mat4(1.f),
|
||||
glm::vec3(mapData->tileSize, mapData->tileSize, 1.0f));
|
||||
|
||||
for (int layer = 0; layer < tileIds.size(); layer++)
|
||||
{
|
||||
for (int y = 0; y < tileIds[layer].size(); y++)
|
||||
{
|
||||
for (int x = 0; x < tileIds[layer][y].size(); x++)
|
||||
{
|
||||
int id = tileIds[layer][y][x];
|
||||
int tileSetIndex = getTileSetIndex(id);
|
||||
if (tileSetIndex == -1)
|
||||
collisionMap[y][x] = 0;
|
||||
else
|
||||
{
|
||||
int startID = mapData->tileSets[tileSetIndex].startID;
|
||||
auto& tile = tileSetData[tileSetIndex]->tiles[id - startID];
|
||||
if (!tile->walkable)
|
||||
collisionMap[y][x] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int textureIndex =
|
||||
static_cast<int>(getTileSetIndex(tileIds[layer][y][x]));
|
||||
glm::vec2 originalSize =
|
||||
(textureIndex != -1) ? glm::vec2(tileSetData[textureIndex]->width,
|
||||
tileSetData[textureIndex]->height)
|
||||
: glm::vec2(0.0f);
|
||||
int tilesPerRow =
|
||||
(textureIndex != -1) ? tileSetData[textureIndex]->columns : 0;
|
||||
int startID =
|
||||
(textureIndex != -1) ? mapData->tileSets[textureIndex].startID : 0;
|
||||
int tileIndex = tileIds[layer][y][x];
|
||||
|
||||
tileData[layer].push_back({modelMatrix, originalSize, tileIndex,
|
||||
textureIndex, tilesPerRow, startID});
|
||||
}
|
||||
}
|
||||
instanceHandles[layer]->updateInstanceData(tileData[layer]);
|
||||
}
|
||||
// The canvas size is the same for every tile atlas, each one growing to match
|
||||
// the size of the larget tileset
|
||||
glm::vec2 canvasSize =
|
||||
glm::vec2(instanceHandles[0]->getTextureArray()->getWidth(),
|
||||
instanceHandles[0]->getTextureArray()->getHeight());
|
||||
editUniformOnce("uCanvasSize", canvasSize);
|
||||
}
|
||||
|
||||
void Map::draw()
|
||||
{
|
||||
for (int layer = 0; layer < instanceHandles.size(); layer++)
|
||||
{
|
||||
instanceHandles[layer]->draw();
|
||||
}
|
||||
// The collision map is just a 2D array of 0's and 1's, 1's being collidable and
|
||||
// 0's not. This may see some changes in the future to properly address special
|
||||
// tiles, such as portals.
|
||||
void Map::createCollisionMap() {
|
||||
// Match collisionMap to map size
|
||||
collisionMap.resize(tileIds[0].size());
|
||||
for (int y = 0; y < tileIds[0].size(); ++y) {
|
||||
collisionMap[y].resize(tileIds[0][y].size(), 0);
|
||||
}
|
||||
|
||||
for (int layer = 0; layer < tileIds.size(); layer++) {
|
||||
for (int y = 0; y < tileIds[layer].size(); y++) {
|
||||
for (int x = 0; x < tileIds[layer][y].size(); x++) {
|
||||
int id = tileIds[layer][y][x];
|
||||
size_t tileSetIndex = getTileSetIndex(id);
|
||||
if (tileSetIndex == -1)
|
||||
collisionMap[y][x] = 0;
|
||||
else {
|
||||
int startID = mapData->tileSets[tileSetIndex].startID;
|
||||
auto &tile = tileSetData[tileSetIndex]->tiles[id - startID];
|
||||
if (!tile->walkable)
|
||||
collisionMap[y][x] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Use this function to get the tileSetIndex from a tile ID.
|
||||
the index of the tileSet is the same index as the texture index used in the instanceHandle
|
||||
// Draw each layer in it's own instance
|
||||
void Map::draw() {
|
||||
for (int layer = 0; layer < instanceHandles.size(); layer++) {
|
||||
instanceHandles[layer]->draw();
|
||||
}
|
||||
}
|
||||
|
||||
returns tileSetIndex of the tile id passed. Unless the id is either 0 or
|
||||
/*
|
||||
Use this function to get the tileSetIndex from a tile ID.
|
||||
the index of the tileSet is the same index as the texture index used in the
|
||||
instanceHandle
|
||||
|
||||
returns tileSetIndex of the tile id passed. Unless the id is either 0 or
|
||||
the returned tileSetIndex is out of bounds, returns -1
|
||||
*/
|
||||
int Map::getTileSetIndex(int id) const
|
||||
{
|
||||
// work from the bottom, break if ID > startID
|
||||
// If we get a textureIndex of -1 then we are on an empty tile
|
||||
size_t tileSetIndex = mapData->tileSets.size() - 1;
|
||||
for (; tileSetIndex != -1; --tileSetIndex)
|
||||
{
|
||||
if (id >= mapData->tileSets[tileSetIndex].startID)
|
||||
break;
|
||||
}
|
||||
return (tileSetIndex >= mapData->tileSets.size()) ? -1 : tileSetIndex;
|
||||
}
|
||||
size_t Map::getTileSetIndex(int id) const {
|
||||
// work from the bottom, break if ID > startID
|
||||
// If we get a textureIndex of -1 then we are on an empty tile
|
||||
size_t tileSetIndex = mapData->tileSets.size() - 1;
|
||||
for (; tileSetIndex != -1; --tileSetIndex) {
|
||||
if (id >= mapData->tileSets[tileSetIndex].startID)
|
||||
break;
|
||||
}
|
||||
return (tileSetIndex >= mapData->tileSets.size()) ? -1 : tileSetIndex;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,210 +2,215 @@
|
|||
#include "gameplay/weapons/bullet.h"
|
||||
#include "utility/events.h"
|
||||
|
||||
#include "utility/logger.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void PhysicsEngine::hookEventManager(const std::shared_ptr<EventManager>& eventManager)
|
||||
{
|
||||
this->eventManager = eventManager;
|
||||
this->eventManager->subscribe("OnBulletFired", [this](std::shared_ptr<Event> e) {
|
||||
auto bulletEvent = std::static_pointer_cast<BulletFiredEvent>(e);
|
||||
this->addObject(bulletEvent->bullet->getPhysicsComponent());
|
||||
});
|
||||
this->eventManager->subscribe("OnBulletDied", [this](std::shared_ptr<Event> e) {
|
||||
auto bulletEvent = std::static_pointer_cast<BulletDiedEvent>(e);
|
||||
this->removeObject(bulletEvent->physObj);
|
||||
});
|
||||
void PhysicsEngine::hookEventManager(
|
||||
const std::shared_ptr<EventManager> &eventManager) {
|
||||
this->eventManager = eventManager;
|
||||
this->eventManager->subscribe<BulletFiredEvent>(
|
||||
[this](const BulletFiredEvent &event) {
|
||||
if (auto bullet = event.bullet.lock()) {
|
||||
this->addObject(bullet->getPhysicsComponent());
|
||||
}
|
||||
});
|
||||
this->eventManager->subscribe<BulletDiedEvent>(
|
||||
[this](const BulletDiedEvent &event) {
|
||||
this->removeObject(event.physObj);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<PhysicsComponent> PhysicsEngine::createObject(const unsigned int ID,
|
||||
const glm::vec3& pos,
|
||||
float mass,
|
||||
PhysicsComponent::Collider::Shape shape,
|
||||
glm::vec3 dimensions, const
|
||||
glm::vec3 offset)
|
||||
{
|
||||
auto component = std::make_shared <PhysicsComponent>();
|
||||
component->ID = ID;
|
||||
component->rigidBody.position = pos;
|
||||
component->rigidBody.mass = mass;
|
||||
component->collider.shape = shape;
|
||||
component->collider.dimensions = dimensions;
|
||||
component->collider.offset = offset;
|
||||
std::shared_ptr<PhysicsComponent>
|
||||
PhysicsEngine::createObject(const unsigned int ID, const glm::vec3 &pos,
|
||||
float mass, PhysicsComponent::Collider::Shape shape,
|
||||
glm::vec3 dimensions, const glm::vec3 offset) {
|
||||
auto component = std::make_shared<PhysicsComponent>();
|
||||
component->ID = ID;
|
||||
component->rigidBody.position = pos;
|
||||
component->rigidBody.acceleration = glm::vec2(0.f);
|
||||
component->rigidBody.mass = mass;
|
||||
component->collider.shape = shape;
|
||||
component->collider.dimensions = dimensions;
|
||||
component->collider.offset = offset;
|
||||
|
||||
addObject(component);
|
||||
return component;
|
||||
addObject(component);
|
||||
return component;
|
||||
}
|
||||
|
||||
void PhysicsEngine::loadCollisionMap(const std::vector<std::vector<int>>& collisionMap, float tileSize)
|
||||
{
|
||||
this->collisionMap = collisionMap;
|
||||
this->tileSize = tileSize;
|
||||
void PhysicsEngine::loadCollisionMap(
|
||||
const std::vector<std::vector<int>> &collisionMap, float tileSize) {
|
||||
this->collisionMap = collisionMap;
|
||||
this->tileSize = tileSize;
|
||||
}
|
||||
|
||||
void PhysicsEngine::addObject(const std::shared_ptr<PhysicsComponent>& component)
|
||||
{
|
||||
if (component)
|
||||
objects.emplace_back(component);
|
||||
void PhysicsEngine::addObject(
|
||||
const std::shared_ptr<PhysicsComponent> &component) {
|
||||
if (component)
|
||||
objects.emplace_back(component);
|
||||
}
|
||||
|
||||
void PhysicsEngine::removeObject(const std::shared_ptr<PhysicsComponent>& component)
|
||||
{
|
||||
if (std::find(objects.begin(), objects.end(), component) != objects.end())
|
||||
objects.erase(std::remove(objects.begin(), objects.end(), component));
|
||||
void PhysicsEngine::removeObject(
|
||||
const std::shared_ptr<PhysicsComponent> &component) {
|
||||
if (std::find(objects.begin(), objects.end(), component) != objects.end())
|
||||
objects.erase(std::remove(objects.begin(), objects.end(), component));
|
||||
}
|
||||
|
||||
int PhysicsEngine::getTileCollider(const glm::vec3& position)
|
||||
{
|
||||
int x = static_cast<int>((position.x + 0.5f * tileSize) / tileSize);
|
||||
int y = static_cast<int>((position.y + 0.5f * tileSize) / tileSize);
|
||||
if (y >= 0 && y < collisionMap.size())
|
||||
{
|
||||
if (x >= 0 && x < collisionMap[y].size())
|
||||
return collisionMap[y][x];
|
||||
}
|
||||
return 0;
|
||||
int PhysicsEngine::getTileCollider(const glm::vec2 &position) {
|
||||
int x = static_cast<int>((position.x + 0.5f * tileSize) / tileSize);
|
||||
int y = static_cast<int>((position.y + 0.5f * tileSize) / tileSize);
|
||||
if (y >= 0 && y < collisionMap.size()) {
|
||||
if (x >= 0 && x < collisionMap[y].size())
|
||||
return collisionMap[y][x];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PhysicsEngine::getPossibleCollisions()
|
||||
{
|
||||
objCollisions.clear();
|
||||
for (size_t i = 0; i < objects.size(); ++i)
|
||||
{
|
||||
auto& obj = objects[i];
|
||||
for (size_t j = i + 1; j < objects.size(); ++j)
|
||||
{
|
||||
auto& colliderObj = objects[j];
|
||||
if (obj.get() == colliderObj.get() || obj->ID == colliderObj->ID) continue;
|
||||
float colliderRight = colliderObj->rigidBody.position.x + colliderObj->collider.dimensions.x;
|
||||
float colliderBottom = colliderObj->rigidBody.position.y + colliderObj->collider.dimensions.y;
|
||||
float objectRight = obj->rigidBody.position.x + obj->collider.dimensions.x;
|
||||
float objectBottom = obj->rigidBody.position.y + obj->collider.dimensions.y;
|
||||
if ((obj->rigidBody.position.x <= colliderRight &&
|
||||
objectRight >= colliderObj->rigidBody.position.x) ||
|
||||
(obj->rigidBody.position.y <= colliderBottom &&
|
||||
objectBottom >= colliderObj->rigidBody.position.y))
|
||||
objCollisions.push_back(CollisionPair(obj.get(), colliderObj.get()));
|
||||
}
|
||||
}
|
||||
void PhysicsEngine::getPossibleCollisions() {
|
||||
objCollisions.clear();
|
||||
for (size_t i = 0; i < objects.size(); ++i) {
|
||||
auto &obj = objects[i];
|
||||
for (size_t j = i + 1; j < objects.size(); ++j) {
|
||||
auto &colliderObj = objects[j];
|
||||
if (obj.get() == colliderObj.get() || obj->ID == colliderObj->ID)
|
||||
continue;
|
||||
float colliderRight = colliderObj->rigidBody.position.x +
|
||||
colliderObj->collider.dimensions.x;
|
||||
float colliderBottom = colliderObj->rigidBody.position.y +
|
||||
colliderObj->collider.dimensions.y;
|
||||
float objectRight =
|
||||
obj->rigidBody.position.x + obj->collider.dimensions.x;
|
||||
float objectBottom =
|
||||
obj->rigidBody.position.y + obj->collider.dimensions.y;
|
||||
if ((obj->rigidBody.position.x <= colliderRight &&
|
||||
objectRight >= colliderObj->rigidBody.position.x) ||
|
||||
(obj->rigidBody.position.y <= colliderBottom &&
|
||||
objectBottom >= colliderObj->rigidBody.position.y))
|
||||
objCollisions.push_back(CollisionPair(obj.get(), colliderObj.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::resolvePossibleCollisions()
|
||||
{
|
||||
for (auto& objs : objCollisions)
|
||||
{
|
||||
// Solve for two circles, we'll need to expand upon this for different colliders...
|
||||
float sumOfRadius = objs.first->collider.dimensions.x + objs.second->collider.dimensions.x;
|
||||
glm::vec3 objFirstCenter = objs.first->rigidBody.position + objs.first->collider.offset;
|
||||
glm::vec3 objSecondCenter = objs.second->rigidBody.position + objs.second->collider.offset;
|
||||
glm::vec3 distance = objFirstCenter - objSecondCenter;
|
||||
if (glm::length(distance) < sumOfRadius)
|
||||
{
|
||||
// We got impact!
|
||||
glm::vec3 normal = distance / glm::length(distance);
|
||||
// That impact is a bullet hitting a gameactor!
|
||||
if ((objs.first->isBullet || objs.second->isBullet) && !(objs.first->isBullet && objs.second->isBullet))
|
||||
{
|
||||
eventManager->notify(std::make_shared<BulletCollideEvent>(
|
||||
( objs.first->isBullet) ? objs.first->ID : objs.second->ID,
|
||||
(!objs.first->isBullet) ? objs.first->ID : objs.second->ID,
|
||||
std::make_shared<PhysicsComponent>(( objs.first->isBullet) ? objs.first : objs.second),
|
||||
normal
|
||||
));
|
||||
}
|
||||
// Apply impulse force
|
||||
float penetrationDepth = sumOfRadius - glm::length(distance);
|
||||
glm::vec3 correctionVector = normal * (penetrationDepth / ((1 / objs.first->rigidBody.mass) + (1 / objs.second->rigidBody.mass)));
|
||||
glm::vec3 vrel = objs.first->rigidBody.velocity - objs.second->rigidBody.velocity;
|
||||
void PhysicsEngine::resolvePossibleCollisions() {
|
||||
for (auto &objs : objCollisions) {
|
||||
// Solve for two circles, we'll need to expand upon this for different
|
||||
// colliders...
|
||||
float sumOfRadius =
|
||||
objs.first->collider.dimensions.x + objs.second->collider.dimensions.x;
|
||||
glm::vec2 objFirstCenter =
|
||||
objs.first->rigidBody.position + objs.first->collider.offset;
|
||||
glm::vec2 objSecondCenter =
|
||||
objs.second->rigidBody.position + objs.second->collider.offset;
|
||||
glm::vec2 distance = objFirstCenter - objSecondCenter;
|
||||
if (glm::length(distance) < sumOfRadius) {
|
||||
// We got impact!
|
||||
glm::vec2 normal = distance / glm::length(distance);
|
||||
// That impact is a bullet hitting a gameactor!
|
||||
if ((objs.first->isBullet || objs.second->isBullet) &&
|
||||
!(objs.first->isBullet && objs.second->isBullet)) {
|
||||
eventManager->notify(std::make_shared<BulletCollideEvent>(
|
||||
(objs.first->isBullet) ? objs.first->ID : objs.second->ID,
|
||||
(!objs.first->isBullet) ? objs.first->ID : objs.second->ID,
|
||||
std::make_shared<PhysicsComponent>(
|
||||
(objs.first->isBullet) ? objs.first : objs.second),
|
||||
normal));
|
||||
}
|
||||
// Apply impulse force
|
||||
float penetrationDepth = sumOfRadius - glm::length(distance);
|
||||
glm::vec2 correctionVector =
|
||||
normal * (penetrationDepth / ((1 / objs.first->rigidBody.mass) +
|
||||
(1 / objs.second->rigidBody.mass)));
|
||||
glm::vec2 vrel =
|
||||
objs.first->rigidBody.velocity - objs.second->rigidBody.velocity;
|
||||
|
||||
// smallest elasticity of the two colliders
|
||||
float e = std::min(objs.first->rigidBody.elasticity, objs.second->rigidBody.elasticity);
|
||||
float impulseMag = (-(1 + e) * glm::dot(vrel, normal)) / ((1 / objs.first->rigidBody.mass) + (1 / objs.second->rigidBody.mass));
|
||||
// smallest elasticity of the two colliders
|
||||
float e = std::min(objs.first->rigidBody.elasticity,
|
||||
objs.second->rigidBody.elasticity);
|
||||
float impulseMag = (-(1 + e) * glm::dot(vrel, normal)) /
|
||||
((1 / objs.first->rigidBody.mass) +
|
||||
(1 / objs.second->rigidBody.mass));
|
||||
|
||||
objs.first->rigidBody.position += (correctionVector / objs.first->rigidBody.mass);
|
||||
objs.second->rigidBody.position -= (correctionVector / objs.second->rigidBody.mass);
|
||||
objs.first->rigidBody.velocity += impulseMag * normal / objs.first->rigidBody.mass;
|
||||
objs.second->rigidBody.velocity -= impulseMag * normal / objs.second->rigidBody.mass;
|
||||
}
|
||||
}
|
||||
objs.first->rigidBody.position +=
|
||||
(correctionVector / objs.first->rigidBody.mass);
|
||||
objs.second->rigidBody.position -=
|
||||
(correctionVector / objs.second->rigidBody.mass);
|
||||
objs.first->rigidBody.velocity +=
|
||||
impulseMag * normal / objs.first->rigidBody.mass;
|
||||
objs.second->rigidBody.velocity -=
|
||||
impulseMag * normal / objs.second->rigidBody.mass;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::resolveWorldCollision(const std::shared_ptr<PhysicsComponent>& obj)
|
||||
{
|
||||
switch (obj->collider.shape)
|
||||
{
|
||||
case PhysicsComponent::Collider::Shape::Circle:
|
||||
float radius = obj->collider.dimensions.x;
|
||||
glm::vec3 position = obj->rigidBody.position + obj->collider.offset;
|
||||
int topTile = getTileCollider(position - glm::vec3(0, radius, 0));
|
||||
int bottomTile = getTileCollider(position + glm::vec3(0, radius, 0));
|
||||
int leftTile = getTileCollider(position - glm::vec3(radius, 0, 0));
|
||||
int rightTile = getTileCollider(position + glm::vec3(radius, 0, 0));
|
||||
if (obj->isBullet)
|
||||
{
|
||||
if (topTile || bottomTile || leftTile || rightTile)
|
||||
{
|
||||
eventManager->notify(std::make_shared<BulletDiedEvent>(obj));
|
||||
return;
|
||||
}
|
||||
}
|
||||
int tileY = static_cast<int>((position.y) / tileSize);
|
||||
int tileX = static_cast<int>((position.x) / tileSize);
|
||||
if (topTile)
|
||||
{
|
||||
//obj->rigidBody.velocity.y = -obj->rigidBody.velocity.y;
|
||||
obj->rigidBody.position.y = (tileY+1) * tileSize + obj->collider.offset.y;
|
||||
}
|
||||
if (bottomTile)
|
||||
{
|
||||
//obj->rigidBody.velocity.y = -obj->rigidBody.velocity.y;
|
||||
obj->rigidBody.position.y = (tileY) * tileSize - obj->collider.offset.y;
|
||||
}
|
||||
if (leftTile)
|
||||
{
|
||||
//obj->rigidBody.velocity.x = -obj->rigidBody.velocity.x;
|
||||
obj->rigidBody.position.x = (tileX + 1) * tileSize + obj->collider.offset.x;
|
||||
}
|
||||
if (rightTile)
|
||||
{
|
||||
//obj->rigidBody.velocity.x = -obj->rigidBody.velocity.x;
|
||||
obj->rigidBody.position.x = (tileX) * tileSize - obj->collider.offset.x;
|
||||
}
|
||||
}
|
||||
void PhysicsEngine::resolveWorldCollision(
|
||||
const std::shared_ptr<PhysicsComponent> &obj) {
|
||||
switch (obj->collider.shape) {
|
||||
case PhysicsComponent::Collider::Shape::Circle:
|
||||
float radius = obj->collider.dimensions.x;
|
||||
glm::vec2 position = obj->rigidBody.position + obj->collider.offset;
|
||||
int topTile = getTileCollider(position - glm::vec2(0, radius));
|
||||
int bottomTile = getTileCollider(position + glm::vec2(0, radius));
|
||||
int leftTile = getTileCollider(position - glm::vec2(radius, 0));
|
||||
int rightTile = getTileCollider(position + glm::vec2(radius, 0));
|
||||
if (obj->isBullet) {
|
||||
if (topTile || bottomTile || leftTile || rightTile) {
|
||||
eventManager->notify(std::make_shared<BulletDiedEvent>(obj));
|
||||
return;
|
||||
}
|
||||
}
|
||||
int tileY = static_cast<int>((position.y) / tileSize);
|
||||
int tileX = static_cast<int>((position.x) / tileSize);
|
||||
if (topTile) {
|
||||
// obj->rigidBody.velocity.y = -obj->rigidBody.velocity.y;
|
||||
obj->rigidBody.position.y =
|
||||
(tileY + 1) * tileSize + obj->collider.offset.y;
|
||||
}
|
||||
if (bottomTile) {
|
||||
// obj->rigidBody.velocity.y = -obj->rigidBody.velocity.y;
|
||||
obj->rigidBody.position.y = (tileY)*tileSize - obj->collider.offset.y;
|
||||
}
|
||||
if (leftTile) {
|
||||
// obj->rigidBody.velocity.x = -obj->rigidBody.velocity.x;
|
||||
obj->rigidBody.position.x =
|
||||
(tileX + 1) * tileSize + obj->collider.offset.x;
|
||||
}
|
||||
if (rightTile) {
|
||||
// obj->rigidBody.velocity.x = -obj->rigidBody.velocity.x;
|
||||
obj->rigidBody.position.x = (tileX)*tileSize - obj->collider.offset.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEngine::update(double deltaTime)
|
||||
{
|
||||
for (auto& obj : objects)
|
||||
{
|
||||
if (!obj) continue;
|
||||
glm::vec3 frictionForce = obj->rigidBody.velocity * -0.1f;
|
||||
if (std::abs(obj->rigidBody.acceleration.x) == std::abs(obj->rigidBody.acceleration.y))
|
||||
{
|
||||
obj->rigidBody.acceleration.x *= 0.75f;
|
||||
obj->rigidBody.acceleration.y *= 0.75f;
|
||||
}
|
||||
if (!obj->isBullet)
|
||||
obj->rigidBody.velocity += (frictionForce);
|
||||
obj->rigidBody.velocity += obj->rigidBody.acceleration;
|
||||
void PhysicsEngine::update(double deltaTime) {
|
||||
for (auto &obj : objects) {
|
||||
if (!obj)
|
||||
continue;
|
||||
glm::vec2 frictionForce = obj->rigidBody.velocity * -0.1f;
|
||||
if (std::abs(obj->rigidBody.acceleration.x) ==
|
||||
std::abs(obj->rigidBody.acceleration.y)) {
|
||||
obj->rigidBody.acceleration.x *= 0.75f;
|
||||
obj->rigidBody.acceleration.y *= 0.75f;
|
||||
}
|
||||
if (!obj->isBullet)
|
||||
obj->rigidBody.velocity += (frictionForce);
|
||||
obj->rigidBody.velocity += obj->rigidBody.acceleration;
|
||||
|
||||
float maxSpeed = 500.f;
|
||||
float curSpeed = glm::length(obj->rigidBody.velocity);
|
||||
if (curSpeed > maxSpeed)
|
||||
{
|
||||
// Move at maxspeed
|
||||
obj->rigidBody.velocity = glm::normalize(obj->rigidBody.velocity) * maxSpeed;
|
||||
}
|
||||
obj->rigidBody.acceleration = glm::vec3(0.f);
|
||||
if (obj->collider.dimensions != glm::vec3(0.f))
|
||||
{
|
||||
// check map collisions
|
||||
resolveWorldCollision(obj);
|
||||
}
|
||||
obj->rigidBody.position += obj->rigidBody.velocity * static_cast<float>(deltaTime);
|
||||
}
|
||||
getPossibleCollisions();
|
||||
if (!objCollisions.empty())
|
||||
resolvePossibleCollisions();
|
||||
}
|
||||
float maxSpeed = 500.f;
|
||||
float curSpeed = glm::length(obj->rigidBody.velocity);
|
||||
if (curSpeed > maxSpeed) {
|
||||
// Move at maxspeed
|
||||
obj->rigidBody.velocity =
|
||||
glm::normalize(obj->rigidBody.velocity) * maxSpeed;
|
||||
}
|
||||
obj->rigidBody.acceleration = glm::vec2(0.f);
|
||||
if (obj->collider.dimensions != glm::vec2(0.f)) {
|
||||
// check map collisions
|
||||
resolveWorldCollision(obj);
|
||||
}
|
||||
obj->rigidBody.position +=
|
||||
obj->rigidBody.velocity * static_cast<float>(deltaTime);
|
||||
// Make sure we keep our Z at 0.f, a better choice would be to remove the
|
||||
// need for vec3 all together!
|
||||
// TODO: REMOVE VEC3 NO NEED FOR Z COODINATE!
|
||||
// obj->rigidBody.position.z = 0.f;
|
||||
}
|
||||
getPossibleCollisions();
|
||||
if (!objCollisions.empty())
|
||||
resolvePossibleCollisions();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,205 +1,192 @@
|
|||
#include "gameplay/scene.h"
|
||||
#include "gameplay/camera.h"
|
||||
#include "gameplay/ai.h"
|
||||
#include "gameplay/gameactor.h"
|
||||
#include "gameplay/weapons/weapons.h"
|
||||
#include "gameplay/weapons/bulletmanager.h"
|
||||
#include "gameplay/map.h"
|
||||
#include "gameplay/physics.h"
|
||||
#include "gameplay/ai.h"
|
||||
#include "gameplay/weapons/bulletmanager.h"
|
||||
#include "gameplay/weapons/weapons.h"
|
||||
|
||||
#include "graphics/sprite.h"
|
||||
#include "graphics/animation.h"
|
||||
#include "graphics/background.h"
|
||||
#include "graphics/sprite.h"
|
||||
|
||||
#include "utility/component.h"
|
||||
#include "utility/xmlloader.h"
|
||||
#include "utility/resourcemanager.h"
|
||||
#include "utility/events.h"
|
||||
#include "utility/raycaster.h"
|
||||
#include "utility/debugdraw.h"
|
||||
#include "utility/events.h"
|
||||
#include "utility/ftfont.h"
|
||||
#include "utility/raycaster.h"
|
||||
#include "utility/resourcemanager.h"
|
||||
#include "utility/script.h"
|
||||
#include "utility/xmlloader.h"
|
||||
|
||||
#include <memory>
|
||||
#include <tracy/Tracy.hpp>
|
||||
|
||||
#include <execution>
|
||||
|
||||
// Scene xml files, should contain a node called <player> that holds the sprite location
|
||||
/*
|
||||
like this:
|
||||
<player sprite="sprites/player2Atlas.png" frameSize=64.0>
|
||||
<x=5/>
|
||||
<y=6/>
|
||||
</player>
|
||||
*/
|
||||
|
||||
Scene::Scene(SceneType sceneType, std::shared_ptr<ResourceManager> resources, std::weak_ptr<EventManager> globalEvents)
|
||||
: type(sceneType), resourceManager(resources), globalEventManager(globalEvents)
|
||||
{
|
||||
camera = std::make_shared<Camera>(800.f, 600.f);
|
||||
physicsEngine = std::make_shared<PhysicsEngine>();
|
||||
eventManager = std::make_shared<EventManager>();
|
||||
Scene::Scene(SceneType sceneType, std::shared_ptr<ResourceManager> resources,
|
||||
std::weak_ptr<EventManager> globalEvents)
|
||||
: type(sceneType), resourceManager(resources),
|
||||
globalEventManager(globalEvents) {
|
||||
physicsEngine = std::make_shared<PhysicsEngine>();
|
||||
eventManager = std::make_shared<EventManager>();
|
||||
}
|
||||
|
||||
void Scene::init()
|
||||
{
|
||||
physicsEngine->hookEventManager(eventManager);
|
||||
//if (sceneType == SCENE_SHOOTER)
|
||||
loadDebugShooterScene();
|
||||
void Scene::init() {
|
||||
physicsEngine->hookEventManager(eventManager);
|
||||
loadDebugShooterScene();
|
||||
}
|
||||
|
||||
// This function is full of hardcoded values and test sprites, NOT for use with final product
|
||||
void Scene::loadDebugShooterScene()
|
||||
{
|
||||
hookSceneEvents();
|
||||
sceneData = resourceManager->loadScene("000");
|
||||
if (!sceneData)
|
||||
return;
|
||||
EntityData playerData = sceneData->entities[0];
|
||||
auto mapData = sceneData->map;
|
||||
auto playerShader = resourceManager->loadShader("GL_player", "shaders/GL_player.vert", "shaders/GL_player.frag");
|
||||
auto bubbleShader = resourceManager->loadShader("GL_bubble", "shaders/GL_bubble.vert", "shaders/GL_bubble.frag");
|
||||
auto weaponShader = resourceManager->loadShader("GL_pistol", "shaders/GL_pistol.vert", "shaders/GL_pistol.frag");
|
||||
// This function is full of hardcoded values and test sprites, NOT for use with
|
||||
// final product
|
||||
void Scene::loadDebugShooterScene() {
|
||||
hookSceneEvents();
|
||||
sceneData = resourceManager->loadScene("000");
|
||||
if (!sceneData)
|
||||
return;
|
||||
EntityData playerData = sceneData->entities[0];
|
||||
auto mapData = sceneData->map;
|
||||
auto playerShader = resourceManager->loadShader(
|
||||
"GL_player", "shaders/GL_player.vert", "shaders/GL_player.frag");
|
||||
auto bubbleShader = resourceManager->loadShader(
|
||||
"GL_bubble", "shaders/GL_bubble.vert", "shaders/GL_bubble.frag");
|
||||
auto weaponShader = resourceManager->loadShader(
|
||||
"GL_pistol", "shaders/GL_pistol.vert", "shaders/GL_pistol.frag");
|
||||
|
||||
// creating map from scene
|
||||
auto tileShader = resourceManager->loadShader("GL_tile", "shaders/GL_tile.vert", "shaders/GL_tile.frag");
|
||||
map = std::make_shared<Map>(mapData, tileShader, resourceManager);
|
||||
if (!sceneData->bgFile.empty()) {
|
||||
LOG(INFO, "Found background loading '{}'", sceneData->bgFile);
|
||||
background = resourceManager->loadBackground(sceneData->bgFile);
|
||||
}
|
||||
|
||||
for (EntityData entityData : sceneData->entities)
|
||||
{
|
||||
auto entity = std::make_shared<GameActor>(this, playerShader);
|
||||
// Directional is the kind of sprite sheet we are using, in this case for directional, I have the sprite sheet handle the rotations
|
||||
// instead of just rotating the object, this makes it look quite a bit better from the end user perspective.
|
||||
if (entityData.animated)
|
||||
{
|
||||
auto entityAnimation = resourceManager->loadAnimationSet(entityData.graphic, entity->getEntityID());
|
||||
// because we don't want to have the engine rotate the object based on the entities rotation,
|
||||
// we set the this value to false so we no longer rotate the object.
|
||||
if (entityAnimation->getDirectional())
|
||||
entity->setRotatable(false);
|
||||
entity->addComponent(std::make_unique<AnimationComponent>(entityAnimation, eventManager));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto entitySprite = resourceManager->loadSpriteStatic(entityData.graphic);
|
||||
entity->addComponent(std::make_unique<SpriteComponent>(entitySprite));
|
||||
}
|
||||
auto defaultWeapon = resourceManager->loadWeapon("pistolGun", weaponShader, bubbleShader);
|
||||
auto entityWeapon = resourceManager->loadWeapon(entityData.weapon, weaponShader, bubbleShader);
|
||||
// creating map from scene
|
||||
auto tileShader = resourceManager->loadShader(
|
||||
"GL_tile", "shaders/GL_tile.vert", "shaders/GL_tile.frag");
|
||||
map = std::make_shared<Map>(mapData, tileShader, resourceManager);
|
||||
|
||||
entity->pickupWeapon(defaultWeapon);
|
||||
entity->pickupWeapon(entityWeapon);
|
||||
entity->setPosition(glm::vec3(entityData.x * mapData->tileSize, entityData.y * mapData->tileSize, 0.f));
|
||||
entity->setScale(glm::vec3(mapData->tileSize, mapData->tileSize, 1.f));
|
||||
for (EntityData entityData : sceneData->entities) {
|
||||
auto entity = std::make_shared<GameActor>(this, playerShader);
|
||||
// Directional is the kind of sprite sheet we are using, in this case for
|
||||
// directional, I have the sprite sheet handle the rotations instead of just
|
||||
// rotating the object, this makes it look quite a bit better from the end
|
||||
// user perspective.
|
||||
if (entityData.animated) {
|
||||
auto entityAnimation = resourceManager->loadAnimationSet(
|
||||
entityData.graphic, entity->getEntityID());
|
||||
// because we don't want to have the engine rotate the object based on the
|
||||
// entities rotation, we set the this value to false so we no longer
|
||||
// rotate the object.
|
||||
if (entityAnimation->getDirectional())
|
||||
entity->setRotatable(false);
|
||||
entity->addComponent(
|
||||
std::make_unique<AnimationComponent>(entityAnimation, eventManager));
|
||||
} else {
|
||||
auto entitySprite = resourceManager->loadSpriteStatic(entityData.graphic);
|
||||
entity->addComponent(std::make_unique<SpriteComponent>(entitySprite));
|
||||
}
|
||||
auto defaultWeapon =
|
||||
resourceManager->loadWeapon("gun/pistol", weaponShader, bubbleShader);
|
||||
auto entityWeapon = resourceManager->loadWeapon(entityData.weapon,
|
||||
weaponShader, bubbleShader);
|
||||
|
||||
entity->addPhysicsComponent(
|
||||
physicsEngine->createObject(entity->getEntityID(),
|
||||
entity->getPosition(),
|
||||
49.0,
|
||||
PhysicsComponent::Collider::Shape::Circle,
|
||||
glm::vec3(mapData->tileSize / 2))
|
||||
);
|
||||
entity->pickupWeapon(std::move(defaultWeapon));
|
||||
entity->pickupWeapon(std::move(entityWeapon));
|
||||
entity->setPosition(glm::vec3(entityData.x * mapData->tileSize,
|
||||
entityData.y * mapData->tileSize, 0.f));
|
||||
entity->setScale(glm::vec3(mapData->tileSize, mapData->tileSize, 1.f));
|
||||
|
||||
if (entityData.isPlayer)
|
||||
{
|
||||
player = entity;
|
||||
camera->setTarget(entity.get());
|
||||
entity->setLocalPosition(camera->worldToLocal(entity->getPosition()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// attach ai
|
||||
if (!entityData.script.empty())
|
||||
{
|
||||
auto behaviourScript = resourceManager->loadAIScript(entityData.script);
|
||||
auto rayCaster = std::make_shared<Raycaster>(40.f, 300.f, map->getCollisionMap(), mapData->tileSize);
|
||||
auto ai = std::make_shared<AI>(entity, rayCaster);
|
||||
ai->setTarget(player);
|
||||
ai->attachBehaviourScript(behaviourScript);
|
||||
entity->addComponent(std::make_unique<AIComponent>(ai));
|
||||
}
|
||||
}
|
||||
entities.emplace(entity->getEntityID(), entity);
|
||||
}
|
||||
entity->addPhysicsComponent(physicsEngine->createObject(
|
||||
entity->getEntityID(), entity->getPosition(), 49.0,
|
||||
PhysicsComponent::Collider::Shape::Circle,
|
||||
glm::vec3(mapData->tileSize / 2)));
|
||||
|
||||
physicsEngine->loadCollisionMap(map->getCollisionMap(), mapData->tileSize);
|
||||
if (entityData.isPlayer) {
|
||||
player = entity;
|
||||
} else {
|
||||
// attach ai
|
||||
if (!entityData.script.empty()) {
|
||||
auto behaviour =
|
||||
resourceManager->loadScript<AIScript>(entityData.script);
|
||||
auto rayCaster = std::make_unique<Raycaster>(
|
||||
40.f, 300.f, map->getCollisionMap(), mapData->tileSize);
|
||||
auto ai = std::make_shared<AI>(entity.get(), std::move(rayCaster));
|
||||
ai->setTarget(player.get());
|
||||
if (behaviour != nullptr)
|
||||
ai->attachBehaviourScript(std::move(behaviour));
|
||||
entity->addComponent(std::make_unique<AIComponent>(ai));
|
||||
}
|
||||
}
|
||||
entities.emplace(entity->getEntityID(), entity);
|
||||
SDL_Delay(1); // This is to make sure each entity gets a unique ID
|
||||
}
|
||||
|
||||
// Setup map and other entities...
|
||||
physicsEngine->loadCollisionMap(map->getCollisionMap(), mapData->tileSize);
|
||||
}
|
||||
|
||||
std::shared_ptr<GameActor> Scene::getPlayer() const
|
||||
{
|
||||
return (!player) ? nullptr : player;
|
||||
std::shared_ptr<GameActor> Scene::getPlayer() const {
|
||||
return (!player) ? nullptr : player;
|
||||
}
|
||||
|
||||
void Scene::update(double deltaTime)
|
||||
{
|
||||
ZoneScoped;
|
||||
for (const auto& [id, e] : entities)
|
||||
{
|
||||
e->update(deltaTime);
|
||||
if (camera->getTarget() == e.get())
|
||||
e->setLocalPosition(camera->worldToLocal(e->getPosition()));
|
||||
}
|
||||
physicsEngine->update(deltaTime);
|
||||
camera->update(deltaTime);
|
||||
void Scene::update(double deltaTime) {
|
||||
|
||||
for (const auto &[id, e] : entities) {
|
||||
e->update(deltaTime);
|
||||
}
|
||||
physicsEngine->update(deltaTime);
|
||||
}
|
||||
|
||||
void Scene::render(std::shared_ptr<Renderer> renderer)
|
||||
{
|
||||
ZoneScoped;
|
||||
renderer->clear();
|
||||
void Scene::render(std::shared_ptr<Renderer> renderer) {
|
||||
renderer->clear();
|
||||
|
||||
renderer->setProjAndViewMatrix(camera->getProjectionMatrix(), camera->getViewMatrix());
|
||||
renderer->addDrawable(RenderLayer::Map, map);
|
||||
//map->draw();
|
||||
for (auto& [id, e] : entities)
|
||||
{
|
||||
//e->draw();
|
||||
renderer->addDrawable(RenderLayer::GameObjects, e);
|
||||
if (e->getHeldWeapon()) {
|
||||
renderer->addDrawable(RenderLayer::GameObjects, e->getHeldWeapon());
|
||||
for (const auto& bullet : e->getHeldWeapon()->getBulletManager()->getBullets()) {
|
||||
renderer->addDrawable(RenderLayer::GameObjects, bullet);
|
||||
}
|
||||
}
|
||||
}
|
||||
renderer->addDrawable(RenderLayer::Map, map.get());
|
||||
if (background) {
|
||||
renderer->addDrawable(RenderLayer::Background,
|
||||
static_cast<Drawable *>(background));
|
||||
}
|
||||
for (auto &[id, e] : entities) {
|
||||
renderer->addDrawable(RenderLayer::GameObjects, e.get());
|
||||
if (e->getHeldWeapon()) {
|
||||
renderer->addDrawable(RenderLayer::GameObjects, e->getHeldWeapon());
|
||||
const auto &weapons = e->getAllWeapons();
|
||||
for (auto &w : weapons) {
|
||||
for (auto b : w->getBulletManager()->getBullets()) {
|
||||
renderer->addDrawable(RenderLayer::GameObjects, b.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderer->render();
|
||||
DebugDrawer::getInstance().draw(camera->getProjectionMatrix() * camera->getViewMatrix());
|
||||
renderer->render();
|
||||
DEBUG_TEXT(glm::vec3(10.f, 10.f, 0.f), glm::vec4(0.f, 0.f, 0.f, 1.f), 0.5f,
|
||||
"{} / {}", getPlayer()->getHeldWeapon()->getMagazine(),
|
||||
getPlayer()->getHeldWeapon()->getAmmo());
|
||||
}
|
||||
|
||||
void Scene::unloadScene()
|
||||
{
|
||||
//xmlLoader.reset();
|
||||
void Scene::unloadScene() {
|
||||
// xmlLoader.reset();
|
||||
}
|
||||
|
||||
void Scene::hookSceneEvents()
|
||||
{
|
||||
std::weak_ptr<Scene> weakSelf = shared_from_this();
|
||||
eventManager->subscribe("OnBulletCollide", [weakSelf](std::shared_ptr<Event> e) {
|
||||
if (auto self = weakSelf.lock()) {
|
||||
auto collideEvent = std::static_pointer_cast<BulletCollideEvent>(e);
|
||||
GameActor* shooter = self->getGameActorByID(collideEvent->ownerID);
|
||||
GameActor* target = self->getGameActorByID(collideEvent->victimID);
|
||||
if (shooter && target)
|
||||
if (auto& weapon = shooter->getHeldWeapon())
|
||||
weapon->onHitCallback(target, collideEvent->bullet.get(), collideEvent->normal);
|
||||
}
|
||||
});
|
||||
void Scene::hookSceneEvents() {
|
||||
std::weak_ptr<Scene> weakSelf = shared_from_this();
|
||||
eventManager->subscribe<BulletCollideEvent>(
|
||||
[weakSelf](const BulletCollideEvent &e) {
|
||||
if (auto self = weakSelf.lock()) {
|
||||
GameActor *shooter = self->getGameActorByID(e.ownerID);
|
||||
GameActor *target = self->getGameActorByID(e.victimID);
|
||||
if (shooter && target)
|
||||
if (auto weapon = shooter->getHeldWeapon())
|
||||
weapon->onHitCallback(target, e.bullet.get(), e.normal);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
GameActor* Scene::getGameActorByID(const unsigned int ID)
|
||||
{
|
||||
auto iterator = entities.find(ID);
|
||||
if (iterator == entities.end())
|
||||
return nullptr;
|
||||
return iterator->second.get();
|
||||
GameActor *Scene::getGameActorByID(const unsigned int ID) {
|
||||
auto iterator = entities.find(ID);
|
||||
if (iterator == entities.end())
|
||||
return nullptr;
|
||||
return iterator->second.get();
|
||||
}
|
||||
|
||||
std::span<std::weak_ptr<GameActor>> Scene::getAllEntities() const
|
||||
{
|
||||
entityCache.clear();
|
||||
for (const auto& [_, entity] : entities) {
|
||||
entityCache.push_back(entity);
|
||||
}
|
||||
return entityCache;
|
||||
}
|
||||
std::span<std::weak_ptr<GameActor>> Scene::getAllEntities() const {
|
||||
entityCache.clear();
|
||||
for (const auto &[_, entity] : entities) {
|
||||
entityCache.push_back(entity);
|
||||
}
|
||||
return entityCache;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "gameplay/weapons/bullet.h"
|
||||
#include "utility/component.h"
|
||||
|
||||
#include "utility/logger.h"
|
||||
|
||||
void Bullet::addComponent(Component* component)
|
||||
{
|
||||
components.push_back(component);
|
||||
|
|
@ -12,6 +14,8 @@ void Bullet::update(double deltaTime)
|
|||
|
||||
deltaPosition.x += direction.x * bulletSpeed;
|
||||
deltaPosition.y += direction.y * bulletSpeed;
|
||||
|
||||
//LOG(INFO, "( {}, {}, {} )", deltaPosition.x, deltaPosition.y, deltaPosition.z);
|
||||
//physics->rigidBody.position = position;
|
||||
|
||||
for (auto& component : components)
|
||||
|
|
@ -33,4 +37,4 @@ void Bullet::draw()
|
|||
Bullet::~Bullet()
|
||||
{
|
||||
components.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "gameplay/camera.h"
|
||||
#include "utility/events.h"
|
||||
#include "utility/component.h"
|
||||
#include "utility/logger.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
|
@ -13,11 +14,10 @@ void BulletManager::hookEventManager(std::weak_ptr<EventManager> eventManager)
|
|||
this->eventManager = eventManager;
|
||||
if (auto event = this->eventManager.lock()) {
|
||||
std::weak_ptr<BulletManager> weakSelf = shared_from_this();
|
||||
event->subscribe("OnBulletDied", [weakSelf](std::shared_ptr<Event> e) {
|
||||
event->subscribe<BulletDiedEvent>([weakSelf](const BulletDiedEvent& e) {
|
||||
if (auto self = weakSelf.lock()) {
|
||||
auto bulletEvent = std::static_pointer_cast<BulletDiedEvent>(e);
|
||||
auto iterator = std::find_if(self->bullets.begin(), self->bullets.end(), [&](std::shared_ptr<Bullet> bullet) {
|
||||
return bullet->getPhysicsComponent() == bulletEvent->physObj;
|
||||
return bullet->getPhysicsComponent() == e.physObj;
|
||||
});
|
||||
if (iterator != self->bullets.end())
|
||||
self->bullets.erase(iterator);
|
||||
|
|
@ -52,11 +52,11 @@ void BulletManager::update(double deltaTime)
|
|||
{
|
||||
if (!bullet) continue;
|
||||
bullet->update(deltaTime);
|
||||
float distance = glm::distance(bullet->getPhysicsComponent()->rigidBody.position, bullet->getBulletOrigin());
|
||||
float distance = glm::distance(glm::vec3(bullet->getPhysicsComponent()->rigidBody.position, 0.f), bullet->getBulletOrigin());
|
||||
if (distance > bullet->getBulletDrop() || glm::length(bullet->getPhysicsComponent()->rigidBody.velocity) < 100.0f)
|
||||
{
|
||||
if (auto event = eventManager.lock())
|
||||
event->notify(std::make_shared<BulletDiedEvent>(bullet->getPhysicsComponent()));
|
||||
event->notify<BulletDiedEvent>((BulletDiedEvent){ bullet->getPhysicsComponent() });
|
||||
//bullets.erase(std::remove(bullets.begin(), bullets.end(), bullet));
|
||||
}
|
||||
}
|
||||
|
|
@ -69,4 +69,4 @@ void BulletManager::draw()
|
|||
if (!bullet) continue;
|
||||
bullet->draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,282 +1,249 @@
|
|||
#include "gameplay/weapons/weapon.h"
|
||||
#include "gameplay/weapons/bulletmanager.h"
|
||||
#include "gameplay/weapons/bullet.h"
|
||||
#include "gameplay/gameactor.h"
|
||||
#include "gameplay/physics.h"
|
||||
#include "gameplay/weapons/bullet.h"
|
||||
#include "gameplay/weapons/bulletmanager.h"
|
||||
#include <SDL_timer.h>
|
||||
|
||||
#include "utility/debugdraw.h"
|
||||
#include "utility/component.h"
|
||||
#include "utility/debugdraw.h"
|
||||
#include "utility/events.h"
|
||||
#include "utility/logger.h"
|
||||
#include "utility/resourcemanager.h"
|
||||
#include "utility/script.h"
|
||||
#include "utility/logger.h"
|
||||
|
||||
// TODO: Regular clean up, make this mess readable!
|
||||
|
||||
Weapon::Weapon(std::shared_ptr<WeaponData> data, const unsigned weaponShaderID, const unsigned bulletShaderID, ResourceManager* resourceManager)
|
||||
:
|
||||
Entity (weaponShaderID),
|
||||
weaponSize (glm::vec2(data->sizeX, data->sizeY)),
|
||||
weaponOffset (glm::vec2(data->offsetX, data->offsetY)),
|
||||
weaponMag (data->clipSize),
|
||||
weaponMagSize (data->clipSize),
|
||||
weaponAmmo (data->maxAmmo),
|
||||
bulletSpeed (data->bulletSpeed),
|
||||
bulletDrop (data->bulletDrop),
|
||||
fireSpeed (data->fireSpeed),
|
||||
bulletSize (glm::vec2(data->bulletSizeX, data->bulletSizeY)),
|
||||
bulletShaderID (bulletShaderID),
|
||||
bulletManager (std::make_shared<BulletManager>()),
|
||||
bulletSpread (std::make_unique<UTIL::RandomGenerator>(-data->bulletSpread, data->bulletSpread)),
|
||||
bulletModifer (std::make_unique<UTIL::RandomGenerator>(data->modMin, data->modMax))
|
||||
{
|
||||
if (data->bulletAnimated)
|
||||
bulletSprite = std::make_unique<AnimationComponent>(resourceManager->loadAnimationSet(data->bulletGraphic, entityid));
|
||||
else
|
||||
bulletSprite = std::make_unique<SpriteComponent>(resourceManager->loadSpriteStatic(data->bulletGraphic));
|
||||
Weapon::Weapon(const WeaponData *data, const unsigned weaponShaderID,
|
||||
const unsigned bulletShaderID, ResourceManager *resourceManager)
|
||||
: Entity(weaponShaderID), weaponType(data->id),
|
||||
weaponSize(glm::vec2(data->sizeX, data->sizeY)),
|
||||
weaponOffset(glm::vec2(data->offsetX, data->offsetY)),
|
||||
weaponMag(data->clipSize), weaponMagSize(data->clipSize),
|
||||
weaponAmmo(data->maxAmmo), bulletSpeed(data->bulletSpeed),
|
||||
bulletDrop(data->bulletDrop), fireSpeed(data->fireSpeed),
|
||||
bulletSize(glm::vec2(data->bulletSizeX, data->bulletSizeY)),
|
||||
bulletShaderID(bulletShaderID),
|
||||
bulletManager(std::make_shared<BulletManager>()),
|
||||
bulletSpread(std::make_unique<UTIL::RandomGenerator>(-data->bulletSpread,
|
||||
data->bulletSpread)),
|
||||
bulletModifer(
|
||||
std::make_unique<UTIL::RandomGenerator>(data->modMin, data->modMax)) {
|
||||
if (data->bulletAnimated)
|
||||
bulletSprite = std::make_unique<AnimationComponent>(
|
||||
resourceManager->loadAnimationSet(data->bulletGraphic, entityid));
|
||||
else
|
||||
bulletSprite = std::make_unique<SpriteComponent>(
|
||||
resourceManager->loadSpriteStatic(data->bulletGraphic));
|
||||
|
||||
if (data->animated)
|
||||
{
|
||||
addComponent(std::make_unique<AnimationComponent>(resourceManager->loadAnimationSet(data->graphic, entityid)));
|
||||
}
|
||||
else
|
||||
addComponent(std::make_unique<SpriteComponent>(resourceManager->loadSpriteStatic(data->graphic)));
|
||||
this->setScale(glm::vec3(weaponSize.x, weaponSize.y, 1.0f));
|
||||
if (data->animated) {
|
||||
addComponent(std::make_unique<AnimationComponent>(
|
||||
resourceManager->loadAnimationSet(data->id, entityid)));
|
||||
} else
|
||||
addComponent(std::make_unique<SpriteComponent>(
|
||||
resourceManager->loadSpriteStatic(data->graphic)));
|
||||
this->setScale(glm::vec3(weaponSize.x, weaponSize.y, 1.0f));
|
||||
};
|
||||
|
||||
void Weapon::addComponent(std::unique_ptr<Component> comp) {
|
||||
components.push_back(std::move(comp));
|
||||
components.push_back(std::move(comp));
|
||||
}
|
||||
|
||||
void Weapon::reload()
|
||||
{
|
||||
// TODO: Create reload event that will be captured by the gun animation set, to start the reloading animation
|
||||
if (auto event = eventManager.lock())
|
||||
{
|
||||
event->notify(std::make_shared<EntityReloadEvent>(entityid));
|
||||
reloading = true;
|
||||
if (weaponAmmo < weaponMagSize) {
|
||||
weaponMag = weaponAmmo;
|
||||
weaponAmmo = 0;
|
||||
}
|
||||
else {
|
||||
weaponMag = weaponMagSize;
|
||||
weaponAmmo -= weaponMagSize;
|
||||
}
|
||||
}
|
||||
void Weapon::reload() {
|
||||
if (auto event = eventManager.lock()) {
|
||||
event->notify<EntityReloadEvent>(
|
||||
{entityid, wielder->getPosition(), weaponType});
|
||||
reloading = true;
|
||||
if (weaponAmmo < weaponMagSize) {
|
||||
weaponMag = weaponAmmo;
|
||||
weaponAmmo = 0;
|
||||
} else {
|
||||
weaponMag = weaponMagSize;
|
||||
weaponAmmo -= weaponMagSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Weapon::shoot()
|
||||
{
|
||||
bool shotsFired = false;
|
||||
if (wielder)
|
||||
{
|
||||
Uint32 currentTime = SDL_GetTicks();
|
||||
if (currentTime - lastFireTime >= fireSpeed && !reloading)
|
||||
{
|
||||
if (weaponMag > 0)
|
||||
{
|
||||
shotsFired = true;
|
||||
if (auto event = eventManager.lock())
|
||||
event->notify(std::make_shared<EntityFireEvent>(entityid, fireSpeed));
|
||||
if (!weaponScript || !weaponScript->lua["onShoot"].valid())
|
||||
{
|
||||
// create bullet using this generated data
|
||||
BulletData b = genBulletData();
|
||||
createBullet(b);
|
||||
weaponMag -= 1;
|
||||
}
|
||||
else {
|
||||
auto result = weaponScript->lua["onShoot"]();
|
||||
if (!result.valid())
|
||||
{
|
||||
sol::error err = result;
|
||||
std::cerr << "lua error: " << err.what() << std::endl;
|
||||
}
|
||||
weaponMag -= 1;
|
||||
}
|
||||
}
|
||||
else if (weaponMag <= 0) reload();
|
||||
lastFireTime = currentTime;
|
||||
}
|
||||
}
|
||||
return shotsFired;
|
||||
bool Weapon::shoot() {
|
||||
bool shotsFired = false;
|
||||
if (wielder) {
|
||||
Uint32 currentTime = SDL_GetTicks();
|
||||
if (currentTime - lastFireTime >= fireSpeed && !reloading) {
|
||||
if (weaponMag > 0) {
|
||||
shotsFired = true;
|
||||
if (auto event = eventManager.lock())
|
||||
event->notify<EntityFireEvent>(
|
||||
{entityid, fireSpeed, wielder->getPosition(), weaponType});
|
||||
if (!weaponScript || !weaponScript->lua["onShoot"].valid()) {
|
||||
// create bullet using this generated data
|
||||
BulletData b = genBulletData();
|
||||
createBullet(b);
|
||||
weaponMag -= 1;
|
||||
} else {
|
||||
auto result = weaponScript->lua["onShoot"]();
|
||||
if (!result.valid()) {
|
||||
sol::error err = result;
|
||||
std::cerr << "lua error: " << err.what() << std::endl;
|
||||
}
|
||||
// auto reload
|
||||
if ((weaponMag -= 1) <= 0)
|
||||
reload();
|
||||
}
|
||||
} else if (weaponMag <= 0)
|
||||
reload();
|
||||
lastFireTime = currentTime;
|
||||
}
|
||||
}
|
||||
return shotsFired;
|
||||
}
|
||||
|
||||
void Weapon::hookEventManager(std::weak_ptr<EventManager> eventManager)
|
||||
{
|
||||
this->eventManager = eventManager;
|
||||
void Weapon::hookEventManager(std::weak_ptr<EventManager> eventManager) {
|
||||
this->eventManager = eventManager;
|
||||
|
||||
for (auto& component : components)
|
||||
{
|
||||
if (component->getType() == Component::TYPE::ANIMATION)
|
||||
{
|
||||
auto animComponent = static_cast<AnimationComponent*>(component.get());
|
||||
animComponent->getAnimationSet()->attachEventManager(eventManager);
|
||||
}
|
||||
}
|
||||
for (auto &component : components) {
|
||||
if (component->getType() == Component::TYPE::ANIMATION) {
|
||||
auto animComponent = static_cast<AnimationComponent *>(component.get());
|
||||
animComponent->getAnimationSet()->attachEventManager(eventManager);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto event = this->eventManager.lock()) {
|
||||
std::weak_ptr<Weapon> selfWeak = shared_from_this();
|
||||
event->subscribe("OnAnimationFinished", [selfWeak](std::shared_ptr<Event> e) {
|
||||
if (auto self = selfWeak.lock()) {
|
||||
auto animFinished = std::static_pointer_cast<AnimationFinishedEvent>(e);
|
||||
if (animFinished->entityid == self->entityid && animFinished->animType == "reload")
|
||||
{
|
||||
if (self->reloading)
|
||||
{
|
||||
self->reloading = false;
|
||||
self->wasReloading = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (auto event = this->eventManager.lock()) {
|
||||
auto self = this;
|
||||
event->subscribe<AnimationFinishedEvent>(
|
||||
[self](const AnimationFinishedEvent &e) {
|
||||
if (self) {
|
||||
if (e.entityid == self->entityid && e.animType == "reload") {
|
||||
if (self->reloading) {
|
||||
self->reloading = false;
|
||||
self->wasReloading = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bulletManager->hookEventManager(eventManager);
|
||||
bulletManager->hookEventManager(eventManager);
|
||||
}
|
||||
|
||||
void Weapon::attachScript(const std::shared_ptr<WeaponScript>& script)
|
||||
{
|
||||
weaponScript = script;
|
||||
weaponScript->lua["weapon"] = shared_from_this();
|
||||
LOG(DEBUG, "Weapon state bound");
|
||||
void Weapon::attachScript(std::unique_ptr<WeaponScript> script) {
|
||||
weaponScript = std::move(script);
|
||||
weaponScript->lua["weapon"] = this;
|
||||
LOG(DEBUG, "Weapon state bound", NULL);
|
||||
}
|
||||
|
||||
void Weapon::onHitCallback(GameActor* target, PhysicsComponent* bullet, const glm::vec2& normal)
|
||||
{
|
||||
if (weaponScript && weaponScript->lua["onHit"].valid())
|
||||
{
|
||||
auto result = weaponScript->lua["onHit"](target, bullet, normal);
|
||||
if (!result.valid())
|
||||
{
|
||||
sol::error err = result;
|
||||
std::cerr << "lua error: " << err.what() << std::endl;
|
||||
}
|
||||
}
|
||||
void Weapon::onHitCallback(GameActor *target, PhysicsComponent *bullet,
|
||||
const glm::vec2 &normal) {
|
||||
if (weaponScript && weaponScript->lua["onHit"].valid()) {
|
||||
auto result = weaponScript->lua["onHit"](target, bullet, normal);
|
||||
if (!result.valid()) {
|
||||
sol::error err = result;
|
||||
std::cerr << "lua error: " << err.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Weapon::update(double deltaTime)
|
||||
{
|
||||
Entity::update(deltaTime);
|
||||
if (wielded)
|
||||
{
|
||||
// move the weapon into place as the wielder rotates and moves
|
||||
if (wielder)
|
||||
adjustWeapon();
|
||||
void Weapon::update(double deltaTime) {
|
||||
Entity::update(deltaTime);
|
||||
if (wielded) {
|
||||
// move the weapon into place as the wielder rotates and moves
|
||||
if (wielder)
|
||||
adjustWeapon();
|
||||
|
||||
for (auto& component : components)
|
||||
component->update();
|
||||
if (wasReloading)
|
||||
{
|
||||
wasReloading = false;
|
||||
if (auto event = eventManager.lock()) {
|
||||
event->notify(std::make_shared<EntityFinishReloadEvent>(entityid));
|
||||
}
|
||||
}
|
||||
}
|
||||
bulletManager->update(deltaTime);
|
||||
for (auto &component : components)
|
||||
component->update();
|
||||
if (wasReloading) {
|
||||
wasReloading = false;
|
||||
if (auto event = eventManager.lock()) {
|
||||
event->notify<EntityFinishReloadEvent>({entityid});
|
||||
}
|
||||
}
|
||||
}
|
||||
bulletManager->update(deltaTime);
|
||||
}
|
||||
|
||||
void Weapon::draw()
|
||||
{
|
||||
Entity::draw();
|
||||
if (wielded)
|
||||
{
|
||||
for (auto& component : components)
|
||||
{
|
||||
component->play();
|
||||
component->bind();
|
||||
component->render();
|
||||
}
|
||||
}
|
||||
//bulletManager->draw();
|
||||
void Weapon::draw() {
|
||||
Entity::draw();
|
||||
if (wielded) {
|
||||
for (auto &component : components) {
|
||||
component->play();
|
||||
component->bind();
|
||||
component->render();
|
||||
}
|
||||
}
|
||||
// bulletManager->draw();
|
||||
}
|
||||
|
||||
void Weapon::adjustWeapon()
|
||||
{
|
||||
float rotation = glm::radians(wielder->getRotation());
|
||||
void Weapon::adjustWeapon() {
|
||||
float rotation = glm::radians(wielder->getRotation());
|
||||
|
||||
glm::vec3 offset = glm::vec3(
|
||||
cos(rotation) * ((wielder->getScale().x) - weaponSize.x * 0.5f),
|
||||
sin(rotation) * ((wielder->getScale().y) - weaponSize.y * 0.5f),
|
||||
0.0f
|
||||
);
|
||||
glm::vec3 origin = wielder->getCenter() + offset;
|
||||
//origin.x += (weaponSize.x) * 0.25f;
|
||||
//origin.y += (weaponSize.y) * 0.25f;
|
||||
glm::vec3 offset = glm::vec3(
|
||||
cos(rotation) * ((wielder->getScale().x) - weaponSize.x * 0.5f),
|
||||
sin(rotation) * ((wielder->getScale().y) - weaponSize.y * 0.5f), 0.0f);
|
||||
glm::vec3 origin = wielder->getCenter() + offset;
|
||||
// origin.x += (weaponSize.x) * 0.25f;
|
||||
// origin.y += (weaponSize.y) * 0.25f;
|
||||
|
||||
// Flip the texture if the weapon is facing upwards or downwards
|
||||
Direction d = getDirectionFromRotation(glm::degrees(rotation));
|
||||
if ((lastDir == Direction::Up || lastDir == Direction::Left) &&
|
||||
(d == Direction::Down || d == Direction::Right) && !isFlipped())
|
||||
flip();
|
||||
if ((lastDir == Direction::Down || lastDir == Direction::Right) &&
|
||||
(d == Direction::Up || d == Direction::Left) && isFlipped())
|
||||
flip();
|
||||
|
||||
setRotation(wielder->getRotation() - 180);
|
||||
setPosition(origin);
|
||||
lastDir = getDirectionFromRotation(glm::degrees(rotation));
|
||||
// Flip the texture if the weapon is facing upwards or downwards
|
||||
Direction d = getDirectionFromRotation(glm::degrees(rotation));
|
||||
if ((lastDir == Direction::Up || lastDir == Direction::Left) &&
|
||||
(d == Direction::Down || d == Direction::Right) && !isFlipped())
|
||||
flip();
|
||||
if ((lastDir == Direction::Down || lastDir == Direction::Right) &&
|
||||
(d == Direction::Up || d == Direction::Left) && isFlipped())
|
||||
flip();
|
||||
|
||||
setRotation(wielder->getRotation() - 180);
|
||||
setPosition(origin);
|
||||
lastDir = getDirectionFromRotation(glm::degrees(rotation));
|
||||
}
|
||||
|
||||
Weapon::BulletData Weapon::genBulletData()
|
||||
{
|
||||
BulletData b;
|
||||
float rotation = glm::radians(wielder->getRotation());
|
||||
b.mass = 0.1f;
|
||||
glm::vec2 facing = glm::vec2(
|
||||
cos(rotation + glm::radians(static_cast<float>(bulletSpread->genFloat()))),
|
||||
sin(rotation + glm::radians(static_cast<float>(bulletSpread->genFloat())))
|
||||
);
|
||||
b.direction = glm::normalize(facing);
|
||||
b.sizeMod = bulletModifer->genFloat();
|
||||
b.speedMod = bulletModifer->genFloat();
|
||||
b.dropMod = bulletModifer->genFloat();
|
||||
Weapon::BulletData Weapon::genBulletData() {
|
||||
BulletData b;
|
||||
float rotation = glm::radians(wielder->getRotation());
|
||||
float spreadOffset =
|
||||
glm::radians(static_cast<float>(bulletSpread->genFloat()));
|
||||
b.mass = 0.1f;
|
||||
glm::vec2 facing =
|
||||
glm::vec2(cos(rotation + spreadOffset), sin(rotation + spreadOffset));
|
||||
b.direction = glm::normalize(facing);
|
||||
b.sizeMod = bulletModifer->genFloat();
|
||||
b.speedMod = bulletModifer->genFloat();
|
||||
b.dropMod = bulletModifer->genFloat();
|
||||
|
||||
double radius = wielder->getScale().x + (weaponSize.x * 0.5) + weaponOffset.x;
|
||||
double radius = wielder->getScale().x + (weaponSize.x * 0.5) + weaponOffset.x;
|
||||
|
||||
b.origin = glm::vec3(
|
||||
// x offset from the wielder
|
||||
wielder->getCenter().x + cos(rotation) * radius - sin(rotation) * weaponOffset.y,
|
||||
// y offset from the wielder
|
||||
wielder->getCenter().y + sin(rotation) * radius + cos(rotation) * weaponOffset.y,
|
||||
0.0f
|
||||
);
|
||||
b.origin.x -= ((bulletSize.x) * b.sizeMod) * 0.5f;
|
||||
b.origin.y -= ((bulletSize.y) * b.sizeMod) * 0.5f;
|
||||
return b;
|
||||
b.origin = glm::vec3(
|
||||
// x offset from the wielder
|
||||
wielder->getCenter().x + cos(rotation) * radius -
|
||||
sin(rotation) * weaponOffset.y,
|
||||
// y offset from the wielder
|
||||
wielder->getCenter().y + sin(rotation) * radius +
|
||||
cos(rotation) * weaponOffset.y,
|
||||
0.0f);
|
||||
b.origin.x -= ((bulletSize.x) * b.sizeMod) * 0.5f;
|
||||
b.origin.y -= ((bulletSize.y) * b.sizeMod) * 0.5f;
|
||||
return b;
|
||||
}
|
||||
|
||||
void Weapon::createBullet(const Weapon::BulletData& data)
|
||||
{
|
||||
auto bullet = std::make_shared<Bullet>(wielder->getEntityID(), bulletShaderID, data.origin, data.direction, bulletSpeed, bulletDrop, bulletSize);
|
||||
bullet->addComponent(bulletSprite.get());
|
||||
bullet->addPhysicsComponent(std::make_shared<PhysicsComponent>(PhysicsComponentFactory::makeBullet(wielder->getEntityID(), data.origin, data.mass, bulletSize.x / 2)));
|
||||
bullet->getPhysicsComponent()->rigidBody.velocity += bulletSpeed * glm::vec3(data.direction.x, data.direction.y, 0.f) / data.mass;
|
||||
void Weapon::createBullet(const Weapon::BulletData &data) {
|
||||
auto bullet = std::make_shared<Bullet>(wielder->getEntityID(), bulletShaderID,
|
||||
data.origin, data.direction,
|
||||
bulletSpeed, bulletDrop, bulletSize);
|
||||
bullet->addComponent(bulletSprite.get());
|
||||
bullet->addPhysicsComponent(
|
||||
std::make_shared<PhysicsComponent>(PhysicsComponentFactory::makeBullet(
|
||||
wielder->getEntityID(), data.origin, data.mass, bulletSize.x / 2)));
|
||||
bullet->getPhysicsComponent()->rigidBody.velocity +=
|
||||
bulletSpeed * data.direction / data.mass;
|
||||
|
||||
if (auto event = eventManager.lock())
|
||||
event->notify(std::make_shared<BulletFiredEvent>(bullet));
|
||||
bulletManager->addBullet(bullet);
|
||||
if (auto event = eventManager.lock())
|
||||
event->notify<BulletFiredEvent>({bullet});
|
||||
bulletManager->addBullet(bullet);
|
||||
}
|
||||
|
||||
/*
|
||||
!| SLATED FOR REMOVAL |!
|
||||
|
||||
// Swap the reload animation and the regular weapon animation
|
||||
void Weapon::swapSprites()
|
||||
{
|
||||
//std::swap(weaponSprites.first, weaponSprites.second);
|
||||
Weapon::~Weapon() {
|
||||
if (weaponScript) {
|
||||
weaponScript->lua["onShoot"] = sol::nil;
|
||||
weaponScript->lua.collect_gc();
|
||||
}
|
||||
}
|
||||
|
||||
void Weapon::checkAndFinishReload()
|
||||
{
|
||||
if (weaponSprites.first->kill() && reloading) {
|
||||
weaponSprites.first->idle();
|
||||
weaponSprites.first->reset();
|
||||
std::swap(weaponSprites.first, weaponSprites.second);
|
||||
reloading = false;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -3,17 +3,19 @@
|
|||
#include "utility/xmlloader.h"
|
||||
#include "utility/resourcemanager.h"
|
||||
#include "utility/events.h"
|
||||
#include "util.h"
|
||||
|
||||
Animation::Animation(const std::shared_ptr<AnimationData>& animData, ResourceManager* resourceManager) :
|
||||
animName(animData->name),
|
||||
animType(animData->type),
|
||||
Animation::Animation(const AnimationData* animData, ResourceManager* resourceManager) :
|
||||
spriteAtlas(resourceManager->loadSpriteAtlas(animData->spriteAtlas, animData->frameSize, animData->directional)),
|
||||
FPS(animData->FPS),
|
||||
currentFrame(0),
|
||||
cycles(0),
|
||||
isDirectional(animData->directional),
|
||||
isPlaying(isDirectional)
|
||||
{}
|
||||
{
|
||||
prefix = UTIL::parsePrefix(animData->id);
|
||||
type = UTIL::get_type(animData->id);
|
||||
}
|
||||
|
||||
void Animation::bind()
|
||||
{
|
||||
|
|
@ -74,24 +76,25 @@ void Animation::frameTick()
|
|||
}
|
||||
|
||||
AnimationSet::AnimationSet(const int& entityid) : entityid(entityid), isDirectional(false) {};
|
||||
AnimationSet::AnimationSet(const int& entityid, ResourceManager* resourceManager, std::vector<std::shared_ptr<AnimationData>> animSet) : entityid(entityid)
|
||||
AnimationSet::AnimationSet(const int& entityid, ResourceManager* resourceManager, std::vector<AnimationData*> animSet) : entityid(entityid)
|
||||
{
|
||||
curAnim = nullptr;
|
||||
for (const auto& anim : animSet)
|
||||
{
|
||||
anims.try_emplace(anim->type, std::make_unique<Animation>(anim, resourceManager));
|
||||
if (anim->type == "idle")
|
||||
curAnim = anims[anim->type].get();
|
||||
std::string type = UTIL::get_type(anim->id);
|
||||
anims.try_emplace(type, std::make_unique<Animation>(anim, resourceManager));
|
||||
if (type == "idle")
|
||||
curAnim = anims[type].get();
|
||||
}
|
||||
// if we don't have an idle animation, we are just gonna set the current animation to the top of the set
|
||||
if (curAnim == nullptr)
|
||||
curAnim = anims[animSet[0]->type].get();
|
||||
curAnim = anims[UTIL::get_type(animSet[0]->id)].get();
|
||||
isDirectional = curAnim->getDirectional();
|
||||
}
|
||||
AnimationSet::AnimationSet(const int& entityid, std::unordered_map<std::string, std::unique_ptr<Animation>> animations)
|
||||
: entityid(entityid), anims(std::move(animations))
|
||||
{
|
||||
for (auto& [type, anim] : animations)
|
||||
for (auto& [type, anim] : anims)
|
||||
{
|
||||
curAnim = anim.get();
|
||||
if (type == "idle")
|
||||
|
|
@ -109,18 +112,18 @@ void AnimationSet::attachEventManager(std::weak_ptr<EventManager> e)
|
|||
if (auto event = eventManager.lock()) {
|
||||
std::weak_ptr<AnimationSet> weakSelf = shared_from_this();
|
||||
|
||||
event->subscribe("OnDirectionChange", [weakSelf](std::shared_ptr<Event> e) {
|
||||
event->subscribe<DirectionChangeEvent>([weakSelf](const DirectionChangeEvent& e) {
|
||||
if (auto self = weakSelf.lock()) {
|
||||
auto directionEvent = std::static_pointer_cast<DirectionChangeEvent>(e);
|
||||
if (directionEvent->entityid == self->entityid)
|
||||
self->setFacingDir(directionEvent->direction);
|
||||
if (e.entityid == self->entityid) {
|
||||
Direction d = e.direction;
|
||||
self->setFacingDir(d);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
event->subscribe("OnEntityMove", [weakSelf](std::shared_ptr<Event> e) {
|
||||
event->subscribe<EntityMoveEvent>([weakSelf](const EntityMoveEvent& e) {
|
||||
if (auto self = weakSelf.lock()) {
|
||||
auto moveEvent = std::static_pointer_cast<EntityMoveEvent>(e);
|
||||
if (moveEvent->entityid == self->entityid)
|
||||
if (e.entityid == self->entityid)
|
||||
{
|
||||
if (self->isDirectional)
|
||||
{
|
||||
|
|
@ -133,10 +136,9 @@ void AnimationSet::attachEventManager(std::weak_ptr<EventManager> e)
|
|||
}
|
||||
});
|
||||
|
||||
event->subscribe("OnEntityStop", [weakSelf](std::shared_ptr<Event> e) {
|
||||
event->subscribe<EntityStopEvent>([weakSelf](const EntityStopEvent& e) {
|
||||
if (auto self = weakSelf.lock()) {
|
||||
auto stopEvent = std::static_pointer_cast<EntityStopEvent>(e);
|
||||
if (stopEvent->entityid == self->entityid)
|
||||
if (e.entityid == self->entityid)
|
||||
{
|
||||
if (self->isDirectional)
|
||||
{
|
||||
|
|
@ -149,51 +151,49 @@ void AnimationSet::attachEventManager(std::weak_ptr<EventManager> e)
|
|||
}
|
||||
});
|
||||
|
||||
event->subscribe("OnEntityReload", [weakSelf](std::shared_ptr<Event> e) {
|
||||
event->subscribe<EntityReloadEvent>([weakSelf](const EntityReloadEvent& e) {
|
||||
if (auto self = weakSelf.lock()) {
|
||||
auto reloadEvent = std::static_pointer_cast<EntityReloadEvent>(e);
|
||||
if (reloadEvent->entityid == self->entityid)
|
||||
if (e.entityid == self->entityid)
|
||||
{
|
||||
if (self->anims["reload"] != NULL)
|
||||
{
|
||||
self->curAnim = self->anims["reload"].get();
|
||||
self->curAnim->reset();
|
||||
self->curAnim = self->anims["reload"].get();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
event->subscribe("OnEntityFinishReload", [weakSelf](std::shared_ptr<Event> e) {
|
||||
event->subscribe<EntityFinishReloadEvent>([weakSelf](const EntityFinishReloadEvent& e) {
|
||||
if (auto self = weakSelf.lock()) {
|
||||
auto reloadEvent = std::static_pointer_cast<EntityFinishReloadEvent>(e);
|
||||
if (reloadEvent->entityid == self->entityid)
|
||||
if (e.entityid == self->entityid)
|
||||
{
|
||||
if (self->anims["idle"] != NULL)
|
||||
if (self->anims["idle"] != NULL) {
|
||||
self->curAnim->reset();
|
||||
self->curAnim = self->anims["idle"].get();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
event->subscribe("OnEntityFire", [weakSelf](std::shared_ptr<Event> e) {
|
||||
event->subscribe<EntityFireEvent>([weakSelf](const EntityFireEvent& e) {
|
||||
if (auto self = weakSelf.lock()) {
|
||||
auto fireEvent = std::static_pointer_cast<EntityFireEvent>(e);
|
||||
if (fireEvent->entityid == self->entityid)
|
||||
if (e.entityid == self->entityid)
|
||||
{
|
||||
if (self->anims["fire"] != NULL)
|
||||
{
|
||||
self->curAnim = self->anims["fire"].get();
|
||||
self->curAnim->reset();
|
||||
float newFPS = (1000.f / fireEvent->fireDelay) * 15.f;
|
||||
float newFPS = (1000.f / e.fireDelay) * 15.f;
|
||||
self->curAnim->setFPS(newFPS);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
event->subscribe("OnAnimationFinished", [weakSelf](std::shared_ptr<Event> e) {
|
||||
event->subscribe<AnimationFinishedEvent>([weakSelf](const AnimationFinishedEvent& e) {
|
||||
if (auto self = weakSelf.lock()) {
|
||||
auto animEvent = std::static_pointer_cast<AnimationFinishedEvent>(e);
|
||||
if (animEvent->entityid == self->entityid && animEvent->animType == "fire")
|
||||
if (e.entityid == self->entityid && e.animType == "fire")
|
||||
{
|
||||
if (self->anims["idle"] != NULL)
|
||||
self->curAnim = self->anims["idle"].get();
|
||||
|
|
@ -220,7 +220,7 @@ void AnimationSet::draw()
|
|||
// If the animation has cycled, we send this event after every cycle
|
||||
if ((curAnim->getCycles() - lastCycle) > 0) {
|
||||
if (auto event = eventManager.lock()) {
|
||||
event->notify(std::make_shared<AnimationFinishedEvent>(entityid, curAnim->getType()));
|
||||
event->notify<AnimationFinishedEvent>({entityid, curAnim->getType()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
YuppleMayham/src/graphics/background.cpp
Normal file
23
YuppleMayham/src/graphics/background.cpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#include "graphics/background.h"
|
||||
|
||||
Background::Background(const std::string& fileName)
|
||||
{
|
||||
// This will fallback to the generic shader.
|
||||
shaderID = 0;
|
||||
texture = new Texture();
|
||||
quad = new ScreenQuad();
|
||||
if (!texture->loadTexture(fileName.c_str()))
|
||||
LOG(ERROR, "Failed to load background texture: '{}'", fileName);
|
||||
}
|
||||
|
||||
void Background::draw()
|
||||
{
|
||||
texture->bind();
|
||||
quad->draw();
|
||||
}
|
||||
|
||||
Background::~Background()
|
||||
{
|
||||
delete texture;
|
||||
delete quad;
|
||||
}
|
||||
28
YuppleMayham/src/graphics/glwindow.cpp
Normal file
28
YuppleMayham/src/graphics/glwindow.cpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#include "graphics/glwindow.h"
|
||||
#include <SDL_error.h>
|
||||
#include <SDL_video.h>
|
||||
|
||||
bool GLWindow::Init() {
|
||||
window = SDL_CreateWindow(name, SDL_WINDOWPOS_UNDEFINED,
|
||||
SDL_WINDOWPOS_UNDEFINED, w, h, SDL_WINDOW_OPENGL);
|
||||
if (!window) {
|
||||
LOG(ERROR, "Failed to create window! {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
glContext = SDL_GL_CreateContext(window);
|
||||
if (!glContext) {
|
||||
LOG(ERROR, "Failed to create opengl context! {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLWindow::resizeWindow(size_t width, size_t height) {
|
||||
w = width;
|
||||
h = height;
|
||||
}
|
||||
|
||||
GLWindow::~GLWindow() {
|
||||
SDL_GL_DeleteContext(glContext);
|
||||
SDL_DestroyWindow(window);
|
||||
}
|
||||
|
|
@ -1,9 +1,6 @@
|
|||
#include "graphics/instancedraw.h"
|
||||
#include "graphics/texture.h"
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <iostream>
|
||||
|
||||
TileTextureInstance::TileTextureInstance(const char* texturePath)
|
||||
{
|
||||
texture = new Texture();
|
||||
|
|
@ -127,3 +124,14 @@ void TileTextureInstance::draw()
|
|||
glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr, numOfInstances);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
TileTextureInstance::~TileTextureInstance()
|
||||
{
|
||||
if (texture)
|
||||
delete texture;
|
||||
else if (textures)
|
||||
delete textures;
|
||||
glDeleteBuffers(1, &VBO);
|
||||
glDeleteBuffers(1, &EBO);
|
||||
glDeleteVertexArrays(1, &VAO);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Postprocessor::Postprocessor(const std::shared_ptr<ResourceManager>& resourceMan
|
|||
);
|
||||
// TODO: ADD FALLBACK SHADER!
|
||||
if (postProcessShader == nullptr) {
|
||||
LOG(ERROR, "Failed to load post processing shader! 'shaders/GL_postprocess.*");
|
||||
LOG(ERROR, "Failed to load post processing shader! 'shaders/GL_postprocess.*", NULL);
|
||||
assert(postProcessShader != nullptr); // force crash
|
||||
}
|
||||
postProcessShader->use();
|
||||
|
|
@ -21,9 +21,9 @@ Postprocessor::Postprocessor(const std::shared_ptr<ResourceManager>& resourceMan
|
|||
|
||||
unsigned int uboIndex = glGetUniformBlockIndex(postProcessShader->ID, "uPostProcess");
|
||||
if (uboIndex == GL_INVALID_INDEX) {
|
||||
LOG(ERROR, "Could not find 'uPostProcess'!");
|
||||
LOG(ERROR, "Could not find 'uPostProcess'!", NULL);
|
||||
}
|
||||
unsigned int bindPoint = 0;
|
||||
unsigned int bindPoint = 1;
|
||||
glUniformBlockBinding(postProcessShader->ID, uboIndex, bindPoint);
|
||||
|
||||
glGenBuffers(1, &UBO);
|
||||
|
|
@ -70,7 +70,6 @@ void Postprocessor::applyPostProcess(bool worldOrHud)
|
|||
postProcessData.dt = curTime - lastTime;
|
||||
|
||||
tickEffectTime(worldOrHud);
|
||||
LOG(DEBUG, "Blur duration: {}", postProcessData.blurDuration);
|
||||
|
||||
postProcessShader->use();
|
||||
postProcessShader->setBool("worldOrHud", worldOrHud);
|
||||
|
|
@ -117,4 +116,4 @@ void Postprocessor::tickEffectTime(bool worldOrHud)
|
|||
RemoveEffects(SHAKE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
#include "graphics/quad.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
Quad::Quad(const float* vertexData)
|
||||
{
|
||||
std::memcpy(vertices, vertexData, sizeof(vertices));
|
||||
|
|
@ -43,4 +41,4 @@ Quad::~Quad()
|
|||
glDeleteBuffers(1, &VBO);
|
||||
glDeleteBuffers(1, &EBO);
|
||||
glDeleteVertexArrays(1, &VAO);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,218 +1,227 @@
|
|||
#include "graphics/renderer.h"
|
||||
#include "graphics/shader.h"
|
||||
#include "utility/resourcemanager.h"
|
||||
#include "utility/logger.h"
|
||||
#include "utility/events.h"
|
||||
#include "utility/logger.h"
|
||||
#include "utility/resourcemanager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <graphics/postprocess.h>
|
||||
#include <memory>
|
||||
|
||||
Renderer::Renderer(const std::shared_ptr<ResourceManager>& r)
|
||||
: resourceManager(r)
|
||||
{
|
||||
projMat = glm::mat4(0.f);
|
||||
viewMat = glm::mat4(0.f);
|
||||
|
||||
initFrameBuffers();
|
||||
screenQuad = std::make_unique<ScreenQuad>();
|
||||
postProcessor = std::make_unique<Postprocessor>(r);
|
||||
Renderer::Renderer(const std::shared_ptr<ResourceManager> &r,
|
||||
const std::shared_ptr<GLWindow> &w)
|
||||
: resourceManager(r), glWindow(w) {
|
||||
initFrameBuffers();
|
||||
initUniformBuffers();
|
||||
screenQuad = std::make_unique<ScreenQuad>();
|
||||
postProcessor = std::make_unique<Postprocessor>(r);
|
||||
}
|
||||
|
||||
void Renderer::hookEventManager(std::weak_ptr<EventManager> eventManager)
|
||||
{
|
||||
if (auto e = eventManager.lock()) {
|
||||
e->subscribe("OnScreenShake", [this](std::shared_ptr<Event> event) {
|
||||
auto shakeEvent = std::dynamic_pointer_cast<ScreenShakeEvent>(event);
|
||||
postProcessor->ApplyEffect(Postprocessor::SHAKE,
|
||||
shakeEvent->intensity,
|
||||
shakeEvent->duration
|
||||
);
|
||||
});
|
||||
e->subscribe("OnScreenBlur", [this](std::shared_ptr<Event> event) {
|
||||
auto blurEvent = std::dynamic_pointer_cast<ScreenBlurEvent>(event);
|
||||
postProcessor->ApplyEffect(Postprocessor::BLUR,
|
||||
blurEvent->intensity,
|
||||
blurEvent->duration
|
||||
);
|
||||
});
|
||||
void Renderer::hookEventManager(std::weak_ptr<EventManager> eventManager) {
|
||||
if (auto e = eventManager.lock()) {
|
||||
e->subscribe<ScreenShakeEvent>([this](const ScreenShakeEvent &event) {
|
||||
postProcessor->ApplyEffect(Postprocessor::SHAKE, event.intensity,
|
||||
event.duration);
|
||||
});
|
||||
e->subscribe<ScreenBlurEvent>([this](const ScreenBlurEvent &event) {
|
||||
postProcessor->ApplyEffect(Postprocessor::BLUR, event.intensity,
|
||||
event.duration);
|
||||
});
|
||||
e->subscribe<WindowResizeEvent>(
|
||||
[this](const WindowResizeEvent &event) { resizeFrameBuffers(); });
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the framebuffers used by the renderer to allow for post-processing
|
||||
// effects
|
||||
void Renderer::initFrameBuffers() {
|
||||
glGenFramebuffers(1, &worldBuffer.frame);
|
||||
glGenFramebuffers(1, &hudBuffer.frame);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, worldBuffer.frame);
|
||||
|
||||
// World buffer creation
|
||||
glGenTextures(1, &worldBuffer.texture);
|
||||
glBindTexture(GL_TEXTURE_2D, worldBuffer.texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glWindow->Width(), glWindow->Height(),
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// Attaching empty texture as color buffer for framebuffer
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
worldBuffer.texture, 0);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
LOG(ERROR, "Failed to complete world framebuffer: {}",
|
||||
glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
||||
assert(1 == 0); // force crash
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// same thing is done for the hud texture
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, hudBuffer.frame);
|
||||
|
||||
glGenTextures(1, &hudBuffer.texture);
|
||||
glBindTexture(GL_TEXTURE_2D, hudBuffer.texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glWindow->Width(), glWindow->Height(),
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
hudBuffer.texture, 0);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
LOG(ERROR, "Failed to complete hud framebuffer: {}",
|
||||
glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
||||
assert(1 == 0); // force crash
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void Renderer::resizeFrameBuffers() {
|
||||
glDeleteFramebuffers(1, &worldBuffer.frame);
|
||||
glDeleteFramebuffers(1, &hudBuffer.frame);
|
||||
glDeleteTextures(1, &worldBuffer.texture);
|
||||
glDeleteTextures(1, &hudBuffer.texture);
|
||||
|
||||
initFrameBuffers();
|
||||
}
|
||||
|
||||
void Renderer::initUniformBuffers() {
|
||||
glGenBuffers(1, &uboMatrices);
|
||||
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
|
||||
glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, uboMatrices);
|
||||
// glBindBufferRange(GL_UNIFORM_BUFFER, 0, uboMatrices, 0, 2 *
|
||||
// sizeof(glm::mat4));
|
||||
}
|
||||
|
||||
void Renderer::setProjAndViewMatrix(const glm::mat4 &proj,
|
||||
const glm::mat4 &view) {
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4),
|
||||
glm::value_ptr(proj));
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4),
|
||||
glm::value_ptr(view));
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
|
||||
void Renderer::addDrawable(RenderLayer renderLayer, Drawable *drawable) {
|
||||
if (renderLayer == RenderLayer::HUD || renderLayer == RenderLayer::Menu)
|
||||
HUDLayerPool[renderLayer].push_back(drawable);
|
||||
else
|
||||
worldLayerPool[renderLayer].push_back(drawable);
|
||||
}
|
||||
|
||||
void Renderer::removeDrawable(RenderLayer renderLayer, Drawable *drawable) {
|
||||
auto erase = [&](auto &pool) {
|
||||
pool.erase(std::remove(pool.begin(), pool.end(), drawable), pool.end());
|
||||
};
|
||||
if (renderLayer == RenderLayer::HUD || renderLayer == RenderLayer::Menu) {
|
||||
erase(HUDLayerPool[renderLayer]);
|
||||
} else
|
||||
erase(worldLayerPool[renderLayer]);
|
||||
}
|
||||
|
||||
void Renderer::render() {
|
||||
// Bind the world frame buffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, worldBuffer.frame);
|
||||
// clear color and depth buffer
|
||||
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
renderPool(worldLayerPool);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, hudBuffer.frame);
|
||||
glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
renderPool(HUDLayerPool);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, worldBuffer.texture);
|
||||
postProcessor->applyPostProcess(0);
|
||||
screenQuad->draw();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, hudBuffer.texture);
|
||||
// postProcessor->applyPostProcess(1);
|
||||
screenQuad->draw();
|
||||
}
|
||||
|
||||
void Renderer::renderPool(auto &layerPool) {
|
||||
sortLayerPool(layerPool);
|
||||
Shader *curShader = nullptr;
|
||||
for (const auto &layer : renderingOrder) {
|
||||
unsigned curShaderID = static_cast<unsigned>(-1);
|
||||
for (const auto &item : layerPool[layer]) {
|
||||
if (item->getShaderID() != curShaderID) {
|
||||
curShaderID = item->getShaderID();
|
||||
curShader = resourceManager->getShaderByID(curShaderID);
|
||||
curShader->use();
|
||||
}
|
||||
if (!item->getOneShotUniforms().empty()) {
|
||||
uploadUniforms(curShaderID, item->getOneShotUniforms());
|
||||
item->clearOneShot();
|
||||
}
|
||||
uploadUniforms(curShaderID, item->getUniforms());
|
||||
item->clearUniforms();
|
||||
|
||||
item->draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the framebuffers used by the renderer to allow for post-processing effects
|
||||
void Renderer::initFrameBuffers()
|
||||
{
|
||||
glGenFramebuffers(1, &worldBuffer.frame);
|
||||
glGenFramebuffers(1, &hudBuffer.frame);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, worldBuffer.frame);
|
||||
|
||||
// World buffer creation
|
||||
glGenTextures(1, &worldBuffer.texture);
|
||||
glBindTexture(GL_TEXTURE_2D, worldBuffer.texture);
|
||||
// !!! NEED TO CREATE STATIC WINDOW SIZING !!!
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 800, 600, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
//Attaching empty texture as color buffer for framebuffer
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, worldBuffer.texture, 0);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
LOG(ERROR, "Failed to complete world framebuffer: {}", glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
||||
assert(1 == 0); // force crash
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// same thing is done for the hud texture
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, hudBuffer.frame);
|
||||
|
||||
glGenTextures(1, &hudBuffer.texture);
|
||||
glBindTexture(GL_TEXTURE_2D, hudBuffer.texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 800, 600, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, hudBuffer.texture, 0);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
LOG(ERROR, "Failed to complete hud framebuffer: {}", glCheckFramebufferStatus(GL_FRAMEBUFFER));
|
||||
assert(1 == 0); // force crash
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
void Renderer::clear() {
|
||||
worldLayerPool.clear();
|
||||
HUDLayerPool.clear();
|
||||
}
|
||||
|
||||
void Renderer::setProjAndViewMatrix(const glm::mat4& proj, const glm::mat4& view)
|
||||
{
|
||||
projMat = proj;
|
||||
viewMat = view;
|
||||
void Renderer::uploadUniforms(const unsigned shaderID,
|
||||
const std::vector<Uniform> &uniforms) {
|
||||
Shader *shader = resourceManager->getShaderByID(shaderID);
|
||||
if (shader == nullptr) {
|
||||
LOG(ERROR, "No shader found with id {} !", shaderID);
|
||||
return;
|
||||
}
|
||||
for (const auto &uniform : uniforms) {
|
||||
std::visit(
|
||||
[&](auto &&arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, bool>) {
|
||||
shader->setBool(uniform.name, arg);
|
||||
} else if constexpr (std::is_same_v<T, int>) {
|
||||
shader->setInt(uniform.name, arg);
|
||||
} else if constexpr (std::is_same_v<T, float>) {
|
||||
shader->setFloat(uniform.name, arg);
|
||||
} else if constexpr (std::is_same_v<T, glm::vec2>) {
|
||||
shader->setVec2(uniform.name, glm::value_ptr(arg));
|
||||
} else if constexpr (std::is_same_v<T, glm::mat4>) {
|
||||
shader->setMatrix4f(uniform.name, glm::value_ptr(arg));
|
||||
}
|
||||
},
|
||||
uniform.value);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::addDrawable(RenderLayer renderLayer, std::shared_ptr<Drawable> drawable)
|
||||
{
|
||||
if (renderLayer == RenderLayer::HUD || renderLayer == RenderLayer::Menu)
|
||||
HUDLayerPool[renderLayer].push_back(drawable);
|
||||
else
|
||||
worldLayerPool[renderLayer].push_back(drawable);
|
||||
void Renderer::sortLayerPool(auto &layerPool) {
|
||||
// Sort by shader id, this works to batch shaders together to avoid shader
|
||||
// switching too much
|
||||
for (auto &[_, pool] : layerPool) {
|
||||
std::sort(pool.begin(), pool.end(),
|
||||
[](const Drawable *a, const Drawable *b) {
|
||||
return a->getShaderID() < b->getShaderID();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::removeDrawable(RenderLayer renderLayer, std::shared_ptr<Drawable> drawable)
|
||||
{
|
||||
auto erase = [&](auto& pool) {
|
||||
pool.erase(std::remove(pool.begin(), pool.end(), drawable), pool.end());
|
||||
};
|
||||
if (renderLayer == RenderLayer::HUD || renderLayer == RenderLayer::Menu) {
|
||||
erase(HUDLayerPool[renderLayer]);
|
||||
}
|
||||
else
|
||||
erase(worldLayerPool[renderLayer]);
|
||||
}
|
||||
|
||||
void Renderer::render()
|
||||
{
|
||||
// Bind the world frame buffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, worldBuffer.frame);
|
||||
// clear color and depth buffer
|
||||
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
renderPool(worldLayerPool);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, hudBuffer.frame);
|
||||
glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
renderPool(HUDLayerPool);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, worldBuffer.texture);
|
||||
postProcessor->applyPostProcess(0);
|
||||
screenQuad->draw();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, hudBuffer.texture);
|
||||
postProcessor->applyPostProcess(1);
|
||||
screenQuad->draw();
|
||||
}
|
||||
|
||||
void Renderer::renderPool(auto& layerPool)
|
||||
{
|
||||
sortLayerPool(layerPool);
|
||||
Shader* curShader = nullptr;
|
||||
for (const auto& layer : renderingOrder) {
|
||||
unsigned curShaderID = static_cast<unsigned>(-1);
|
||||
for (const auto& item : layerPool[layer]) {
|
||||
if (item->getShaderID() != curShaderID) {
|
||||
curShaderID = item->getShaderID();
|
||||
curShader = resourceManager->getShaderByID(curShaderID);
|
||||
|
||||
if (curShader) {
|
||||
curShader->use();
|
||||
curShader->setMatrix4f("projection", glm::value_ptr(projMat));
|
||||
curShader->setMatrix4f("view", glm::value_ptr(viewMat));
|
||||
} else {
|
||||
LOG(ERROR, "Shader with ID {} not found!", curShaderID);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!item->getOneShotUniforms().empty()) {
|
||||
uploadUniforms(curShaderID, item->getOneShotUniforms());
|
||||
item->clearOneShot();
|
||||
}
|
||||
uploadUniforms(curShaderID, item->getUniforms());
|
||||
item->clearUniforms();
|
||||
|
||||
item->draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::clear()
|
||||
{
|
||||
worldLayerPool.clear();
|
||||
HUDLayerPool.clear();
|
||||
}
|
||||
|
||||
void Renderer::uploadUniforms(const unsigned shaderID, const std::vector<Uniform>& uniforms)
|
||||
{
|
||||
Shader *shader = resourceManager->getShaderByID(shaderID);
|
||||
if (shader == nullptr) {
|
||||
LOG(ERROR, "No shader found with id {} !", shaderID);
|
||||
return;
|
||||
}
|
||||
for (const auto& uniform : uniforms) {
|
||||
std::visit([&](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, bool>) {
|
||||
shader->setBool(uniform.name, arg);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, int>) {
|
||||
shader->setInt(uniform.name, arg);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, float>) {
|
||||
shader->setFloat(uniform.name, arg);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, glm::vec2>) {
|
||||
shader->setVec2(uniform.name, glm::value_ptr(arg));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, glm::mat4>) {
|
||||
shader->setMatrix4f(uniform.name, glm::value_ptr(arg));
|
||||
}
|
||||
}, uniform.value);
|
||||
}
|
||||
}
|
||||
void Renderer::sortLayerPool(auto& layerPool)
|
||||
{
|
||||
// Sort by shader id, this works to batch shaders together to avoid shader switching too much
|
||||
for (auto& [_,pool] : layerPool) {
|
||||
std::sort(pool.begin(), pool.end(),
|
||||
[](const std::shared_ptr<Drawable>& a, const std::shared_ptr<Drawable>& b) {
|
||||
return a->getShaderID() < b->getShaderID();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,37 @@
|
|||
#include "utility/logger.h"
|
||||
#include <cassert>
|
||||
|
||||
Shader::Shader(const char* vertexPath, const char* fragmentPath)
|
||||
// Load a generic fallback shader
|
||||
Shader::Shader()
|
||||
{
|
||||
std::string vertexSource = {
|
||||
// generic vertex shader program
|
||||
"#version 330 core\n"
|
||||
"layout (location = 0) in vec3 aPos;\n"
|
||||
"layout (location = 1) in vec2 aTexCoord;\n"
|
||||
"out vec2 texCoord;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" texCoord = aTexCoord;\n"
|
||||
" gl_Position = vec4(aPos, 1.0);\n"
|
||||
"}\0"
|
||||
};
|
||||
std::string fragmentSource = {
|
||||
// generic fragment shader program
|
||||
"#version 330 core\n"
|
||||
"in vec2 texCoord;\n"
|
||||
"out vec4 FragColor;\n"
|
||||
"uniform sampler2D sprite;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" FragColor = texture(sprite, texCoord);\n"
|
||||
"}\0"
|
||||
};
|
||||
compileAndLink(vertexSource.c_str(), fragmentSource.c_str());
|
||||
valid = true;
|
||||
}
|
||||
|
||||
Shader::Shader(const char* vertPath, const char* fragPath) : vertexPath(vertPath), fragmentPath(fragPath)
|
||||
{
|
||||
std::string vertexSource;
|
||||
std::string fragmentSource;
|
||||
|
|
@ -35,7 +65,15 @@ Shader::Shader(const char* vertexPath, const char* fragmentPath)
|
|||
}
|
||||
const char* vSource = vertexSource.c_str();
|
||||
const char* fSource = fragmentSource.c_str();
|
||||
if (!compileAndLink(vertexSource.c_str(), fragmentSource.c_str())) {
|
||||
LOG(ERROR, "Failed to compile and/or link shader! Check error details for more information.\nUsing fallback shader!", NULL);
|
||||
valid = false;
|
||||
}
|
||||
valid = true;
|
||||
}
|
||||
|
||||
bool Shader::compileAndLink(const char *vSource, const char *fSource)
|
||||
{
|
||||
unsigned int vertexid, fragmentid;
|
||||
char infoLog[512];
|
||||
int success;
|
||||
|
|
@ -50,6 +88,7 @@ Shader::Shader(const char* vertexPath, const char* fragmentPath)
|
|||
{
|
||||
glGetShaderInfoLog(vertexid, 512, NULL, infoLog);
|
||||
LOG(ERROR, "VERTEX SHADER '{}' COMPILE ERROR\n{}", vertexPath, infoLog);
|
||||
return false;
|
||||
}
|
||||
|
||||
//compile fragment shader
|
||||
|
|
@ -62,6 +101,7 @@ Shader::Shader(const char* vertexPath, const char* fragmentPath)
|
|||
{
|
||||
glGetShaderInfoLog(fragmentid, 512, NULL, infoLog);
|
||||
LOG(ERROR, "FRAGMENT SHADER '{}' COMPILE ERROR\n{}", fragmentPath, infoLog);
|
||||
return false;
|
||||
}
|
||||
|
||||
//create and link program with compiled shaders
|
||||
|
|
@ -75,10 +115,14 @@ Shader::Shader(const char* vertexPath, const char* fragmentPath)
|
|||
{
|
||||
glGetProgramInfoLog(ID, 512, NULL, infoLog);
|
||||
LOG(ERROR, "PROGRAM LINKER ERROR\n{}", infoLog);
|
||||
return false;
|
||||
}
|
||||
|
||||
glDeleteShader(vertexid);
|
||||
glDeleteShader(fragmentid);
|
||||
|
||||
glUniformBlockBinding(ID, glGetUniformBlockIndex(ID, "Matrices"), 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Shader::setFloat(const std::string& name, float value)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
#include "graphics/sprite.h"
|
||||
#include "graphics/texture.h"
|
||||
#include "graphics/quad.h"
|
||||
#include "util.h"
|
||||
#include "utility/events.h"
|
||||
|
||||
bool Sprite::loaded() const { return (texture != nullptr); }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,158 +1,129 @@
|
|||
#include "graphics/texture.h"
|
||||
#include "utility/logger.h"
|
||||
#include "util.h"
|
||||
#include "utility/logger.h"
|
||||
|
||||
#include <SDL_image.h>
|
||||
#include <glad/glad.h>
|
||||
bool Texture::loadTexture(const char *imagePath) {
|
||||
SDL_Surface *buffer = IMG_Load(imagePath);
|
||||
if (!buffer)
|
||||
ERROR_LOG("Failed to load image file: {}", imagePath);
|
||||
// UTIL::flip_surface(buffer);
|
||||
|
||||
bool Texture::loadTexture(const char* imagePath)
|
||||
{
|
||||
SDL_Surface* buffer = IMG_Load(imagePath);
|
||||
if (!buffer)
|
||||
ERROR_LOG("Failed to load image file: {}", imagePath);
|
||||
//UTIL::flip_surface(buffer);
|
||||
glGenTextures(1, &ID);
|
||||
/*
|
||||
GLenum error = glGetError();
|
||||
if(error != GL_NO_ERROR) {
|
||||
std::cout << "OpenGL error: " << error << std::endl;
|
||||
}
|
||||
*/
|
||||
|
||||
glGenTextures(1, &ID);
|
||||
/*
|
||||
GLenum error = glGetError();
|
||||
if(error != GL_NO_ERROR) {
|
||||
std::cout << "OpenGL error: " << error << std::endl;
|
||||
}
|
||||
*/
|
||||
glBindTexture(GL_TEXTURE_2D, ID);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, ID);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, buffer->w, buffer->h, 0, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, buffer->pixels);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, buffer->w, buffer->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer->pixels);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glGenerateMipmap(ID);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glGenerateMipmap(ID);
|
||||
textureWidth = buffer->w;
|
||||
textureHeight = buffer->h;
|
||||
|
||||
textureWidth = buffer->w;
|
||||
textureHeight = buffer->h;
|
||||
SDL_FreeSurface(buffer);
|
||||
|
||||
SDL_FreeSurface(buffer);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Texture::bind()
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, ID);
|
||||
void Texture::bind() {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, ID);
|
||||
}
|
||||
|
||||
Texture::~Texture() { glDeleteTextures(1, &ID); }
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
glDeleteTextures(1, &ID);
|
||||
bool TextureArray::loadTextures(std::vector<const char *> imagePaths) {
|
||||
std::vector<SDL_Surface *> surfaces;
|
||||
surfaces.resize(imagePaths.size());
|
||||
// Fill surfaces vector
|
||||
for (int i = 0; i < imagePaths.size(); ++i) {
|
||||
surfaces[i] = IMG_Load(imagePaths[i]);
|
||||
if (!surfaces[i])
|
||||
ERROR_LOG("Failed to load image file: {}", imagePaths[i]);
|
||||
}
|
||||
if (!adjustCanvasSizes(surfaces))
|
||||
ERROR_LOG(
|
||||
"Failed to adjust canvas size of images! \n Make sure to check that "
|
||||
"every tileset has square dimensions! (512x512, 756x756 ... etc)",
|
||||
NULL);
|
||||
if (surfaces.empty())
|
||||
ERROR_LOG("No surfaces created!", NULL);
|
||||
numOfLayers = imagePaths.size();
|
||||
|
||||
glGenTextures(1, &ID);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, ID);
|
||||
|
||||
// Creating the texture array all of our textures will live in.
|
||||
// adjustCanvasSizes makes every image the same size, so we can just use the
|
||||
// first surface in the list of surfaces
|
||||
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, surfaces[0]->w, surfaces[0]->h,
|
||||
(GLsizei)numOfLayers, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
|
||||
for (int layer = 0; layer < numOfLayers; ++layer) {
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, layer, surfaces[layer]->w,
|
||||
surfaces[layer]->h, 1, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
surfaces[layer]->pixels);
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
|
||||
|
||||
for (auto &surface : surfaces)
|
||||
SDL_FreeSurface(surface);
|
||||
surfaces.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureArray::loadTextures(std::vector<const char*> imagePaths)
|
||||
{
|
||||
std::vector<SDL_Surface*> surfaces;
|
||||
surfaces.resize(imagePaths.size());
|
||||
// Fill surfaces vector
|
||||
for (int i = 0; i < imagePaths.size(); ++i)
|
||||
{
|
||||
surfaces[i] = IMG_Load(imagePaths[i]);
|
||||
if (!surfaces[i])
|
||||
ERROR_LOG("Failed to load image file: {}", imagePaths[i]);
|
||||
}
|
||||
if (!adjustCanvasSizes(surfaces))
|
||||
ERROR_LOG("Failed to adjust canvas size of images! \n Make sure to check that every tileset has square dimensions! (512x512, 756x756 ... etc)");
|
||||
if (surfaces.empty())
|
||||
ERROR_LOG("No surfaces created!");
|
||||
numOfLayers = imagePaths.size();
|
||||
|
||||
glGenTextures(1, &ID);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, ID);
|
||||
|
||||
// Creating the texture array all of our textures will live in.
|
||||
// adjustCanvasSizes makes every image the same size, so we can just use the first
|
||||
// surface in the list of surfaces
|
||||
glTexImage3D(GL_TEXTURE_2D_ARRAY,
|
||||
0,
|
||||
GL_RGBA,
|
||||
surfaces[0]->w,
|
||||
surfaces[0]->h,
|
||||
(GLsizei)numOfLayers,
|
||||
0,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
nullptr);
|
||||
|
||||
for (int layer = 0; layer < numOfLayers; ++layer)
|
||||
{
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY,
|
||||
0,
|
||||
0, 0, layer,
|
||||
surfaces[layer]->w,
|
||||
surfaces[layer]->h,
|
||||
1,
|
||||
GL_RGBA,
|
||||
GL_UNSIGNED_BYTE,
|
||||
surfaces[layer]->pixels);
|
||||
}
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
|
||||
|
||||
for (auto& surface : surfaces)
|
||||
SDL_FreeSurface(surface);
|
||||
surfaces.clear();
|
||||
|
||||
return true;
|
||||
void TextureArray::bind() {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, ID);
|
||||
}
|
||||
|
||||
void TextureArray::bind()
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, ID);
|
||||
bool TextureArray::adjustCanvasSizes(std::vector<SDL_Surface *> &surfaces) {
|
||||
int maxWidth = 0;
|
||||
int maxHeight = 0;
|
||||
for (auto &surface : surfaces) {
|
||||
if (surface->w != surface->h)
|
||||
ERROR_LOG("Image must be a square!", NULL);
|
||||
|
||||
if (surface->w > maxWidth)
|
||||
maxWidth = surface->w;
|
||||
if (surface->h > maxHeight)
|
||||
maxHeight = surface->h;
|
||||
textures.push_back(TextureData({surface->w, surface->h}));
|
||||
}
|
||||
for (int i = 0; i < surfaces.size(); ++i) {
|
||||
SDL_Surface *canvas = SDL_CreateRGBSurface(
|
||||
0, maxWidth, maxHeight, surfaces[i]->format->BitsPerPixel,
|
||||
surfaces[i]->format->Rmask, surfaces[i]->format->Gmask,
|
||||
surfaces[i]->format->Bmask, surfaces[i]->format->Amask);
|
||||
|
||||
SDL_FillRect(canvas, NULL, SDL_MapRGBA(canvas->format, 0, 0, 0, 0));
|
||||
|
||||
SDL_BlitSurface(surfaces[i], NULL, canvas, NULL);
|
||||
|
||||
SDL_FreeSurface(surfaces[i]);
|
||||
surfaces[i] = canvas;
|
||||
}
|
||||
|
||||
canvasWidth = maxWidth;
|
||||
canvasHeight = maxHeight;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureArray::adjustCanvasSizes(std::vector<SDL_Surface*>& surfaces)
|
||||
{
|
||||
int maxWidth = 0;
|
||||
int maxHeight = 0;
|
||||
for (auto& surface : surfaces)
|
||||
{
|
||||
if (surface->w != surface->h)
|
||||
ERROR_LOG("Image must be a square!");
|
||||
|
||||
if (surface->w > maxWidth) maxWidth = surface->w;
|
||||
if (surface->h > maxHeight) maxHeight = surface->h;
|
||||
textures.push_back(new TextureData({ surface->w, surface->h }));
|
||||
}
|
||||
for (int i = 0; i < surfaces.size(); ++i)
|
||||
{
|
||||
SDL_Surface* canvas = SDL_CreateRGBSurface(0, maxWidth, maxHeight,
|
||||
surfaces[i]->format->BitsPerPixel,
|
||||
surfaces[i]->format->Rmask,
|
||||
surfaces[i]->format->Gmask,
|
||||
surfaces[i]->format->Bmask,
|
||||
surfaces[i]->format->Amask);
|
||||
|
||||
SDL_FillRect(canvas, NULL, SDL_MapRGBA(canvas->format, 0, 0, 0, 0));
|
||||
|
||||
SDL_BlitSurface(surfaces[i], NULL, canvas, NULL);
|
||||
|
||||
SDL_FreeSurface(surfaces[i]);
|
||||
surfaces[i] = canvas;
|
||||
}
|
||||
|
||||
canvasWidth = maxWidth;
|
||||
canvasHeight = maxHeight;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TextureArray::~TextureArray()
|
||||
{
|
||||
glDeleteTextures(1, &ID);
|
||||
}
|
||||
TextureArray::~TextureArray() { glDeleteTextures(1, &ID); }
|
||||
|
|
|
|||
|
|
@ -1,24 +1,22 @@
|
|||
#include <SDL.h>
|
||||
#include <SDL_events.h>
|
||||
#include <SDL_image.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <tracy/Tracy.hpp>
|
||||
|
||||
// TODO: Fix circular dependency issues, mostly with input.h needing gameactor.h and command.h
|
||||
#include "gameplay/game.h"
|
||||
|
||||
const float vertices[] = {
|
||||
0.0f, 0.5f, 0.0f,
|
||||
-0.5f, -0.5f, 0.0f,
|
||||
0.5f, -0.5f, 0.0f
|
||||
};
|
||||
#include "utility/logger.h"
|
||||
|
||||
int main(int argc, char* args[])
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
LOG(ERROR, "Failed to initialize SDL!", NULL);
|
||||
return -1;
|
||||
if (IMG_Init(IMG_INIT_PNG) < 0)
|
||||
}
|
||||
if (IMG_Init(IMG_INIT_PNG) < 0) {
|
||||
LOG(ERROR, "Could not initialize png library", NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
|
|
@ -27,13 +25,13 @@ int main(int argc, char* args[])
|
|||
Game* game = new Game();
|
||||
if (!game->init())
|
||||
{
|
||||
std::cout << "Failed to init game!" << std::endl;
|
||||
LOG(ERROR, "Failed to init game!", NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!game->loadDebugScene())
|
||||
{
|
||||
std::cout << "Failed to load debug scene!" << std::endl;
|
||||
LOG(ERROR, "Failed to load debug scene!", NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -45,21 +43,23 @@ int main(int argc, char* args[])
|
|||
Uint64 curCounter = SDL_GetPerformanceCounter();
|
||||
double deltaTime = ((curCounter - lastCounter) / freq);
|
||||
deltaTime = (deltaTime < 1.0) ? deltaTime : 1.0;
|
||||
SDL_PollEvent(&e);
|
||||
if (e.type == SDL_QUIT)
|
||||
game->quit();
|
||||
while (SDL_PollEvent(&e)) {
|
||||
if (e.type == SDL_QUIT)
|
||||
game->quit();
|
||||
|
||||
game->handleInput(e);
|
||||
|
||||
game->update(deltaTime);
|
||||
game->captureInput(e);
|
||||
}
|
||||
game->executeInputs();
|
||||
|
||||
game->render();
|
||||
game->update(deltaTime);
|
||||
lastCounter = curCounter;
|
||||
|
||||
}
|
||||
|
||||
delete game;
|
||||
IMG_Quit();
|
||||
SDL_Quit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
200
YuppleMayham/src/sound/audiostream.cpp
Normal file
200
YuppleMayham/src/sound/audiostream.cpp
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
#include "sound/audiostream.h"
|
||||
|
||||
AudioStream::AudioStream()
|
||||
{
|
||||
alGenBuffers(4, buffers);
|
||||
alGenSources(1, &source);
|
||||
data = (short *)std::malloc(UTIL::AUDIO::CHUNK_SIZE * sizeof(short) * 2); // Make space for stereo even if we are playing mono
|
||||
stopThread = false;
|
||||
eof = true;
|
||||
paused = true;
|
||||
streamThread = std::thread(&AudioStream::stream, this);
|
||||
//streamThread.detach();
|
||||
}
|
||||
|
||||
std::string AudioStream::CurStreamName()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
return songNames.front();
|
||||
}
|
||||
|
||||
bool AudioStream::PlayStream()
|
||||
{
|
||||
if (CurStreamName().empty()) {
|
||||
LOG(WARN, "Song queue empty!", NULL);
|
||||
return false;
|
||||
}
|
||||
LOG(INFO, "Current song is: {}", CurStreamName());
|
||||
return (paused = true);
|
||||
}
|
||||
|
||||
void AudioStream::AddToQueue(std::string songName)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
LOG(INFO, "Adding song {} to queue", songName);
|
||||
songNames.push(songName);
|
||||
}
|
||||
|
||||
bool AudioStream::openFile(std::string fileName)
|
||||
{
|
||||
int error = 0;
|
||||
file = stb_vorbis_open_filename(CurStreamName().c_str(), &error, NULL);
|
||||
if (file == nullptr || error != STBVorbisError::VORBIS__no_error) {
|
||||
LOG(ERROR, "Failed to open file: '{}' error code: {}", CurStreamName(), error);
|
||||
return false;
|
||||
}
|
||||
info = stb_vorbis_get_info(file);
|
||||
if (info.sample_rate != UTIL::AUDIO::SAMPLE_RATE) {
|
||||
LOG(ERROR, "Failed to open file: '{}', make sure to convert sample rate to 44100!", CurStreamName());
|
||||
stb_vorbis_close(file);
|
||||
return false;
|
||||
}
|
||||
stb_vorbis_seek_start(file);
|
||||
std::free(data);
|
||||
data = (short *)std::malloc(UTIL::AUDIO::CHUNK_SIZE * sizeof(short) * info.channels);
|
||||
return true;
|
||||
}
|
||||
|
||||
int AudioStream::loadChunk(ALuint buffer)
|
||||
{
|
||||
int sample = 0;
|
||||
if (file == NULL) {
|
||||
return sample;
|
||||
}
|
||||
if (info.channels == 0) {
|
||||
return sample;
|
||||
}
|
||||
sample = stb_vorbis_get_samples_short_interleaved(file, info.channels, data, UTIL::AUDIO::CHUNK_SIZE);
|
||||
if (sample == 0)
|
||||
return sample;
|
||||
format = info.channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
||||
//LOG(INFO, "Buffer id {}, Format {}, data size {}, sample rate {},\nfilename {}",
|
||||
// buffer, format == AL_FORMAT_MONO16 ? "MONO" : "STEREO", sample * info.channels * sizeof(short), info.sample_rate, songNames.front());
|
||||
alBufferData(buffer, format, data, sample * info.channels * sizeof(short), info.sample_rate);
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
int AudioStream::loadInitalChunk()
|
||||
{
|
||||
int samples = 0;
|
||||
if (!openFile(CurStreamName())) {
|
||||
return samples;
|
||||
}
|
||||
for (int i = 0; i < 4; i++) {
|
||||
samples = loadChunk(buffers[i]);
|
||||
if (samples == 0) {
|
||||
LOG(ERROR, "Music file {} too small!", CurStreamName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
int AudioStream::loadNextChunk()
|
||||
{
|
||||
int samples = loadChunk(buffers[last_buffer]);
|
||||
return samples;
|
||||
}
|
||||
|
||||
void AudioStream::popQueue()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (songNames.empty())
|
||||
return;
|
||||
songNames.pop();
|
||||
}
|
||||
|
||||
void AudioStream::cycleQueue()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
LOG(INFO, "Cycling queue", NULL);
|
||||
if (songNames.empty())
|
||||
return;
|
||||
std::string buffer = songNames.front();
|
||||
songNames.pop();
|
||||
songNames.push(buffer);
|
||||
LOG(INFO, "New front: {}", songNames.front());
|
||||
}
|
||||
|
||||
void AudioStream::stream()
|
||||
{
|
||||
int processed = 0;
|
||||
int queued = 0;
|
||||
bool inital = true;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
//LOG(INFO, "Call from in audio thread!", NULL);
|
||||
//LOG(INFO, "Top of queue is: {}", CurStreamName());
|
||||
paused = false;
|
||||
|
||||
while (!stopThread) {
|
||||
if (songNames.empty() || paused) {
|
||||
LOG(INFO, "Paused or queue empty", NULL);
|
||||
continue;
|
||||
}
|
||||
alGetSourcei(source, AL_SOURCE_STATE, &state);
|
||||
if (inital) {
|
||||
int samples = loadInitalChunk();
|
||||
if (samples == 0) {
|
||||
LOG(ERROR, "Music file '{}' either failed to load or not big enough!", CurStreamName());
|
||||
popQueue();
|
||||
continue;
|
||||
}
|
||||
alSourceQueueBuffers(source, 4, buffers);
|
||||
alSourcePlay(source);
|
||||
alGetSourcei(source, AL_SOURCE_STATE, &state);
|
||||
inital = false;
|
||||
eof = false;
|
||||
}
|
||||
// If we are at the end of file, the and stream queue has a song in it
|
||||
// Load in the inital buffers
|
||||
if (state != AL_PLAYING) {
|
||||
alSourcePlay(source);
|
||||
}
|
||||
while (state == AL_PLAYING && !stopThread) {
|
||||
if (paused) {
|
||||
alSourcePause(source);
|
||||
}
|
||||
alGetSourcei(source, AL_SOURCE_STATE, &state);
|
||||
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
|
||||
alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
|
||||
|
||||
while (processed != 0 && !stopThread) {
|
||||
ALuint unqueued;
|
||||
alSourceUnqueueBuffers(source, 1, &unqueued);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
if (unqueued == buffers[i])
|
||||
last_buffer = i;
|
||||
int samples = loadNextChunk();
|
||||
if (samples == 0) {
|
||||
LOG(INFO, "Hit the end of the song!", NULL);
|
||||
cycleQueue();
|
||||
stb_vorbis_close(file);
|
||||
while (!openFile(CurStreamName())) {
|
||||
LOG(ERROR, "Failed to open next in stream! {}", CurStreamName());
|
||||
popQueue();
|
||||
}
|
||||
} else {
|
||||
}
|
||||
//LOG(INFO, "Buffer index: {}", last_buffer);
|
||||
alSourceQueueBuffers(source, 1, &buffers[last_buffer]);
|
||||
//last_buffer = (last_buffer + 1) % 4;
|
||||
processed--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AudioStream::~AudioStream()
|
||||
{
|
||||
stopThread = true;
|
||||
if (streamThread.joinable())
|
||||
streamThread.join();
|
||||
stb_vorbis_close(file);
|
||||
std::free(data);
|
||||
alSourcei(source, AL_BUFFER, 0);
|
||||
alDeleteSources(1, &source);
|
||||
alDeleteBuffers(4, buffers);
|
||||
}
|
||||
92
YuppleMayham/src/sound/engine.cpp
Normal file
92
YuppleMayham/src/sound/engine.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#include "sound/engine.h"
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
AudioEngine::AudioEngine(std::weak_ptr<ResourceManager> _resource)
|
||||
{
|
||||
// Open the default device for now
|
||||
if ((device = alcOpenDevice(NULL)) == NULL) {
|
||||
LOG(ERROR, "Failed to open default device {}", alGetError());
|
||||
return;
|
||||
}
|
||||
context = alcCreateContext(device, NULL);
|
||||
alcMakeContextCurrent(context);
|
||||
musicPlayer = std::make_unique<AudioStream>();
|
||||
soundManager = std::make_unique<SoundManager>();
|
||||
resourceManager = _resource;
|
||||
alGetError();
|
||||
}
|
||||
|
||||
void AudioEngine::hookEventManager(std::weak_ptr<EventManager> _events)
|
||||
{
|
||||
globalEventManager = _events;
|
||||
}
|
||||
|
||||
void AudioEngine::hookSceneManager(std::weak_ptr<EventManager> _events)
|
||||
{
|
||||
sceneEventManager = _events;
|
||||
if (auto weak = sceneEventManager.lock()) {
|
||||
std::weak_ptr<AudioEngine> weakSelf = shared_from_this();
|
||||
auto play_sound = [weakSelf](const std::string& id, int priority, const std::optional<glm::vec3>& pos = std::nullopt) {
|
||||
if (auto self = weakSelf.lock()) {
|
||||
if (auto res = self->resourceManager.lock()) {
|
||||
auto buf = res->loadSoundEffect(id);
|
||||
if (buf != 0) {
|
||||
if (pos != std::nullopt)
|
||||
self->soundManager->playSound(buf, priority, pos.value());
|
||||
else
|
||||
self->soundManager->playSound(buf, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
weak->subscribe<EntityFireEvent>([weakSelf,play_sound](const EntityFireEvent& e){
|
||||
play_sound(e.weaponType + "/shoot", 10, e.firePosition);
|
||||
});
|
||||
weak->subscribe<EntityReloadEvent>([weakSelf,play_sound](const EntityReloadEvent& e) {
|
||||
play_sound(e.weaponType + "/reload", 9, e.position);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void AudioEngine::poll()
|
||||
{
|
||||
soundManager->pollSources();
|
||||
}
|
||||
|
||||
void AudioEngine::updateListener(const glm::vec3& pos)
|
||||
{
|
||||
alListenerfv(AL_POSITION, glm::value_ptr(pos));
|
||||
}
|
||||
|
||||
void AudioEngine::pushMusic(std::string _songName)
|
||||
{
|
||||
LOG(INFO, "Loading song {}", _songName);
|
||||
musicPlayer->AddToQueue(_songName);
|
||||
}
|
||||
|
||||
void AudioEngine::playMusic()
|
||||
{
|
||||
LOG(INFO, "Playing stream", NULL);
|
||||
musicPlayer->PlayStream();
|
||||
}
|
||||
|
||||
void AudioEngine::pauseMusic()
|
||||
{
|
||||
musicPlayer->PauseStream();
|
||||
}
|
||||
|
||||
void AudioEngine::killMusic()
|
||||
{
|
||||
musicPlayer->kill();
|
||||
}
|
||||
|
||||
AudioEngine::~AudioEngine()
|
||||
{
|
||||
musicPlayer->kill();
|
||||
alcMakeContextCurrent(NULL);
|
||||
alcDestroyContext(context);
|
||||
alcCloseDevice(device);
|
||||
}
|
||||
32
YuppleMayham/src/sound/soundeffect.cpp
Normal file
32
YuppleMayham/src/sound/soundeffect.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#include "sound/soundeffect.h"
|
||||
#include "util.h"
|
||||
#include <AL/al.h>
|
||||
|
||||
SoundEffect::SoundEffect(const std::string& filename)
|
||||
{
|
||||
valid = loadFile(filename);
|
||||
}
|
||||
|
||||
// load the whole file into a buffer, this should only be used for small sound effects. Never with music or sounds exceeding 10MB!
|
||||
bool SoundEffect::loadFile(const std::string& filename)
|
||||
{
|
||||
short *data;
|
||||
//std::unique_ptr<short[]> data((short*)new short[UTIL::AUDIO::CHUNK_SIZE * sizeof(short) * 2]());
|
||||
//std::vector<short> data(UTIL::AUDIO::CHUNK_SIZE * sizeof(short) * 2);
|
||||
int channels, sample_rate, samples;
|
||||
|
||||
samples = stb_vorbis_decode_filename(filename.c_str(), &channels, &sample_rate, &data);
|
||||
if (samples == 0) {
|
||||
LOG(ERROR, "Failed to load sound effect '{}'", filename);
|
||||
return false;
|
||||
}
|
||||
alGenBuffers(1, &buffer);
|
||||
alBufferData(buffer, channels == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16, data, samples * channels * sizeof(short), sample_rate);
|
||||
std::free(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
SoundEffect::~SoundEffect()
|
||||
{
|
||||
alDeleteBuffers(1, &buffer);
|
||||
}
|
||||
108
YuppleMayham/src/sound/soundmanager.cpp
Normal file
108
YuppleMayham/src/sound/soundmanager.cpp
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#include "sound/soundmanager.h"
|
||||
#include <AL/al.h>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
SoundManager::SoundManager()
|
||||
{
|
||||
for (auto& s : sources) {
|
||||
alGenSources(1, &s.source);
|
||||
s.inUse = false;
|
||||
s.priority = 0;
|
||||
alSourcef(s.source, AL_REFERENCE_DISTANCE, 1.0f);
|
||||
alSourcef(s.source, AL_MAX_DISTANCE, 10000.f);
|
||||
alSourcef(s.source, AL_ROLLOFF_FACTOR, 0.3f);
|
||||
}
|
||||
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
|
||||
}
|
||||
|
||||
void SoundManager::playSound(ALuint buffer, int priority, const glm::vec3& pos)
|
||||
{
|
||||
std::size_t ring = lastUsed;
|
||||
AudioSource* nextBest = nullptr;
|
||||
std::size_t secIndex = 0;
|
||||
auto nextIndex = [](std::size_t i){ return (i + 1) % 10; };
|
||||
|
||||
if (!sources[ring].inUse) {
|
||||
sources[ring].inUse = true;
|
||||
sources[ring].priority = priority;
|
||||
sources[ring].buffer = buffer;
|
||||
alSourcei(sources[ring].source, AL_BUFFER, buffer);
|
||||
if (pos != glm::vec3(0)) {
|
||||
//alSourcei(sources[ring].source, AL_SOURCE_RELATIVE, 1);
|
||||
alSourcefv(sources[ring].source, AL_POSITION, glm::value_ptr(pos));
|
||||
} else {
|
||||
//alSourcei(sources[ring].source, AL_SOURCE_RELATIVE, 0);
|
||||
}
|
||||
alSourcePlay(sources[ring].source);
|
||||
lastUsed = nextIndex(lastUsed);
|
||||
return;
|
||||
}
|
||||
for (ring = nextIndex(lastUsed); ring != lastUsed; ring = nextIndex(ring)) {
|
||||
if (!sources[ring].inUse) {
|
||||
sources[ring].inUse = true;
|
||||
sources[ring].priority = priority;
|
||||
sources[ring].buffer = buffer;
|
||||
alSourcei(sources[ring].source, AL_BUFFER, buffer);
|
||||
if (pos != glm::vec3(0)) {
|
||||
//alSourcei(sources[ring].source, AL_SOURCE_RELATIVE, 1);
|
||||
alSourcefv(sources[ring].source, AL_POSITION, glm::value_ptr(pos));
|
||||
} else {
|
||||
//alSourcei(sources[ring].source, AL_SOURCE_RELATIVE, 0);
|
||||
}
|
||||
alSourcePlay(sources[ring].source);
|
||||
lastUsed = nextIndex(ring);
|
||||
return;
|
||||
}
|
||||
if (sources[ring].priority > priority || (sources[ring].buffer != buffer && sources[ring].priority == priority)) {
|
||||
if (nextBest) {
|
||||
if (nextBest->priority >= sources[ring].priority) {
|
||||
nextBest = &sources[ring];
|
||||
secIndex = ring;
|
||||
}
|
||||
} else {
|
||||
nextBest = &sources[ring];
|
||||
secIndex = ring;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextBest) {
|
||||
nextBest->priority = priority;
|
||||
alSourcei(nextBest->source, AL_BUFFER, 0);
|
||||
alSourcei(nextBest->source, AL_BUFFER, buffer);
|
||||
nextBest->buffer = buffer;
|
||||
if (pos != glm::vec3(0)) {
|
||||
//alSourcei(nextBest->source, AL_SOURCE_RELATIVE, 1);
|
||||
alSourcefv(nextBest->source, AL_POSITION, glm::value_ptr(pos));
|
||||
} else {
|
||||
//alSourcei(nextBest->source, AL_SOURCE_RELATIVE, 0);
|
||||
}
|
||||
alSourcePlay(nextBest->source);
|
||||
lastUsed = secIndex;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::pollSources()
|
||||
{
|
||||
for (auto& s : sources) {
|
||||
if (s.inUse) {
|
||||
ALint processed, state;
|
||||
alGetSourcei(s.source, AL_BUFFERS_PROCESSED, &processed);
|
||||
alGetSourcei(s.source, AL_SOURCE_STATE, &state);
|
||||
if (state == AL_STOPPED) {
|
||||
s.inUse = false;
|
||||
s.buffer = 0;
|
||||
alSourcei(s.source, AL_BUFFER, 0);
|
||||
}
|
||||
//else if (state != AL_PLAYING)
|
||||
// alSourcePlay(s.source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SoundManager::~SoundManager()
|
||||
{
|
||||
for (auto& s : sources) {
|
||||
alSourcei(s.source, AL_BUFFER, 0);
|
||||
alDeleteSources(1, &s.source);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue