Merge remote-tracking branch 'origin/windows-devel'

This commit is contained in:
2023-12-13 07:29:23 -05:00
24 changed files with 1645 additions and 0 deletions

56
CMakeLists.txt Normal file
View File

@@ -0,0 +1,56 @@
cmake_minimum_required(VERSION 3.18)
project(SDL3D
VERSION 1.0
LANGUAGES CXX
)
if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(FATAL_ERROR "In-source builds are not allowed")
endif()
set(CMAKE_CXX_STANDARD 20)
#set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_FLAGS "-municode")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# Enable Package Managers
include(cmake/CPM.cmake)
include(cmake/sdl.cmake)
include(cmake/glm.cmake)
file(GLOB_RECURSE HEADERS "include/*")
file(GLOB_RECURSE SOURCES "src/*")
file(GLOB_RECURSE ASSETS "assets/*")
include_directories("include")
add_library(SDL3D SHARED ${SOURCES}
src/engine/engine.cpp
src/types/entity.cpp
src/types/moby.cpp
src/types/camera.cpp
src/types/vector.cpp)
# Why god???
set_target_properties(SDL3D PROPERTIES LINKER_LANGUAGE CXX)
CPMAddPackage(
NAME glm
GITHUB_REPOSITORY g-truc/glm
OPTIONS "GLM_STATIC_LIBRARY_ENABLE"
#SOURCE_SUBDIR glm
GIT_TAG 0.9.9.8
)
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS})
#find_package(SDL2_image REQUIRED)
#include_directories(${SDL2_IMAGE_INCLUDE_DIRS})
find_package(OpenGL REQUIRED)
include_directories({$OPENGL_INCLUDE_DIRS})
find_package(glm REQUIRED)
target_link_libraries(SDL3D PUBLIC glm::glm SDL2::SDL2 ${OPENGL_LIBRARIES})
add_executable(SDL3D_Demo "demo/main.cpp")
target_link_libraries(SDL3D_Demo PUBLIC SDL3D)

24
cmake/CPM.cmake Normal file
View File

@@ -0,0 +1,24 @@
# SPDX-License-Identifier: MIT
#
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
set(CPM_DOWNLOAD_VERSION 0.38.7)
set(CPM_HASH_SUM "83e5eb71b2bbb8b1f2ad38f1950287a057624e385c238f6087f94cdfc44af9c5")
if(CPM_SOURCE_CACHE)
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
else()
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
endif()
# Expand relative path. This is important if the provided path contains a tilde (~)
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
file(DOWNLOAD
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM}
)
include(${CPM_DOWNLOAD_LOCATION})

163
cmake/FindSDL2.cmake Normal file
View File

@@ -0,0 +1,163 @@
# Locate SDL2 library
# This module defines
# SDL2_LIBRARY, the name of the library to link against
# SDL2_FOUND, if false, do not try to link to SDL2
# SDL2_INCLUDE_DIR, where to find SDL.h
#
# This module responds to the the flag:
# SDL2_BUILDING_LIBRARY
# If this is defined, then no SDL2main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL2_LIBRARY variable.
#
# Don't forget to include SDLmain.h and SDLmain.m your project for the
# OS X framework based version. (Other versions link to -lSDL2main which
# this module will try to find on your behalf.) Also for OS X, this
# module will automatically add the -framework Cocoa on your behalf.
#
#
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
# as appropriate. These values are used to generate the final SDL2_LIBRARY
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
#
#
# $SDL2DIR is an environment variable that would
# correspond to the ./configure --prefix=$SDL2DIR
# used in building SDL2.
# l.e.galup 9-20-02
#
# Modified by Eric Wing.
# Added code to assist with automated building by using environmental variables
# and providing a more controlled/consistent search behavior.
# Added new modifications to recognize OS X frameworks and
# additional Unix paths (FreeBSD, etc).
# Also corrected the header search path to follow "proper" SDL guidelines.
# Added a search for SDL2main which is needed by some platforms.
# Added a search for threads which is needed by some platforms.
# Added needed compile switches for MinGW.
#
# On OSX, this will prefer the Framework version (if found) over others.
# People will have to manually change the cache values of
# SDL2_LIBRARY to override this selection or set the CMake environment
# CMAKE_INCLUDE_PATH to modify the search paths.
#
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
# This needed to change because "proper" SDL convention
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
# reasons because not all systems place things in SDL2/ (see FreeBSD).
#=============================================================================
# Copyright 2003-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
SET(SDL2_SEARCH_PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES include/SDL2 include
PATHS ${SDL2_SEARCH_PATHS}
)
FIND_LIBRARY(SDL2_LIBRARY_TEMP
NAMES SDL2
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS}
)
IF(NOT SDL2_BUILDING_LIBRARY)
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
# Non-OS X framework versions expect you to also dynamically link to
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
# seem to provide SDL2main for compatibility even though they don't
# necessarily need it.
FIND_LIBRARY(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS}
)
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# SDL2 may require threads on your system.
# The Apple build may not need an explicit flag because one of the
# frameworks may already provide it.
# But for non-OSX systems, I will use the CMake Threads package.
IF(NOT APPLE)
FIND_PACKAGE(Threads)
ENDIF(NOT APPLE)
# MinGW needs an additional library, mwindows
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows
# (Actually on second look, I think it only needs one of the m* libraries.)
IF(MINGW)
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
ENDIF(MINGW)
IF(SDL2_LIBRARY_TEMP)
# For SDL2main
IF(NOT SDL2_BUILDING_LIBRARY)
IF(SDL2MAIN_LIBRARY)
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(SDL2MAIN_LIBRARY)
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
# CMake doesn't display the -framework Cocoa string in the UI even
# though it actually is there if I modify a pre-used variable.
# I think it has something to do with the CACHE STRING.
# So I use a temporary variable until the end so I can set the
# "real" variable in one-shot.
IF(APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
ENDIF(APPLE)
# For threads, as mentioned Apple doesn't need this.
# In fact, there seems to be a problem if I used the Threads package
# and try using this line, so I'm just skipping it entirely for OS X.
IF(NOT APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
ENDIF(NOT APPLE)
# For MinGW library
IF(MINGW)
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(MINGW)
# Set the final string here so the GUI reflects the final state.
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "")
ENDIF(SDL2_LIBRARY_TEMP)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)

