Merge remote-tracking branch 'origin/windows-devel'
This commit is contained in:
56
CMakeLists.txt
Normal file
56
CMakeLists.txt
Normal 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
24
cmake/CPM.cmake
Normal 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
163
cmake/FindSDL2.cmake
Normal 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
64
cmake/glm.cmake
Normal 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
92
cmake/sdl.cmake
Normal 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
48
demo/main.cpp
Normal 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
46
include/engine/engine.h
Normal 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
107
include/engine/render.h
Normal 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
43
include/engine/tick.h
Normal 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();
|
||||
}
|
||||
}
|
60
include/types/animation/scriptedMove.h
Normal file
60
include/types/animation/scriptedMove.h
Normal 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
144
include/types/camera.h
Normal 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
49
include/types/entity.h
Normal 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;
|
||||
};
|
||||
|
||||
|
20
include/types/entityList.h
Normal file
20
include/types/entityList.h
Normal 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
28
include/types/moby.h
Normal 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
64
include/types/player.h
Normal 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
36
include/types/skybox.h
Normal 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
162
include/types/vector3.h
Normal 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
69
include/types/vertex.h
Normal 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
5
include/types/world.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace World {
|
||||
|
||||
}
|
114
src/engine/engine.cpp
Normal file
114
src/engine/engine.cpp
Normal 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
14
src/types/camera.cpp
Normal 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
15
src/types/entity.cpp
Normal 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
73
src/types/moby.cpp
Normal 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
149
src/types/vector.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user