64
cmake/glm.cmake Normal file
View File

@@ -0,0 +1,64 @@
#-----------------------------------------------------------------------
# CPM configuration
#-----------------------------------------------------------------------
set(CPM_MODULE_NAME iauns_cpm_glm)
set(CPM_LIB_TARGET_NAME ${CPM_MODULE_NAME})
if ((DEFINED CPM_DIR) AND (DEFINED CPM_UNIQUE_ID) AND (DEFINED CPM_TARGET_NAME))
set(CPM_LIB_TARGET_NAME ${CPM_TARGET_NAME})
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CPM_DIR})
include(CPM)
else()
set(CPM_DIR "${CMAKE_CURRENT_BINARY_DIR}/cpm-packages" CACHE TYPE STRING)
find_package(Git)
if(NOT GIT_FOUND)
message(FATAL_ERROR "CPM requires Git.")
endif()
if (NOT EXISTS ${CPM_DIR}/CPM.cmake)
execute_process(
COMMAND "${GIT_EXECUTABLE}" clone https://github.com/iauns/cpm ${CPM_DIR}
RESULT_VARIABLE error_code
OUTPUT_VARIABLE head_sha)
if(error_code)
message(FATAL_ERROR "CPM failed to get the hash for HEAD")
endif()
endif()
include(${CPM_DIR}/CPM.cmake)
endif()
# All externals *must* define this.
CPM_ForceOnlyOneModuleVersion()
CPM_InitModule(${CPM_MODULE_NAME})
#------------------------------------------------------------------------------
# GLM
#------------------------------------------------------------------------------
# We manually download GLM into our source directory so the user can access
# glm through 'glm/...'. There is no build step since this is a header only
# library.
set(REPO_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdParty/glm")
CPM_EnsureRepoIsCurrent(
TARGET_DIR ${REPO_SOURCE_DIR}
GIT_REPOSITORY "https://github.com/g-truc/glm.git"
GIT_TAG "0.9.4.6"
USE_CACHING TRUE
)
CPM_ExportAdditionalIncludeDir("${REPO_SOURCE_DIR}")
# Enable a number of useful GLM options.
if (NOT DEFINED CPM_GLM_NO_SWIZZLE)
CPM_ExportAdditionalDefinition("-DGLM_SWIZZLE")
endif()
if (NOT DEFINED CPM_GLM_CPP11)
CPM_ExportAdditionalDefinition("-DGLM_FORCE_CXX03")
endif()
if (NOT DEFINED CPM_GLM_DEGREES)
CPM_ExportAdditionalDefinition("-DGLM_FORCE_RADIANS")
endif()

92
cmake/sdl.cmake Normal file
View File

@@ -0,0 +1,92 @@
include(cmake/CPM.cmake)
if(NOT DEFINED EMSCRIPTEN)
SET(BUILD_SHARED_LIBS OFF CACHE BOOL "BUILD_SHARED_LIBS" FORCE)
SET(BUILD_STATIC_LIBS ON CACHE BOOL "BUILD_STATIC_LIBS" FORCE)
SET(POSITION_INDEPENDENT_CODE ON CACHE BOOL "POSITION_INDEPENDENT_CODE" FORCE)
# SDL2
string(TIMESTAMP BEFORE "%s")
CPMAddPackage(
NAME SDL2
GITHUB_REPOSITORY libsdl-org/SDL
GIT_TAG release-2.26.2
OPTIONS
"SDL2_DISABLE_INSTALL ON"
"SDL_SHARED OFF"
"SDL_STATIC ON"
"SDL_STATIC_PIC ON"
"SDL_WERROR OFF"
)
find_package(SDL2 REQUIRED)
file(GLOB SDL2_HEADERS "${SDL2_SOURCE_DIR}/include/*.h")
# Create a target that copies headers at build time, when they change
add_custom_target(sdl_copy_headers_in_build_dir
COMMAND ${CMAKE_COMMAND} -E copy_directory "${SDL2_SOURCE_DIR}/include" "${CMAKE_BINARY_DIR}/SDLHeaders/SDL2"
DEPENDS ${SDL2_HEADERS})
# Make SDL depend from it
add_dependencies(SDL2-static sdl_copy_headers_in_build_dir)
# And add the directory where headers have been copied as an interface include dir
target_include_directories(SDL2-static INTERFACE "${CMAKE_BINARY_DIR}/SDLHeaders")
set (SDL2_INCLUDE_DIR ${SDL2_SOURCE_DIR}/include)
include_directories(${SDL2_INCLUDE_DIR})
string(TIMESTAMP AFTER "%s")
math(EXPR DELTASDL "${AFTER} - ${BEFORE}")
MESSAGE(STATUS "SDL2 TIME: ${DELTASDL}s")
## SDL_ttf
#string(TIMESTAMP BEFORE "%s")
#CPMAddPackage(GITHUB_REPOSITORY libsdl-org/SDL_ttf
# GIT_TAG release-2.20.1
# OPTIONS
# "SDL2TTF_INSTALL OFF"
# "SDL2TTF_BUILD_SHARED_LIBS OFF"
# "SDL2TTF_VENDORED ON"
# "SDL2TTF_SAMPLES OFF"
#) # vendor is required for mingw builds
#find_package(SDL_ttf REQUIRED)
#include_directories(${SDL_ttf_SOURCE_DIR})
#string(TIMESTAMP AFTER "%s")
#math(EXPR DELTASDL_ttf "${AFTER} - ${BEFORE}")
#MESSAGE(STATUS "SDL_ttf TIME: ${DELTASDL_ttf}s")
# SDL_image
string(TIMESTAMP BEFORE "%s")
CPMAddPackage(GITHUB_REPOSITORY libsdl-org/SDL_image
GIT_TAG release-2.6.2
OPTIONS
"SDL2IMAGE_INSTALL OFF"
"SDL2IMAGE_SAMPLES OFF"
"SDL2IMAGE_VENDORED ON"
"SDL2IMAGE_BUILD_SHARED_LIBS OFF"
"GIT_SUBMODULES_RECURSE ON"
)
find_package(SDL_image REQUIRED)
include_directories(${SDL_image_SOURCE_DIR})
string(TIMESTAMP AFTER "%s")
math(EXPR DELTASDL_image "${AFTER} - ${BEFORE}")
MESSAGE(STATUS "SDL_image TIME: ${DELTASDL_image}s")
#
### SDL_mixer
#string(TIMESTAMP BEFORE "%s")
#CPMAddPackage(GITHUB_REPOSITORY libsdl-org/SDL_mixer
# GIT_TAG release-2.6.2
# OPTIONS
# "SDL2MIXER_INSTALL OFF"
# "SDL2MIXER_VENDORED ON"
# "SDL2MIXER_SAMPLES OFF"
# "SDL2MIXER_BUILD_SHARED_LIBS OFF"
#)
#find_package(SDL_mixer REQUIRED)
#include_directories(${SDL_mixer_SOURCE_DIR}/include)
#string(TIMESTAMP AFTER "%s")
#math(EXPR DELTASDL_mixer "${AFTER} - ${BEFORE}")
#MESSAGE(STATUS "SDL_mixer TIME: ${DELTASDL_mixer}s")
ENDIF()

48
demo/main.cpp Normal file
View File

@@ -0,0 +1,48 @@
#include <thread>
#include "engine/tick.h"
#include "engine/render.h"1
class App
{
public:
int Status;
virtual void Run()
{
}
};
class GameApp : public App
{
};
class glDemoGameApp : public GameApp
{
public:
void Run() override
{
std::thread renderThread(render_loop);
std::thread tickThread (gameTick);
renderThread.join();
tickThread.join();
}
};
int main(int argc, char** argv) {
auto* app = new glDemoGameApp();
app->Run();
return app->Status;
}
#define WINDOWS_SMH
#ifdef WINDOWS_SMH
extern "C" {
int wmain(int argc, wchar_t* argv[])
{
return main(0, nullptr);
}
}
#endif

46
include/engine/engine.h Normal file
View File

@@ -0,0 +1,46 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <iostream>
#include <fstream>
#include <SDL2/SDL.h>
#include <GL/gl.h>
enum class GAMESTATE: uint8_t {
NORMAL = 0, //Gameplay.
IN_MAIN_MENU = 1,
IN_PAUSE_MENU = 2,
IN_LEVEL_ANIMATION = 3, //A cutscene which moves entities in the world and cannot be interrupted.
IN_QUIT = 4, //The game should close.
};
class Engine {
public:
GAMESTATE gameState = GAMESTATE::NORMAL;
bool debug = true;
SDL_Window *window = nullptr;
uint16_t windowWidth = 1152;
uint16_t windowHeight = 864;
uint64_t tickCount = 0;
float tickDelta = NULL;
uint64_t frameCount = 0;
float frameDelta = NULL;
uint16_t minimumTickDelta = 15625;
GLenum glError = GL_NO_ERROR;
SDL_GLContext glContext = nullptr;
float nearPlane = 0.01f;
float farPlane = 100.0f;
float fov = 72.5f;
Uint8* keyState = nullptr;
SDL_Event event;
[[nodiscard]] float framerate() const;
void takeScreenshot() const;
void quit() const;
static float getGLVersion();
void initGL();
void initVideo();
void debugInfo() const;
};
inline auto* engine = new(Engine);

107
include/engine/render.h Normal file
View File

@@ -0,0 +1,107 @@
#pragma once
#include <chrono>
#include <SDL_events.h>
#include <thread>
#include <engine/engine.h>
#include <types/camera.h>
#include <types/skybox.h>
#include <types/entityList.h>
void process_sdl_events() {
while (SDL_PollEvent(&engine->event)) {
// TODO: Consider switch statements as opposed to ifs
// This adds control flow in the form of "break" statement.
// Which will save checking every single event type unnecessarily
// IMO premature optimization
// I personally dislike deciphering switch statements aswell
if (engine->event.type == SDL_QUIT) {engine->quit();}
if (engine->event.type == SDL_DROPFILE) {}
// Window Events
if (engine->event.type == SDL_WINDOWEVENT) {
auto window_ev = engine->event.window;
auto ev_type = window_ev.event;
if (ev_type == SDL_WINDOWEVENT_FOCUS_LOST) {}
if (ev_type == SDL_WINDOWEVENT_FOCUS_GAINED) {}
if (ev_type == SDL_WINDOWEVENT_CLOSE) {}
if (ev_type == SDL_WINDOWEVENT_SHOWN) {}
if (ev_type == SDL_WINDOWEVENT_HIDDEN) {}
if (ev_type == SDL_WINDOWEVENT_EXPOSED) {}
if (ev_type == SDL_WINDOWEVENT_MOVED) {}
if (ev_type == SDL_WINDOWEVENT_RESIZED) {}
if (ev_type == SDL_WINDOWEVENT_SIZE_CHANGED) {}
if (ev_type == SDL_WINDOWEVENT_MINIMIZED) {}
if (ev_type == SDL_WINDOWEVENT_MAXIMIZED) {}
if (ev_type == SDL_WINDOWEVENT_RESTORED) {}
if (ev_type == SDL_WINDOWEVENT_ENTER) {}
if (ev_type == SDL_WINDOWEVENT_LEAVE) {}
}
}
}
void pre_render() {
// NO
if(engine->frameCount == 0) {
engine->initVideo();
engine->initGL();
auto* camera = new Camera();
storeEntity(camera);
getCamera()->position.set(0.0f,-2.0f,-5.0f);
getCamera()->angle.y = 0.0f;
getCamera()->scriptedMove.load("../scriptedMove/default.smov");
auto* skybox = new Skybox();
skybox->draw = true;
storeEntity(skybox);
auto* player = new Player();
player->angle = {0,0,0};
storeEntity(player);
}
engine->frameCount++;
process_sdl_events();
engine->keyState = const_cast<Uint8 *>(SDL_GetKeyboardState(nullptr));
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
getCamera()->pre_render();
getPlayer()->pre_render();
getSkybox()->pre_render();
}
void render() {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//*Always* render the camera first.
getCamera()->render();
getSkybox()->render();
getPlayer()->render();
}
void post_render() {
SDL_GL_SwapWindow(engine->window);
getCamera()->post_render();
//Resets all of the transformations for the next frame.
glPopMatrix();
glPushMatrix();
}
[[noreturn]] void render_loop() {
while (true) {
auto start = std::chrono::high_resolution_clock::now();
pre_render();
render();
post_render();
auto stop = std::chrono::high_resolution_clock::now();
float dT = (std::chrono::duration_cast<std::chrono::microseconds>(stop - start).count());
//If we have more than 1000 fps.
if (dT < 1000){
int remaining = 1000 - dT;
std::this_thread::sleep_for(std::chrono::microseconds(remaining));
dT = 1000;
}
engine->frameDelta = dT / 1000000;
}
}

43
include/engine/tick.h Normal file
View File

@@ -0,0 +1,43 @@
#pragma once
#include <chrono>
#include <thread>
#include <engine/engine.h>
#include <types/entityList.h>
#include <types/camera.h>
[[noreturn]] void gameTick() {
while (true) {
auto start = std::chrono::high_resolution_clock::now();
//do stuff
engine->tickCount++;
switch (engine->gameState) {
case GAMESTATE::NORMAL:
if(engine->frameCount > 1)
getCamera()->update();
break;
case GAMESTATE::IN_MAIN_MENU:
break;
case GAMESTATE::IN_PAUSE_MENU:
break;
case GAMESTATE::IN_LEVEL_ANIMATION:
break;
case GAMESTATE::IN_QUIT:
engine->quit();
break;
}
//The compiler and/or the cpu would probably fix this on it's own anyways.
//But, always put the simplest condition first in the if statement if it's likely to fail.
if (engine->debug && engine->tickCount % 64 == 0)
engine->debugInfo();
auto stop = std::chrono::high_resolution_clock::now();
//limit to 64 ticks per second.
if ((int) engine->tickDelta < engine->minimumTickDelta)
std::this_thread::sleep_for(
std::chrono::microseconds((uint16_t) engine->minimumTickDelta - (uint16_t) engine->tickDelta));
//Pause without potential deSync issues.
engine->tickDelta = std::chrono::duration_cast<std::chrono::microseconds>(stop - start).count();
}
}

View File

@@ -0,0 +1,60 @@
#pragma once
#include <vector>
#include <fstream>
#include <sstream>
#include <iostream>
#include "../vector3.h"
#include "../../engine/engine.h"
#include "../entityList.h"
//A "scripted move" is a vector of points and angles that function as keyframes.
//A moby will follow *and interpolate* between those points.
class ScriptedMove {
public:
std::vector<Position> positions;
std::vector<Angle> angles;
//We won't even need that many positions anyways.
int16_t index = -1;
Position start = {NULL,NULL,NULL};
float velocity;
void load(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cout << "File not found: " << filename << std::endl;
engine->quit();
return;
}
std::string line;
while (std::getline(file, line)) {
std::istringstream stream(line);
std::string prefix;
stream >> prefix;
if (prefix == "position") {
float x, y, z;
stream >> x >> y >> z;
positions.push_back({x, y, z});
}
else if (prefix == "angle") {
float x, y, z;
stream >> x >> y >> z;
angles.push_back({x, y, z});
}
else if (prefix == "start") {
float x, y, z;
stream >> x >> y >> z;
start.x = x;
start.y = y;
start.z = z;
}
else if (prefix == "velocity") {
float x;
stream >> x;
velocity = x;
}
}
file.close();
}
};

144
include/types/camera.h Normal file
View File

@@ -0,0 +1,144 @@
#pragma once
#include "moby.h"
#include "player.h"
#include "../engine/engine.h"
#include <glm/glm.hpp>
#include "entityList.h"
#include <GL/glu.h>
#include "glm/ext/matrix_transform.hpp"
enum class CameraMode: uint8_t {
THIRD_PERSON = 0,
FREECAM = 1,
SCRIPTED_MOVE = 2
};
const glm::vec3 UP = {0, 1, 0};
class Camera : public Moby {
protected:
public:
glm::mat4x4 GetViewMatrix()
{
//return glm::lookAt(position, position + angle, UP);
}
void MoveForward(float dist);
void StrafeLeft(float dist);
void StrafeRight(float dist);
void TurnLeft(float deg = 90);
void TurnRight(float deg = 90);
void LookAt(glm::vec3 pos, glm::vec3 target, glm::vec3 upaxis = UP) {}
void Rotate(float amt, glm::vec3 axis) { }
void Translate(glm::vec3 dir) { }
float fov;
Camera() : Moby(), fov(60)
{
}
CameraMode cameraMode = CameraMode::FREECAM;
bool takingScreenshot = false;
void update();
void pre_render() {
if (engine->keyState[SDL_SCANCODE_0] == 1) {
this->takingScreenshot = true;
}
if (engine->keyState[SDL_SCANCODE_1] == 1)
this->cameraMode = CameraMode::FREECAM;
if (engine->keyState[SDL_SCANCODE_2] == 1)
this->cameraMode = CameraMode::THIRD_PERSON;
if (engine->keyState[SDL_SCANCODE_3] == 1)
this->cameraMode = CameraMode::SCRIPTED_MOVE;
if (cameraMode == CameraMode::SCRIPTED_MOVE) {
doScriptedMovement();
}
if (cameraMode == CameraMode::THIRD_PERSON) {
if (engine->debug)
std::cout << "Calculated Pitch: " << VectorMath::calcAngle(position,getPlayer()->position).x << " Calculated Yaw: " << VectorMath::calcAngle(position,getPlayer()->position).y << std::endl;
this->position = getPlayer()->cameraPoint(2);
//Make the camera pitch down a little bit.
this->position.y += 0.5;
this->angle.x = VectorMath::calcAngle(position,getPlayer()->position).x;
this->angle.y = VectorMath::calcAngle(position,getPlayer()->position).y;
}
//if (engine->frameCount == 5000)
//getPlayer()->thirdPersonCameraPoints();
if (cameraMode == CameraMode::FREECAM) {
if (engine->keyState[SDL_SCANCODE_S] == 1) {
move(bAngle(),2);
}
if (engine->keyState[SDL_SCANCODE_W] == 1) {
move(fAngle(),2);
}
if (engine->keyState[SDL_SCANCODE_A] == 1) {
move(lAngle(), 2);
}
if (engine->keyState[SDL_SCANCODE_D] == 1) {
move(rAngle(),2);
}
if (engine->keyState[SDL_SCANCODE_SPACE] == 1) {
this->position.y += 5*engine->frameDelta;
}
if (engine->keyState[SDL_SCANCODE_LSHIFT] == 1) {
this->position.y -= 5*engine->frameDelta;
}
if (engine->keyState[SDL_SCANCODE_LEFT] == 1) {
this->angle.y +=75.0f*engine->frameDelta;
}
if (engine->keyState[SDL_SCANCODE_RIGHT] == 1) {
this->angle.y -=75.0f*engine->frameDelta;
}
if (engine->keyState[SDL_SCANCODE_UP] == 1) {
this->angle.x -=75.0f*engine->frameDelta;
}
if (engine->keyState[SDL_SCANCODE_DOWN] == 1) {
this->angle.x +=75.0f*engine->frameDelta;
}
}
}
void render() {
// Preferrably: Camera would return a coordinate system that GameEngine
// would set gluLookAt() with, this helps keep objects self contained
this->angle.clamp();
glRotatef(angle.x,1.0f, 0.0f, 0.0f);
glRotatef(-angle.y,0.0f, 1.0f, 0.0f);
glTranslatef(0.0f, 0.0f, 0.0f);
gluLookAt(position.x, position.y, position.z, // camera position
position.x, position.y, engine->farPlane+position.z, // target position, We're always *looking at* the far plane straight ahead so the camera never turns around.
upVector.x,upVector.y,upVector.z);
}
void post_render() {
if (this->takingScreenshot) {
engine->takeScreenshot();
takingScreenshot = false;
}
this->ticksAlive++;
}
};
inline Camera* getCamera() {
for (auto& e : entityList)
if (auto* c = dynamic_cast<Camera*>(e))
return c;
std::cerr << "Attempt to get Camera pointer while not in scene." << std::endl;
engine->quit();
}

49
include/types/entity.h Normal file
View File

@@ -0,0 +1,49 @@
#pragma once
#include <cstdint>
#include <glm/glm.hpp>
#include <types/vector3.h>
class Entity {
protected:
glm::mat4x4 coordinates;
public:
Position position; //X Y Z
uint32_t ticksAlive; //At 64tps it'd take 776 days to overflow.
glm::vec3 GetPos() const;
void SetPos(const glm::vec3& rhs);
glm::mat4 GetMatrix() const;
void SetMatrix(const glm::mat4& rhs);
//Angle GetRotation() const { return glm::eulerAngles(); }
//void SetRotation(Angle&const rhs);
bool draw = true;
bool collidable = true;
void destruct() {
//TODO: Search entity list for this entity and remove it to avoid use-after-free.
// Non. This should be the concern of the object managing entities. Entity.h should JUST provide entity behavior.
}
virtual void pre_render() {}
virtual void post_render() {}
virtual void render() {}
virtual void update(float elapsed) {}
virtual void ticc(int tics)
{
ticksAlive++;
}
Entity() :
position({0,0,0}),
ticksAlive(0)
{
}
Entity(const Entity& rhs) = default; // Boilerplate: Copy Constructor
Entity(Entity&& rhs) = default; // Boilerplate: Move Constructor
~Entity() = default;
};

View File

@@ -0,0 +1,20 @@
#pragma once
#include <vector>
#include "entity.h"
//the primary entity list.
inline std::vector<Entity*> entityList;
inline bool storeEntity(Entity* e) {
// TODO: Check if entity is already in list
if (std::ranges::find(entityList, e) != entityList.end())
{ return false; }
entityList.push_back(e);
return true;
}
inline Entity* getFirstByName(const std::string& name);
inline Entity* getByUUID(uint64_t uuid);

28
include/types/moby.h Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#include <tuple>
#include <types/entity.h>
#include "animation/scriptedMove.h"
//Movable Object.
class Moby : public Entity {
public:
Moby(): Entity(), velAngle({0,0,0})
{
}
Angle angle = {0,0,0}; //Pitch Yaw Roll, The orientation of the entity in the world,
Angle velAngle = {0,0,0}; //The angle of an entities velocity.
glm::vec3 Velocity;
vector3 upVector = {0.0f,1.0f,0.0f};
ScriptedMove scriptedMove;
void move(Angle a, float speed);
Angle fAngle(); // forward angle
Angle bAngle(); // back angle
Angle lAngle(); // left angle
Angle rAngle(); // right angle
void doScriptedMovement();
};

64
include/types/player.h Normal file
View File

@@ -0,0 +1,64 @@
#pragma once
#include <iostream>
#include <cmath>
#include "moby.h"
#include "entityList.h"
#include "../engine/engine.h"
#include "vertex.h"
class Player : public Moby {
public:
bool alive;
uint8_t health;
uint8_t state;
Position cameraTarget;//The point where the 3rd-person camera will want to look at.
VertexArray geometry;
//Each type of entity will have an "update" function and a "render" function.
//These will be declared in each type of entity and not in the base entity because
//It'll be different for each one.
//The "camera point" is the position the camera will want to be while following the player.
//We will probably end up having a different camera point class controlled by the "world".
Position cameraPoint (float distance) {
Position behindPosition = this->position;
Angle reverseDirection = this->bAngle();
behindPosition.x -= reverseDirection.x * distance;
behindPosition.y -= reverseDirection.y * distance;
behindPosition.z -= reverseDirection.z * distance;
return behindPosition;
}
void update(float elapsed) override {}
//if (engine->tickCount >=10)
//std::cout << "Player* Hopefully: " << scriptedMove.parent->position.x;
void pre_render() override {
// PLACEHOLDER LOL.
// William continues to load assets on the first frame.
if (engine->frameCount == 1) {
geometry.load("../models/cube.obj");
geometry.scale(0.25f);
position.set(0,-2,0);
}
//Rotate
this->angle.y += 200.0*engine->frameDelta;
}
void render() override {
glColor3f(0.75,0.75,0.75);
glPushMatrix();
glRotatef(-angle.x,1.0f, 0.0f, 0.0f);
glRotatef(-angle.y,0.0f, 1.0f, 0.0f);
glTranslatef(position.x ,position.y,position.z);
geometry.draw();
glPopMatrix();
}
};
// Shit like this is just my
inline Player* getPlayer() {
for (auto& e : entityList)
if (auto* p = dynamic_cast<Player*>(e) )
return p;
std::cerr << "Attempt to get Player pointer while not in scene." << std::endl;
engine->quit();
}

36
include/types/skybox.h Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#include <SDL_opengl.h>
#include "entityList.h"
#include "moby.h"
#include "camera.h"
#include "vertex.h"
class Skybox : public Entity {
public:
VertexArray geometry;
void pre_render() {
//PLACEHOLDER.
if (engine->frameCount == 1) {
geometry.load("../models/cube.obj");
}
}
void render() {
glColor3f(0.75,0.75,0.75);
glPushMatrix();
glTranslatef(position.x + 4,position.y,position.z);
//geometry.draw();
glPopMatrix();
}
};
inline Skybox* getSkybox() {
for (auto& e : entityList)
if (auto* s = dynamic_cast<Skybox*>(e))
return s;
std::cerr << "Attempt to get Skybox pointer while not in scene." << std::endl;
engine->quit();
}

162
include/types/vector3.h Normal file
View File

@@ -0,0 +1,162 @@
#pragma once
#include <cstdint>
#include <cmath>
#include <cstdlib>
#include <bits/ranges_algo.h>
#include <glm/ext/scalar_constants.hpp>
inline float lerp(float a, float b, float t) {
}
// A stab at a vector<length> implementation suitable for game engineering
template <std::size_t length>
class numeric_vector<length> {
public:
virtual float operator[](std::size_t index) = 0;
};
class vector2 : public numeric_vector<2>
{
public:
float x = 0;
float y = 0;
float z = 0;
vector2();
};
class vector3 : public numeric_vector<3>
{
public:
float x = 0;
float y = 0;
float z = 0;
public:
vector3() : x(0), y(0), z(0) {}
vector3(float X, float Y, float Z): x(X), y(Y), z(Z) {}
vector3(const vector3&); // Copy
vector3(vector3&&) = default;
vector3(const vector2&);
float operator[](std::size_t index) override;
bool IsWithinMarginOfError(const vector3& rhs, float margin=0.001f) const;
bool operator == (const vector3& rhs) const;
bool operator != (const vector3& rhs) const;
vector3 min(const vector3& min) const;
vector3 max(const vector3& max) const;
vector3 clamp(const vector3& min, const vector3& max) const;
float distance(const vector3& to) const;
float length() const;
float lengthSquared() const;
float magnitude() const;
float dot(const vector3& rhs) const;
vector3 project(const vector3& rhs) const;
vector3 cross(const vector3& rhs) const;
vector3 normalize() const;
vector3 lerp(const vector3& goal, float alpha) const;
vector3 operator+(const vector3& rhs) const;
vector3 operator-(const vector3& rhs) const;
vector3 operator*(float rhs) const;
vector3 operator/(float rhs) const;
vector3 operator+() const;
vector3 operator-() const;
};
class vector4 : public numeric_vector<4> {};
class Angle {
public:
float pitch;
float yaw;
float roll;
bool operator==(const Angle& a) const {
return (x == a.x) && (y == a.y) && (z == a.z);
}
void clamp() {
if (this->x > 89.0f)
this->x = 89.0f;
if (this->x <= -89.0f)
this->x = -89.0f;
//TODO: Make this entirely seamless by getting the amount they rotated passed -180 and +180 by.
if (this->y <= -180.0f)
this->y = 180.0f;
if (this->y >= 180.01f)
this->y = -179.9f;
if (this->z >= 360.0f)
this->z = 0.0;
if(this->z <= -360.0f)
this->z = 0.0;
}
[[nodiscard]] float length() const {
return sqrt(x * x + y * y + z * z);
}
Angle movementAngle() {
Angle a;
a.x = (cos(glm::radians(y)) * cos(glm::radians(x)));
a.y = -sin(glm::radians(x));
a.z = (sin(glm::radians(y)) * cos(glm::radians(x)));
return a;
}
};
class Position : public vector3 {
public:
bool operator==(const Position& p) const {
return (x == p.x) && (y == p.y) && (z == p.z);
}
void set(Position p) {
this->x = p.x;
this->y = p.y;
this->z = p.z;
}
void set(Position* p) {
this->x = p->x;
this->y = p->y;
this->z = p->z;
}
void set(vector3 v) {
this->x = v.x;
this->y = v.y;
this->z = v.z;
}
void set(vector3* v) {
this->x = v->x;
this->y = v->y;
this->z = v->z;
}
void set(float x, float y, float z) {
this->x = x;
this->y = y;
this->z = z;
}
float distanceFrom(Position p) {
return sqrt(pow(this->x - p.x, 2) + pow(this->y - p.y, 2) + pow(this->z - p.z, 2));
}
};
struct Movement {
Angle angle;
Position position;
};
inline namespace VectorMath {
inline float distance(Position sP, Position eP) {
return sqrt(pow(eP.x - sP.x, 2) + pow(eP.y - sP.y, 2) + pow(eP.z - sP.z, 2));
}
//Basically an aimbot.
inline Angle calcAngle(Position sP, Position eP) {
const auto pi = glm::pi<float>();
//returned.x = -(asinf((eP.y - sP.y) / distance(sP, eP)) * 180.0f / M_PI);
//returned.y = (atan2f(eP.x - sP.x,eP.z - sP.z) / M_PI * 180.0f);
return {static_cast<float>((-(asinf((eP.y - sP.y) / distance(sP, eP)) * 180.0f / pi ))),static_cast<float>((atan2f(eP.x - sP.x,eP.z - sP.z) / pi * 180.0f)),0};
}
}

69
include/types/vertex.h Normal file
View File

@@ -0,0 +1,69 @@
#pragma once
//#include <windows.h>
#include <vector>
#include <iostream>
#include <fstream>
#include <sstream>
#include <GL/gl.h>
#include <types/vector3.h>
struct Vertex {
GLfloat x, y, z;
};
class VertexArray {
public:
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
void scale(GLfloat multiplier) {
for (auto & vertice : vertices) {
vertice.x *= multiplier;
vertice.y *= multiplier;
vertice.z *= multiplier;
}
}
void load(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cout << "File not found: " << filename << std::endl;
engine->quit();
return;
}
std::string line;
while (std::getline(file, line)) {
std::istringstream stream(line);
std::string prefix;
stream >> prefix;
if (prefix == "v") {
float x, y, z;
stream >> x >> y >> z;
vertices.push_back({x, y, z});
} else if (prefix == "f") {
unsigned int v1, v2, v3;
stream >> v1 >> v2 >> v3;
indices.push_back(v1 - 1);
indices.push_back(v2 - 1);
indices.push_back(v3 - 1);
}
}
file.close();
}
//Wrong
void draw() {
glBegin(GL_TRIANGLES);
for (size_t i = 0; i < indices.size(); i += 3) {
glVertex3f(vertices[indices[i]].x, vertices[indices[i]].y, vertices[indices[i]].z);
glVertex3f(vertices[indices[i + 1]].x, vertices[indices[i + 1]].y, vertices[indices[i + 1]].z);
glVertex3f(vertices[indices[i + 2]].x, vertices[indices[i + 2]].y, vertices[indices[i + 2]].z);
}
glEnd();
}
};

5
include/types/world.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
namespace World {
}

114
src/engine/engine.cpp Normal file
View File

@@ -0,0 +1,114 @@
#include <engine/engine.h>
#include <GL/gl.h>
#include <GL/glu.h>
void Engine::quit() const {
SDL_DestroyWindow(this->window);
SDL_Quit();
exit(0);
}
float Engine::getGLVersion()
{
std::string str = reinterpret_cast<const char *>(glGetString(GL_VERSION));
str.erase(str.begin()+3,str.end());
return std::stof(str);
}
void Engine::initGL()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -0.5f, 0.5f);
gluPerspective(this->fov, this->windowWidth / this->windowHeight, this->nearPlane, this->farPlane);
this->glError = glGetError();
if (glError != GL_NO_ERROR) {
std::cerr << "OpenGL: " << gluErrorString(glError);
exit (1);
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (glError != GL_NO_ERROR) {
std::cerr << "OpenGL: " << gluErrorString(glError);
exit (1);
}
glClearColor(0.f, 0.f, 0.f, 1.f);
glViewport(0,0,this->windowWidth,this->windowHeight);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glClearDepth(1.0f);
glDisable(GL_CULL_FACE);
//glDepthRange(-0.5f,0.5f);
}
void Engine::initVideo()
{
Uint32 sdl_initializer_flags = SDL_INIT_VIDEO;// | SDL_INIT_AUDIO;
// Start SDL, check for errors
if (SDL_Init(sdl_initializer_flags) < 0) {
std::cerr << "SDL_Error: " << SDL_GetError() << std::endl;
exit(1);
}
// Create the window, set resizable, check for errors
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 1);
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 4);
SDL_GL_SetSwapInterval(0);
this->window = SDL_CreateWindow("SDL3D", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, this->windowWidth, this->windowHeight, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
SDL_SetWindowResizable(this->window,SDL_FALSE);
this->glContext = SDL_GL_CreateContext(window);
if (Engine::getGLVersion() < 1.4f) {
std::cerr << "Driver OpenGL version: " << Engine::getGLVersion() << std::endl;
std::cerr << "OpenGL >= 1.4 is required." << std::endl;
exit(1);
}
if (this->window == nullptr) {
std::cerr << "SDL_Error: " << SDL_GetError() << std::endl;
exit(1);
}
}
void Engine::debugInfo() const
{
if (this->debug) {
std::cout << "Last tick completed in: " << (tickDelta / 1000) << "ms, " << "sleeping " << ((minimumTickDelta - tickDelta) / 1000) << "ms." << std::endl;
std::cout << "Last frame dT: " << this->frameDelta << std::endl;
std::cout << "Framerate: " << framerate() << std::endl;
//std::cout << "EntityCount: " << entityList.list.size() << std::endl;
//std::cout << "Camera Position: " << "X " << entityList.getCamera()->position.x << " Y " << entityList.getCamera()->position.y << " Z " << entityList.getCamera()->position.z << std::endl;
}
}
float Engine::framerate() const
{
return 1.f / this->frameDelta;
}
void Engine::takeScreenshot() const {
#ifdef FREEIMAGE_FOUND
//Prevent taking a ton of screenshots really fast.
std::string fName = std::to_string(time(nullptr)) + ".bmp";
std::ifstream f (fName, std::ifstream::in);
if(f.good()) {
f.close();
return;
}
f.close();
FreeImage_Initialise();
auto* fbData = new GLubyte[this->windowWidth * this->windowHeight * 3];
glReadPixels(0, 0, this->windowWidth, this->windowHeight, GL_RGB, GL_UNSIGNED_BYTE, fbData);
FIBITMAP* image = FreeImage_ConvertFromRawBits(fbData, this->windowWidth, this->windowHeight, this->windowWidth * 3, 24, 0xFF0000, 0x00FF00, 0x0000FF, false);
FreeImage_Save(FIF_BMP, image, (fName.c_str()), 0);
FreeImage_Unload(image);
delete[] fbData;
FreeImage_DeInitialise();
#endif
}

14
src/types/camera.cpp Normal file
View File

@@ -0,0 +1,14 @@
#include <types/camera.h>
// Even tho CLion reports this as const-qualifiable
// It is not in "intent" (obviously the gametick changes gamestate)
void Camera::update()
{
if (engine->debug && engine->tickCount %64 == 0) {
std::cout << "Camera:" << std::endl;
std::cout << "X: " << position.x << " Y: " << position.y << " Z: " << position.z << std::endl;
std::cout << "Pitch: " << angle.x << " Yaw: " << angle.y << " Roll: " << angle.z << std::endl;
}
}

15
src/types/entity.cpp Normal file
View File

@@ -0,0 +1,15 @@
#include <types/entity.h>
#include <glm/vec3.hpp>
#include <types/vector3.h>
inline glm::vec3 Entity::GetPos() const
{ return glm::vec3(coordinates[1]); }
void Entity::SetPos(const glm::vec3& rhs)
{ coordinates[1] = glm::vec4(rhs.x, rhs.y, rhs.z, 0); }
glm::mat4 Entity::GetMatrix() const
{ return coordinates;}
void Entity::SetMatrix(const glm::mat4& rhs)
{ coordinates = rhs; }

73
src/types/moby.cpp Normal file
View File

@@ -0,0 +1,73 @@
#include <types/moby.h>
inline void Moby::move(Angle a, float speed)
{
this->position.z += (speed*engine->frameDelta) * a.x;
this->position.y += (speed*engine->frameDelta) * a.y;
this->position.x += (speed*engine->frameDelta) * a.z;
}
inline Angle Moby::fAngle()
{
Angle a;
a.x = (cos(glm::radians(this->angle.y)) * cos(glm::radians(this->angle.x)));
a.y = -sin(glm::radians(this->angle.x));
a.z = (sin(glm::radians(this->angle.y)) * cos(glm::radians(this->angle.x)));
return a;
}
Angle Moby::bAngle()
{
Angle a;
a.x = -cos(glm::radians(this->angle.y)) * cos(glm::radians(this->angle.x));
a.y = (sin(glm::radians(this->angle.x)));
a.z = -sin(glm::radians(this->angle.y)) * cos(glm::radians(this->angle.x));
return a;
}
Angle Moby::lAngle()
{
Angle f = fAngle();
Angle a;
a.x = f.y * upVector.z - f.z * upVector.y;
a.y = f.z * upVector.x - f.x * upVector.z;
a.z = f.x * upVector.y - f.y * upVector.x;
return a;
}
Angle Moby::rAngle()
{
Angle f = fAngle();
Angle a;
a.x = -(f.y * upVector.z - f.z * upVector.y);
a.y = (f.z * upVector.x - f.x * upVector.z);
a.z = -(f.x * upVector.y - f.y * upVector.x);
return a;
}
void Moby::doScriptedMovement()
{
//If the movement has a set starting position, Then teleport there.
if (scriptedMove.index == -1) {
this->position = scriptedMove.start;
scriptedMove.index++;
}
scriptedMove.start = position;
//We've reached the end of the movement script.
if (scriptedMove.index + 1 > scriptedMove.positions.size())
return;
Angle a = VectorMath::calcAngle(this->position, scriptedMove.positions[scriptedMove.index]).movementAngle();
move(a,scriptedMove.velocity);
//TODO: Fix this later, This will break at high speed or if the framerate is low.
//We'll have to detect if the next movement would make us go passed it.
//If it's *super* close just teleport it.
if (VectorMath::distance(this->position,scriptedMove.positions[scriptedMove.index]) <= 0.002) {
this->position = scriptedMove.positions[scriptedMove.index];
scriptedMove.index++;
}
}

149
src/types/vector.cpp Normal file
View File

@@ -0,0 +1,149 @@
#include <types/vector3.h>
vector3 vector3::operator+(const vector3& rhs) const
{
return {this->x + rhs.x, this->y + rhs.y, this->z + rhs.z};
}
vector3 vector3::operator-(const vector3& rhs) const
{
return {
this->x- rhs.x,
this->y-rhs.y,
this->z-rhs.z
};
}
vector3 vector3::operator*(float rhs) const
{
return {
this->x * rhs,
this->y * rhs,
this->z * rhs
};
}
vector3 vector3::operator/(float rhs) const
{
return {
this->x / rhs,
this->y / rhs,
this->z / rhs
};
}
vector3 vector3::operator-() const
{
return {-x, -y, -z};
}
float vector3::operator[](std::size_t index)
{
assert(index < 3);
if (index==0) return x;
if (index==1) return y;
if (index==2) return z;
return 0;
}
bool vector3::IsWithinMarginOfError(const vector3& rhs, float margin) const
{
return this->distance(rhs) <= margin;
}
bool vector3::operator==(const vector3& rhs) const
{
return this->IsWithinMarginOfError(rhs);
}
vector3 vector3::min(const vector3& min) const
{
return {
std::min(this->x, min.x),
std::min(this->y, min.y),
std::min(this->z, min.z)
};
}
vector3 vector3::max(const vector3& max) const
{
return {
std::max(this->x, max.x),
std::max(this->y, max.y),
std::max(this->z, max.z)
};
}
vector3 vector3::clamp(const vector3& min, const vector3& max) const
{
return {
std::ranges::clamp(min.x, this->x, max.x),
std::ranges::clamp(min.y, this->y, max.y),
std::ranges::clamp(min.z, this->z, max.z)
};
}
float vector3::distance(const vector3& to) const
{
return ((*this)-to).magnitude();
}
float vector3::length() const
{
return std::sqrt(lengthSquared());
}
float vector3::lengthSquared() const
{
return (x*x + y*y + z*z);
}
float vector3::magnitude() const
{
return std::sqrt(x*x + y*y + z*z);
}
float vector3::dot(const vector3& rhs) const
{
auto a = this->normalize();
auto b = rhs.normalize();
return a.x * b.x +
a.y * b.y +
a.z * b.z;
}
vector3 vector3::project(const vector3& rhs) const
{
float scalar = this->dot(rhs) / (rhs.magnitude()*rhs.magnitude());
return rhs * scalar;
}
vector3 vector3::cross(const vector3& rhs) const
{
return {
this->y * rhs.z - this->z * rhs.y,
this->z * rhs.x - this->x * rhs.z,
this->x * rhs.y - this->y * rhs.x
};
}
vector3 vector3::normalize() const
{
if (length() > 0)
return {
x / length(),
y / length(),
z / length()
};
else
return {0,0,0};
}
vector3 vector3::lerp(const vector3& goal, float alpha) const
{
return this->operator*(1.0f - alpha) + (goal * alpha);
}