Initial Commit

This commit is contained in:
2026-02-12 11:46:06 +03:00
commit b044c8d1a5
3973 changed files with 1599881 additions and 0 deletions

63
src/CMakeLists.txt Normal file
View File

@@ -0,0 +1,63 @@
file(GLOB ENGINE_SRC
"${CMAKE_CURRENT_SOURCE_DIR}/engine/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/engine/*.h"
"${CMAKE_CURRENT_SOURCE_DIR}/engine/*.inl"
)
source_group("engine" FILES ${ENGINE_SRC})
file(GLOB ENGINE_PHYSICS_SRC
"${CMAKE_CURRENT_SOURCE_DIR}/engine/physics/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/engine/physics/*.h"
"${CMAKE_CURRENT_SOURCE_DIR}/engine/physics/*.inl"
)
source_group("engine/physics" FILES ${ENGINE_PHYSICS_SRC})
file(GLOB RENDER_SRC
"${CMAKE_CURRENT_SOURCE_DIR}/render/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/render/*.h"
"${CMAKE_CURRENT_SOURCE_DIR}/render/*.inl"
)
source_group("render" FILES ${RENDER_SRC})
file(GLOB SOUND_SRC
"${CMAKE_CURRENT_SOURCE_DIR}/sound/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/sound/*.h"
"${CMAKE_CURRENT_SOURCE_DIR}/sound/*.inl"
)
source_group("sound" FILES ${SOUND_SRC})
file(GLOB GAME_SRC
"${CMAKE_CURRENT_SOURCE_DIR}/game/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/game/*.h"
"${CMAKE_CURRENT_SOURCE_DIR}/game/*.inl"
)
source_group("game" FILES ${GAME_SRC})
add_executable(engine WIN32
${ENGINE_SRC}
${ENGINE_PHYSICS_SRC}
${RENDER_SRC}
${SOUND_SRC}
${GAME_SRC}
)
target_include_directories(engine PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}"
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/engine"
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/engine/physics"
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/game"
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/render"
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/sound"
)
#target_precompile_headers(engine PRIVATE "pch.h")
target_include_directories(engine PRIVATE "${CMAKE_SOURCE_DIR}/thirdparty/glm")
target_include_directories(engine PRIVATE "${CMAKE_SOURCE_DIR}/thirdparty/stb")
target_include_directories(engine PRIVATE "${CMAKE_SOURCE_DIR}/thirdparty/openal")
target_include_directories(engine PRIVATE "${CMAKE_SOURCE_DIR}/thirdparty/SDL2-2.26.5")
target_include_directories(engine PRIVATE "${CMAKE_SOURCE_DIR}/thirdparty/miniaudio-0.11.24")
target_include_directories(engine PRIVATE "${CMAKE_SOURCE_DIR}/thirdparty/")
target_link_directories(engine PRIVATE "${CMAKE_SOURCE_DIR}/thirdparty/openal")
target_link_libraries(engine SDL3::SDL3 pugixml luaplus glad bullet)

View File

@@ -0,0 +1,30 @@
#include "boundingbox.h"
void BoundingBox::SetIdentity()
{
m_min = glm::vec3(-0.1f);
m_max = glm::vec3(0.1f);
}
BoundingBox& BoundingBox::TransformAABB(const glm::mat4& modelMatrix)
{
// transform to center/extents box representation
glm::vec3 center = (m_max + m_min) * glm::vec3(0.5);
glm::vec3 extents = m_max - center;
// transform center
glm::vec3 t_center = glm::vec3(modelMatrix * glm::vec4(center, 1.0));
// transform extents (take maximum)
glm::mat3 abs_mat = glm::mat3(abs(glm::vec3(modelMatrix[0])), abs(glm::vec3(modelMatrix[1])), abs(glm::vec3(modelMatrix[2])));
glm::vec3 t_extents = abs_mat * extents;
// transform to min/max box representation
glm::vec3 tmin = t_center - t_extents;
glm::vec3 tmax = t_center + t_extents;
m_min = tmin;
m_max = tmax;
return *this;
}

41
src/engine/boundingbox.h Normal file
View File

@@ -0,0 +1,41 @@
#ifndef BOUNDINGBOX_H
#define BOUNDINGBOX_H
#include <glm/glm.hpp>
class BoundingBox
{
public:
glm::vec3 m_min;
glm::vec3 m_max;
public:
void SetIdentity();
BoundingBox& TransformAABB(const glm::mat4& modelMatrix);
//! Returns a bool whether the given point is inside the bounding volume.
bool Contains(const glm::vec3& pPoint) const
{
return m_min.x <= pPoint.x && m_min.y <= pPoint.y && m_min.z <= pPoint.z &&
m_max.x >= pPoint.x && m_max.y >= pPoint.y && m_max.z >= pPoint.z;
}
bool Contains(const BoundingBox& pBox) const
{
return Contains(pBox.m_min) && Contains(pBox.m_max);
}
glm::vec3 GetCenter() const
{
return m_min + (m_max - m_min) * 0.5f;
}
float GetExtend() const
{
return glm::length((m_max - m_min)) * 0.5f;
}
};
#endif

92
src/engine/camera.cpp Normal file
View File

@@ -0,0 +1,92 @@
#include "camera.h"
Camera::Camera()
{
m_projectionType = PERSPECTIVE;
m_position = glm::vec3(0.0f);
m_direction = glm::vec3(0.0f, 0.0f, 1.0f);
m_front = glm::vec3(0.0f, 0.0f, 0.0f);
m_right = glm::vec3(0.0f, 0.0f, 0.0f);
m_up = glm::vec3(0.0f, 1.0f, 0.0f);
m_fov = 70.0f;
m_yaw = 0.0f;
m_pitch = 0.0f;
}
Camera::~Camera()
{
}
void Camera::SetYawPitch(float yaw, float pitch)
{
m_yaw = yaw;
m_pitch = pitch;
m_direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
m_direction.y = sin(glm::radians(pitch));
m_direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
m_front = glm::normalize(m_direction);
m_right = glm::normalize(glm::cross(m_front, m_up));
}
glm::quat Camera::GetOrientation()
{
const glm::quat rotQuatY = glm::angleAxis(glm::radians(-GetYaw()), glm::vec3(0, 1, 0));
const glm::quat rotQuatX = glm::angleAxis(glm::radians(GetPitch()), GetRight());
return rotQuatX * rotQuatY;
}
glm::mat4 Camera::GetProjectionMatrix(const Viewport& viewport) const
{
glm::mat4 proj = glm::mat4(1.0f);
if (m_projectionType == ORTHOGONAL)
{
proj = glm::ortho(0.0f, (float)viewport.width, 0.0f, (float)viewport.height, 0.1f, 1000.0f);
}
else // if (m_projectionType == PERSPECTIVE): Control return in all path's
{
float aspectRatio = (float)viewport.width / (float)viewport.height;
proj = glm::perspective(glm::radians(m_fov), aspectRatio, 0.01f, 100.0f);
}
return proj;
}
glm::mat4 Camera::GetViewMatrix() const
{
glm::mat4 view = glm::mat4(1.0f);
view = glm::lookAt(m_position, m_position + m_front, m_up);
return view;
}
glm::mat4 Camera::GetTranslationMatrix() const
{
glm::mat4 t = glm::mat4(1.0f);
t = glm::translate(t, m_position);
t = glm::rotate(t, m_direction.x, glm::vec3(1.0f, 0.0f, 0.0f));
t = glm::rotate(t, m_direction.y, glm::vec3(0.0f, 1.0f, 0.0f));
t = glm::rotate(t, m_direction.z, glm::vec3(0.0f, 0.0f, 1.0f));
return t;
}
Frustum& Camera::GetFrustum()
{
return m_frustum;
}
//////////////////////////////////////////////////////////////////////////
// Camera Manager
CameraManager g_cameraManager;
void CameraManager::SetActiveCamera(Camera* p_camera)
{
m_activeCamera = p_camera;
}
Camera* CameraManager::GetActiveCamera() const
{
return m_activeCamera;
}

232
src/engine/camera.h Normal file
View File

@@ -0,0 +1,232 @@
#ifndef CAMERA_H
#define CAMERA_H
#include "boundingbox.h"
#include "render_shared.h"
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/norm.hpp>
enum CameraProjection_t
{
ORTHOGONAL,
PERSPECTIVE,
};
enum FrustumSide_t
{
FRUSTUM_TOP,
FRUSTUM_BOTTOM,
FRUSTUM_RIGHT,
FRUSTUM_LEFT,
FRUSTUM_FAR,
FRUSTUM_NEAR,
FRUSTUM_MAX
};
enum Halfspace_t
{
HALFSPACE_NEGATIVE = -1,
HALFSPACE_ONPLANE = 0,
HALFSPACE_POSITIVE = 1
};
struct Plane
{
glm::vec3 normal;
float distance;
Plane() : normal(0.0f, 1.0f, 0.0f), distance(0.0f)
{
}
inline void Normalize();
inline float ClassifyPoint(const glm::vec3& point) const;
};
inline void Plane::Normalize()
{
// calculate vector magnitude
float mag = glm::length(normal);
// divide plane normal and distance
normal /= mag;
distance /= mag;
}
inline float Plane::ClassifyPoint(const glm::vec3& point) const
{
return glm::dot(normal, point) + distance;
}
struct Sphere
{
glm::vec3 center;
float radius;
Sphere() : center(0.0f), radius(0.0f)
{
}
};
class Frustum
{
public:
Plane m_planes[FRUSTUM_MAX];
public:
inline void Build(const glm::mat4& a_ViewProj);
inline bool CullBoundingBox(const BoundingBox& a_Bounds);
inline bool CullSphere(const Sphere& a_Sphere);
};
inline void Frustum::Build(const glm::mat4& a_ViewProj)
{
// left
m_planes[FRUSTUM_LEFT].normal.x = a_ViewProj[3][0] + a_ViewProj[0][0];
m_planes[FRUSTUM_LEFT].normal.y = a_ViewProj[3][1] + a_ViewProj[0][1];
m_planes[FRUSTUM_LEFT].normal.z = a_ViewProj[3][2] + a_ViewProj[0][2];
m_planes[FRUSTUM_LEFT].distance = a_ViewProj[3][3] + a_ViewProj[0][3];
m_planes[FRUSTUM_LEFT].Normalize();
// right
m_planes[FRUSTUM_RIGHT].normal.x = a_ViewProj[3][0] - a_ViewProj[0][0];
m_planes[FRUSTUM_RIGHT].normal.y = a_ViewProj[3][1] - a_ViewProj[0][1];
m_planes[FRUSTUM_RIGHT].normal.z = a_ViewProj[3][2] - a_ViewProj[0][2];
m_planes[FRUSTUM_RIGHT].distance = a_ViewProj[3][3] - a_ViewProj[0][3];
m_planes[FRUSTUM_RIGHT].Normalize();
// bottom
m_planes[FRUSTUM_BOTTOM].normal.x = a_ViewProj[3][0] + a_ViewProj[1][0];
m_planes[FRUSTUM_BOTTOM].normal.y = a_ViewProj[3][1] + a_ViewProj[1][1];
m_planes[FRUSTUM_BOTTOM].normal.z = a_ViewProj[3][2] + a_ViewProj[1][2];
m_planes[FRUSTUM_BOTTOM].distance = a_ViewProj[3][3] + a_ViewProj[1][3];
m_planes[FRUSTUM_BOTTOM].Normalize();
// top
m_planes[FRUSTUM_TOP].normal.x = a_ViewProj[3][0] - a_ViewProj[1][0];
m_planes[FRUSTUM_TOP].normal.y = a_ViewProj[3][1] - a_ViewProj[1][1];
m_planes[FRUSTUM_TOP].normal.z = a_ViewProj[3][2] - a_ViewProj[1][2];
m_planes[FRUSTUM_TOP].distance = a_ViewProj[3][3] - a_ViewProj[1][3];
m_planes[FRUSTUM_TOP].Normalize();
// near
m_planes[FRUSTUM_NEAR].normal.x = a_ViewProj[3][0] + a_ViewProj[2][0];
m_planes[FRUSTUM_NEAR].normal.y = a_ViewProj[3][1] + a_ViewProj[2][1];
m_planes[FRUSTUM_NEAR].normal.z = a_ViewProj[3][2] + a_ViewProj[2][2];
m_planes[FRUSTUM_NEAR].distance = a_ViewProj[3][3] + a_ViewProj[2][3];
m_planes[FRUSTUM_NEAR].Normalize();
// far
m_planes[FRUSTUM_FAR].normal.x = a_ViewProj[3][0] - a_ViewProj[2][0];
m_planes[FRUSTUM_FAR].normal.y = a_ViewProj[3][1] - a_ViewProj[2][1];
m_planes[FRUSTUM_FAR].normal.z = a_ViewProj[3][2] - a_ViewProj[2][2];
m_planes[FRUSTUM_FAR].distance = a_ViewProj[3][3] - a_ViewProj[2][3];
m_planes[FRUSTUM_FAR].Normalize();
}
inline bool Frustum::CullBoundingBox(const BoundingBox& a_Bounds)
{
glm::vec3 center = a_Bounds.GetCenter();
glm::vec3 magnitude = (a_Bounds.m_max - a_Bounds.m_min) * 0.5f;
for (int i = 0; i < FRUSTUM_MAX; i++)
{
const Plane& plane = m_planes[i];
float dist = plane.ClassifyPoint(center);
dist += fabsf(plane.normal.z) * magnitude.z;
dist += fabsf(plane.normal.y) * magnitude.y;
dist += fabsf(plane.normal.x) * magnitude.x;
if (dist < 0.0f)
return true;
}
return false;
}
inline bool Frustum::CullSphere(const Sphere& a_Sphere)
{
return m_planes[FRUSTUM_LEFT].ClassifyPoint(a_Sphere.center) >= -a_Sphere.radius &&
m_planes[FRUSTUM_RIGHT].ClassifyPoint(a_Sphere.center) >= -a_Sphere.radius &&
m_planes[FRUSTUM_TOP].ClassifyPoint(a_Sphere.center) >= -a_Sphere.radius &&
m_planes[FRUSTUM_BOTTOM].ClassifyPoint(a_Sphere.center) >= -a_Sphere.radius &&
m_planes[FRUSTUM_NEAR].ClassifyPoint(a_Sphere.center) >= -a_Sphere.radius &&
m_planes[FRUSTUM_FAR].ClassifyPoint(a_Sphere.center) >= -a_Sphere.radius;
}
class Camera
{
public:
Camera();
~Camera();
inline void LookAt(const glm::vec3& direction) { m_direction = direction /*- m_position*/; }
inline void SetProjection(CameraProjection_t projection) { m_projectionType = projection; }
inline CameraProjection_t GetProjection() const { return m_projectionType; }
// In degrees
inline void SetFOV(float fov) { m_fov = fov; }
inline float GetFOV() const { return m_fov; }
inline void SetPosition(const glm::vec3& position) { m_position = position; }
inline const glm::vec3& GetPosition() const { return m_position; }
void SetYawPitch(float yaw, float pitch);
inline const glm::vec3& GetDirection() const { return m_direction; }
inline const glm::vec3& GetFront() const { return m_front; }
inline const glm::vec3& GetRight() const { return m_right; }
inline const glm::vec3& GetUp() const { return m_up; }
inline float GetYaw() { return m_yaw; }
inline float GetPitch() { return m_pitch; }
glm::quat GetOrientation();
// FIXME: Control zNear/zFar in perspective projection
glm::mat4 GetProjectionMatrix(const Viewport& viewport) const;
glm::mat4 GetViewMatrix() const;
glm::mat4 GetTranslationMatrix() const;
Frustum& GetFrustum();
private:
Frustum m_frustum;
CameraProjection_t m_projectionType;
glm::vec3 m_position;
glm::vec3 m_direction;
glm::vec3 m_front;
glm::vec3 m_right;
glm::vec3 m_up;
float m_yaw;
float m_pitch;
float m_fov;
};
//////////////////////////////////////////////////////////////////////////
// Camera Manager
class CameraManager
{
public:
void SetActiveCamera(Camera* p_camera);
Camera* GetActiveCamera() const;
private:
Camera* m_activeCamera;
};
extern CameraManager g_cameraManager;
#endif // !CAMERA_H

121
src/engine/core.cpp Normal file
View File

@@ -0,0 +1,121 @@
#include <stdio.h>
#include <string.h>
#include "core.h"
#include "log.h"
#include <SDL3/SDL.h>
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#endif
void error_backend(const char* txt)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Engine Error", txt, nullptr);
}
void warning_backend(const char* txt)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Warning!", txt, nullptr);
}
//static void initializeConsole() {
// AllocConsole();
// freopen("CONOUT$", "w", stdout);
//}
int Core::ms_argc;
char** Core::ms_argv;
void Core::Init(int argc, char* argv[])
{
ms_argc = argc;
ms_argv = argv;
// initialize timer
//getSystemTimer()->init();
#ifdef WIN32
//initializeConsole();
#endif
Logger::Open();
}
void Core::Shutdown()
{
Logger::Close();
// shutdown timer
//getSystemTimer()->shutdown();
}
void Core::Frame()
{
//getSystemTimer()->update();
}
void Core::Error(const char* msg, ...)
{
static char buf[2048 * 2];
va_list args;
va_start(args, msg);
vsnprintf(buf, sizeof(buf), msg, args);
va_end(args);
Logger::Msg("ERROR! %s", buf);
error_backend(buf);
// __debugbreak();
#ifdef WIN32
ExitProcess(1);
#endif // WIN32
}
void Core::Warning(const char* msg, ...)
{
static char buf[2048 * 2];
va_list args;
va_start(args, msg);
vsnprintf(buf, sizeof(buf), msg, args);
va_end(args);
Logger::Msg("Warning! %s", buf);
warning_backend(buf);
}
bool Core::HasOption(const char* name)
{
for (int i = 0; i < ms_argc; i++) {
if (ms_argv[i]) {
if (strcmp(ms_argv[i], name) == 0)
return true;
}
}
return false;
}
const char* Core::GetOptionParameter(const char* name)
{
for (int i = 0; i < ms_argc; i++) {
if (ms_argv[i]) {
if (strcmp(ms_argv[i], name) == 0) {
if (!ms_argv[i + 1]) {
Core::Error("Commandline option %s doesn't have parameter!", name);
} else {
return ms_argv[i + 1];
}
}
}
}
return nullptr;
}

25
src/engine/core.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef CORE_H
#define CORE_H
#define SDL_ASSERT_LEVEL 3
#include <SDL3/SDL.h>
class Core
{
private:
static int ms_argc;
static char** ms_argv;
public:
static void Init(int argc, char* argv[]);
static void Shutdown();
static void Frame();
static void Error(const char* fmt, ...);
static void Warning(const char* fmt, ...);
static bool HasOption(const char* name);
static const char* GetOptionParameter(const char* name);
};
#endif // !CORE_H

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

@@ -0,0 +1,308 @@
#include <SDL3/SDL_main.h>
#include "ifilesystem.h"
#include "core.h"
#include "log.h"
#include "engine.h"
#include "entitymanager.h"
#include "world.h"
#include "camera.h"
#include "inputmanager.h"
// renderer
#include "render.h"
#include "renderdevice.h"
// game
#include "game.h"
static bool s_bCoreProfile = true;
void ParseCommandLine()
{
if (Core::HasOption("-help") || Core::HasOption("-?")) {
const char* help =
"Commandline arguments list:\n\n"
"\n-? or -help - open this help.\n"
"\n-map - load level after engine start.\n"
"\n-disablecoreprofile - disable OpenGL core profile.\n";
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Commandline help",
help,
nullptr);
exit(0);
}
else if (Core::HasOption("-disablecoreprofile")) {
s_bCoreProfile = false;
}
}
Engine::Engine()
{
}
Engine::~Engine()
{
}
void Engine::Init()
{
ParseCommandLine();
if (SDL_Init(SDL_INIT_VIDEO) != true) {
Core::Error("Failed to initialize SDL3. Error code: %s", SDL_GetError());
}
// Enable core profile for compability for new hardware
if (s_bCoreProfile)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#ifdef _DEBUG
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
#endif // _DEBUG
}
// Create render window
m_window = SDL_CreateWindow("Engine", 1280, 720, SDL_WINDOW_OPENGL);
if (!m_window) {
Core::Error("Failed to create window. Error code: %s", SDL_GetError());
}
// Initialize renderer
g_pRender = new Render();
g_pRender->Init(m_window);
// Initialize engine
g_entityManager->Init();
// Initialize game
g_game->Init();
if (Core::HasOption("-map")) {
const char* levelname = Core::GetOptionParameter("-map");
NewGame(levelname);
}
}
void Engine::Shutdown()
{
if (g_world) {
delete g_world;
g_world = nullptr;
}
g_game->Shutdown();
g_entityManager->Shutdown();
if (g_pRender) {
g_pRender->Shutdown();
delete g_pRender;
g_pRender = nullptr;
}
if (m_window) {
SDL_DestroyWindow(m_window);
m_window = nullptr;
}
}
void Engine::Run()
{
m_run = true;
while (m_run) {
Frame_SDL();
Core::Frame();
Frame();
}
}
void Engine::Frame_SDL()
{
// clear input system
g_inputManager.Frame();
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_EVENT_QUIT) {
m_run = false;
}
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE) {
m_run = false;
}
// keyboard action
if (event.key.key < kMaxKeyboardKeys) {
if (event.type == SDL_EVENT_KEY_DOWN) {
g_inputManager.OnKeyboardAction(event.key.key, true);
}
else if (event.type == SDL_EVENT_KEY_UP) {
g_inputManager.OnKeyboardAction(event.key.key, false);
}
}
// mouse action
if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
g_inputManager.OnMouseAction(GetMouseButtonsFromSDL(event.button.button), true);
}
else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP) {
g_inputManager.OnMouseAction(GetMouseButtonsFromSDL(event.button.button), false);
}
// mouse pos action
if (event.type == SDL_EVENT_MOUSE_MOTION) {
g_inputManager.OnMouseMoveAction(event.motion.xrel, event.motion.yrel);
}
}
}
void Engine::Frame()
{
// *** updating
// Get current ticks
static Uint64 frequency = SDL_GetPerformanceFrequency();
static Uint64 oldTime = 0;
Uint64 currentTime = SDL_GetPerformanceCounter();
if (currentTime <= oldTime)
currentTime = oldTime + 1;
float dt = oldTime > 0 ? (float)((double)(currentTime - oldTime) / frequency) : (float)(1.0f / 60.0f);
// update entities
if (g_world)
g_world->Update(dt);
// *** rendering
RenderFrame();
// save old time
oldTime = currentTime;
}
void Engine::RenderFrame()
{
// *** start rendering
int windowSizeX = 0, windowSizeY = 0;
SDL_GetWindowSize(m_window, &windowSizeX, &windowSizeY);
// initialize matrices
Camera* camera = g_cameraManager.GetActiveCamera();
if (camera)
{
Viewport vp = { 0,0,windowSizeX,windowSizeY };
g_pRender->SetProjectionMatrix(camera->GetProjectionMatrix(vp));
g_pRender->SetViewMatrix(camera->GetViewMatrix());
}
// install viewport
g_pRenderDevice->SetViewport(0, 0, windowSizeX, windowSizeY);
g_pRenderDevice->Clear(TST_COLOR | TST_DEPTH, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0xffffffff);
g_pRender->RenderScene();
g_pRender->Present();
}
static bool Server_validateMap(const char* mapname)
{
char buffer[kMaxPathLength];
snprintf(buffer, kMaxPathLength, "data/levels/%s/%s.xml", mapname, mapname);
if (!GetFileSystem()->IsExist(buffer)) {
Logger::Msg("Server::ValidateMap: map %s is not exist", mapname);
return false;
}
return true;
}
void Engine::NewGame(const char* mapname)
{
if (!Server_validateMap(mapname)) {
Logger::Msg("Engine::NewGame: map %s doesnt exist", mapname);
return;
}
// create entity container
Disconnect();
g_world = new World();
g_pRender->LoadSceneXML(mapname);
// after initializing client scene and collision system - we initialize the server game
g_game->InitForNewMap(mapname);
}
void Engine::Disconnect()
{
g_cameraManager.SetActiveCamera(nullptr);
if (g_world) {
delete g_world;
g_world = nullptr;
}
}
SDL_Window* Engine::GetWindow()
{
return m_window;
}
Engine g_engine;
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
HANDLE g_executionLockerMutex;
void initializeExecutionLockerMutex()
{
g_executionLockerMutex = OpenMutexA(READ_CONTROL, FALSE, "engine_execution_locker_mutex");
if (!g_executionLockerMutex) {
g_executionLockerMutex = CreateMutexA(NULL, FALSE, "engine_execution_locker_mutex");
} else {
MessageBoxA(NULL, "Failed to open second instance of application.", "Error", MB_OK | MB_ICONERROR);
ExitProcess(1);
}
}
#endif // WIN32
int main(int argc, char* argv[])
{
#ifdef WIN32
initializeExecutionLockerMutex();
#endif // WIN32
Core::Init(argc, argv);
g_engine.Init();
g_engine.Run();
g_engine.Shutdown();
Core::Shutdown();
return 0;
}
Engine* GetEngine()
{
return &g_engine;
}

36
src/engine/engine.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef ENGINE_H
#define ENGINE_H
#include <SDL3/SDL.h>
class Engine
{
public:
Engine();
~Engine();
void Init();
void Shutdown();
void Run();
void Frame_SDL();
void Frame();
void RenderFrame();
void NewGame(const char* mapname);
void Disconnect();
SDL_Window* GetWindow();
private:
SDL_Window* m_window = nullptr;
bool m_run = true;
};
Engine* GetEngine();
#endif

View File

@@ -0,0 +1,64 @@
#include "core.h"
#include "log.h"
#include "entitymanager.h"
static EntityManager s_entityManager;
EntityManager* g_entityManager = &s_entityManager;
void EntityManager::Init()
{
Logger::Msg("Initializing entity manager ...");
RegisterEntities();
}
void EntityManager::Shutdown()
{
}
void EntityManager::RegisterEntities()
{
SDL_assert(EntityRegistrator::ms_entityRegistrators);
for (EntityRegistrator* entityReg = EntityRegistrator::ms_entityRegistrators;
entityReg != nullptr;
entityReg = entityReg->m_nextEntityRegistrator)
{
m_entityRegistrationInfo.push_back(entityReg->m_entityRegInfo);
}
Logger::Msg("total registered %i entities", m_entityRegistrationInfo.size());
}
void EntityManager::Update(float dt)
{
//for (std::vector<IEntityBase*>::iterator it = m_entities.begin();
// it != m_entities.end();
// ++it)
//{
// IEntityBase* entity = (*it);
// if (entity) {
// entity->Update(dt);
// }
//}
}
IEntityBase* EntityManager::CreateEntity(const char* classname)
{
for (std::vector<EntityRegistrationInfo>::iterator it = m_entityRegistrationInfo.begin();
it != m_entityRegistrationInfo.end();
++it)
{
EntityRegistrationInfo& info = (*it);
if (strcmp(info.m_entityName, classname) == 0) {
IEntityBase* entity = info.m_createEntityFunc();
entity->SetClassname(info.m_entityName);
//m_entities.push_back(entity);
return entity;
}
}
Core::Error("EntityManager::CreateEntity: can't create entity with unknown '%s' class name!", classname);
return nullptr;
}

View File

@@ -0,0 +1,31 @@
#ifndef ENTITYMANAGER_H
#define ENTITYMANAGER_H
#include <vector>
#include "ientity.h"
#include "entityregistrator.h"
class IEntityBase;
class EntityManager
{
public:
void Init();
void Shutdown();
void Update(float dt);
IEntityBase* CreateEntity(const char* classname);
private:
void RegisterEntities();
private:
std::vector<EntityRegistrationInfo> m_entityRegistrationInfo;
// std::vector<IEntityBase*> m_entities;
};
extern EntityManager* g_entityManager;
#endif // !ENTITYMANAGER_H

View File

@@ -0,0 +1,10 @@
#include "engine/entityregistrator.h"
EntityRegistrator* EntityRegistrator::ms_entityRegistrators;
EntityRegistrator::EntityRegistrator(EntityRegistrationInfo& regInfo) :
m_entityRegInfo(regInfo)
{
m_nextEntityRegistrator = ms_entityRegistrators;
ms_entityRegistrators = this;
}

View File

@@ -0,0 +1,30 @@
#ifndef ENTITYREGISTRATOR_H
#define ENTITYREGISTRATOR_H
class IEntityBase;
struct EntityRegistrationInfo
{
IEntityBase* (*m_createEntityFunc)();
const char* m_entityName;
};
template <typename T>
IEntityBase* createEntityTemplated()
{
return new T();
}
class EntityRegistrator
{
public:
EntityRegistrator(EntityRegistrationInfo& regInfo);
public:
EntityRegistrationInfo m_entityRegInfo;
EntityRegistrator* m_nextEntityRegistrator;
static EntityRegistrator* ms_entityRegistrators;
};
#endif // !ENTITYREGISTRATOR_H

121
src/engine/filesystem.cpp Normal file
View File

@@ -0,0 +1,121 @@
#include "filesystem.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <io.h>
FileSystem::FileSystem()
{
m_iOpenedFiles = 0;
memset(m_Files, 0, sizeof(m_Files));
}
FileSystem::~FileSystem()
{
}
void FileSystem::Init()
{
}
void FileSystem::Shutdown()
{
// find already opened file
for (int i = 0; i < MAX_OPENED_FILES; i++)
{
if (m_Files[i].m_pFile)
{
fclose(m_Files[i].m_pFile);
m_Files[i].m_pFile = nullptr;
}
}
}
FileHandle_t FileSystem::OpenFile(const char* pFilename, const char* pMode)
{
int iHandle;
// find already opened file
for (int i = 0; i < MAX_OPENED_FILES; i++)
{
if (m_Files[i].m_pFile && strcmp(m_Files[i].m_szFilename, pFilename) == 0)
return i;
}
if (!IsExist(pFilename) && pMode[0] == 'r')
{
printf("FileSystem::OpenFile: '%s' is not exist\n", pFilename);
return -1;
}
// Find free handle
for (int i = 0; i < MAX_OPENED_FILES; i++)
{
if (m_Files[i].m_pFile == NULL)
{
iHandle = i;
break;
}
}
m_Files[iHandle].m_pFile = fopen(pFilename, pMode);
assert(m_Files[iHandle].m_pFile);
m_iOpenedFiles++;
return iHandle;
}
void FileSystem::CloseFile(FileHandle_t hFile)
{
assert(hFile <= MAX_OPENED_FILES);
fclose(m_Files[hFile].m_pFile);
m_Files[hFile].m_pFile = NULL;
m_iOpenedFiles--;
}
size_t FileSystem::GetFileLength(FileHandle_t hFile)
{
assert(hFile <= MAX_OPENED_FILES);
assert(m_Files[hFile].m_pFile);
size_t size;
fseek(m_Files[hFile].m_pFile, 0, SEEK_END);
size = (size_t)ftell(m_Files[hFile].m_pFile);
fseek(m_Files[hFile].m_pFile, 0, SEEK_SET);
return size;
}
void FileSystem::ReadFile(FileHandle_t hFile, void* pData, size_t uiSize)
{
assert(hFile <= MAX_OPENED_FILES);
assert(m_Files[hFile].m_pFile);
fread(pData, uiSize, 1, m_Files[hFile].m_pFile);
}
void FileSystem::WriteFile(FileHandle_t hFile, void* pData, size_t uiSize)
{
assert(hFile <= MAX_OPENED_FILES);
assert(m_Files[hFile].m_pFile);
fwrite(pData, uiSize, 1, m_Files[hFile].m_pFile);
}
bool FileSystem::IsExist(const char* pFilename)
{
return _access(pFilename, /*F_OK*/ 0) == 0;
}
extern "C" __declspec(dllexport) IFileSystem* GetFileSystem()
{
static FileSystem fileSystem;
return &fileSystem;
}

46
src/engine/filesystem.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef FILESYSTEM_H
#define FILESYSTEM_H
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <stdio.h>
#include "ifilesystem.h"
#define MAX_OPENED_FILES 16
#define MAX_FNAME 260
struct FileHandleImpl
{
char m_szFilename[MAX_FNAME];
FILE* m_pFile;
};
class FileSystem : public IFileSystem
{
public:
FileSystem(void);
~FileSystem(void);
void Init();
void Shutdown();
FileHandle_t OpenFile(const char* pFilename, const char* pMode);
void CloseFile(FileHandle_t hFile);
// File API
size_t GetFileLength(FileHandle_t hFile);
void ReadFile(FileHandle_t hFile, void* pData, size_t uiSize);
void WriteFile(FileHandle_t hFile, void* pData, size_t uiSize);
bool IsExist(const char* pFilename);
private:
FileHandleImpl m_Files[MAX_OPENED_FILES];
int m_iOpenedFiles;
};
#endif // !FILESYSTEM_H

38
src/engine/ientity.cpp Normal file
View File

@@ -0,0 +1,38 @@
#include "ientity.h"
#include <glm/gtx/quaternion.hpp>
IEntityBase::IEntityBase() :
m_worldTM(1.0f),
m_position(0.0f),
m_rotation(0.0f),
m_scale(1.0f),
m_classname(nullptr)
{
}
IEntityBase::~IEntityBase()
{
}
void IEntityBase::Update(float dt)
{
}
void IEntityBase::Render()
{
}
const glm::mat4& IEntityBase::GetWorldTransform()
{
return m_worldTM;
}
void IEntityBase::UpdateTransform()
{
glm::vec3 radiansRotation = glm::vec3(glm::radians(m_rotation.x), glm::radians(m_rotation.y), glm::radians(m_rotation.z));
glm::mat4 rotation = glm::toMat4(glm::quat(radiansRotation));
m_worldTM = glm::mat4(1.0f);
m_worldTM = glm::translate(m_worldTM, m_position) * rotation * glm::scale(m_worldTM, m_scale);
}

46
src/engine/ientity.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef IENTITY_H
#define IENTITY_H
#include "boundingbox.h"
class IEntityBase
{
public:
IEntityBase();
virtual ~IEntityBase();
virtual void Update(float dt);
virtual void Render();
const char* GetClassname() { return m_classname; }
void SetClassname(const char* classname) { m_classname = classname; }
const BoundingBox& GetBoundingBox() { return m_boundingBox; }
const glm::mat4& GetWorldTransform();
void UpdateTransform();
protected:
glm::vec3 m_position;
glm::vec3 m_rotation;
glm::vec3 m_scale;
glm::mat4 m_worldTM;
BoundingBox m_boundingBox;
const char* m_classname;
};
// registration
#include "engine/entityregistrator.h"
#define REGISTER_ENTITY(CLASSNAME) \
static EntityRegistrationInfo s_##CLASSNAME##RegisterInfo = { &createEntityTemplated<CLASSNAME>, #CLASSNAME }; \
static EntityRegistrator s_##CLASSNAME##Registrator(s_##CLASSNAME##RegisterInfo)
#define REGISTER_ENTITY_EX(CLASSNAME, NAME) \
static EntityRegistrationInfo s_##CLASSNAME##RegisterInfo = { &createEntityTemplated<CLASSNAME>, NAME }; \
static EntityRegistrator s_##CLASSNAME##Registrator(s_##CLASSNAME##RegisterInfo)
#endif // !ENTITYBASE_H

32
src/engine/ifilesystem.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef IFILESYSTEM_H
#define IFILESYSTEM_H
// Constants
static const int kInvalidFileHandleValue = -1;
static const int kMaxPathLength = 260;
// File handle type
typedef int FileHandle_t;
// The file system interface.
class IFileSystem
{
public:
virtual void Init() = 0;
virtual void Shutdown() = 0;
virtual FileHandle_t OpenFile( const char* pFilename, const char* pMode ) = 0;
virtual void CloseFile( FileHandle_t hFile ) = 0;
// File API
virtual size_t GetFileLength( FileHandle_t hFile ) = 0;
virtual void ReadFile( FileHandle_t hFile, void* pData, size_t uiSize ) = 0;
virtual void WriteFile( FileHandle_t hFile, void* pData, size_t uiSize ) = 0;
virtual bool IsExist( const char* pFilename ) = 0;
};
extern "C" __declspec(dllexport) IFileSystem* GetFileSystem();
#endif // !FILESYSTEM_H

View File

@@ -0,0 +1,68 @@
#include <SDL3/SDL.h>
#include "engine/inputmanager.h"
#include "engine/engine.h"
InputManager g_inputManager;
void InputManager::OnKeyboardAction(uint32_t key, bool action)
{
if (key >= kMaxKeyboardKeys)
return;
m_keyboard.m_keys[key] = action;
}
void InputManager::OnMouseAction(uint32_t key, bool action)
{
assert(key <= kMaxMouseKeys);
m_mouse.m_keys[key] = action;
}
void InputManager::OnMouseMoveAction(float x, float y)
{
m_deltaMousePos.x = x - m_mousePos.x;
m_deltaMousePos.y = y - m_mousePos.y;
if (m_relativeMouseMode) {
m_mousePos.x += x;
m_mousePos.y += y;
} else {
m_mousePos.x = x;
m_mousePos.y = y;
}
}
void InputManager::SetRelativeMouseMode(bool relativeMode)
{
if (m_relativeMouseMode != relativeMode) {
SDL_SetWindowRelativeMouseMode(GetEngine()->GetWindow(), relativeMode);
m_relativeMouseMode = relativeMode;
}
}
void InputManager::Frame()
{
m_deltaMousePos.x = 0;
m_deltaMousePos.y = 0;
m_mousePos.x = 0;
m_mousePos.y = 0;
}
EMouseButtons GetMouseButtonsFromSDL(uint8_t button)
{
EMouseButtons mousebutton = EMouseButton_None;
if (button == SDL_BUTTON_LEFT)
mousebutton = EMouseButton_Left;
else if (button == SDL_BUTTON_MIDDLE)
mousebutton = EMouseButton_Middle;
else if (button == SDL_BUTTON_RIGHT)
mousebutton = EMouseButton_Right;
else if (button == SDL_BUTTON_X1)
mousebutton = EMouseButton_1;
else if (button == SDL_BUTTON_X2)
mousebutton = EMouseButton_2;
return mousebutton;
}

66
src/engine/inputmanager.h Normal file
View File

@@ -0,0 +1,66 @@
#ifndef INPUTMANAGER_H
#define INPUTMANAGER_H
#include <stdint.h>
#include <glm/glm.hpp>
const int kMaxKeyboardKeys = 460;
const int kMaxMouseKeys = 5;
enum EMouseButtons
{
EMouseButton_None,
EMouseButton_Left,
EMouseButton_Middle,
EMouseButton_Right,
EMouseButton_1,
EMouseButton_2,
};
template <int KEYCOUNT>
class InputLayout
{
public:
bool IsKeyDown(uint32_t key)
{
assert(key <= KEYCOUNT);
return m_keys[key];
}
bool m_keys[KEYCOUNT];
};
class InputManager
{
public:
void OnKeyboardAction(uint32_t key, bool action);
void OnMouseAction(uint32_t key, bool action);
void OnMouseMoveAction(float x, float y);
InputLayout<kMaxKeyboardKeys>& GetKeyboard() { return m_keyboard; }
glm::vec2& GetMousePos() { return m_mousePos; }
glm::vec2& GetMouseDeltaPos() { return m_deltaMousePos; }
void SetRelativeMouseMode(bool relativeMode);
void Frame();
private:
InputLayout<kMaxKeyboardKeys> m_keyboard;
InputLayout<kMaxMouseKeys> m_mouse;
glm::vec2 m_mousePos;
glm::vec2 m_deltaMousePos;
bool m_relativeMouseMode = false;
};
extern InputManager g_inputManager;
EMouseButtons GetMouseButtonsFromSDL(uint8_t button);
#endif // !INPUTMANAGER_H

143
src/engine/log.cpp Normal file
View File

@@ -0,0 +1,143 @@
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <SDL3/SDL.h>
#include "engine/log.h"
static char* g_month[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
static int g_day_in_month[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int build_id;
FILE* g_logFile;
void CalculateBuildNumber()
{
static int start_day = 3;
static int start_month = 10;
static int start_year = 2021;
// Calculating build
int build = 0, mnum = 0, dnum, ynum, mcnt;
char mon[4];
char buf[128];
strcpy(buf, __DATE__);
sscanf(buf, "%s %d %d", mon, &dnum, &ynum);
for (int i = 0; i < 12; i++) {
#ifdef WIN32
if (stricmp(g_month[i], mon) == 0) {
#else
if (strcasecmp(g_month[i], mon) == 0) {
#endif // WIN32
mnum = i;
break;
}
}
build_id = (ynum - start_year) * 365 + dnum - start_day;
for (int i = 0; i < mnum; ++i)
build_id += g_day_in_month[i];
for (int i = 0; i < start_month - 1; ++i)
build_id -= g_day_in_month[i];
}
void Logger::Open()
{
CalculateBuildNumber();
#if 0//def WIN32
char UserName[256];
DWORD dwUserName = 256;
if (!GetUserNameA(UserName, &dwUserName))
strcpy(UserName, "unnamed");
char logfilename[256];
sprintf(logfilename, "engine_%s.log", UserName);
#else
char logfilename[256];
sprintf(logfilename, "engine.log");
#endif
g_logFile = fopen(logfilename, "w");
Msg("'%s' build %d, %s", "engine", build_id, __DATE__);
}
void Logger::Close()
{
if (g_logFile) {
fclose(g_logFile);
g_logFile = nullptr;
}
}
void Logger::Msg(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
MsgArg(fmt, args);
va_end(args);
}
void Logger::MsgArg(const char* fmt, va_list args)
{
char buffer[2048];
int len = vsnprintf(buffer, sizeof(buffer), fmt, args);
assert(len >= 0);
assert(len < 2048);
SDL_assert_always(len >= 0);
SDL_assert_always(len < 2048);
//if (len >= sizeof(buffer))
// len = sizeof(buffer) - 1;
//if (buffer[len] != '\n' && len < sizeof(buffer) - 1)
//{
// buffer[len++] = '\n';
// buffer[len] = '\0';
//}
len = strlen(buffer);
if (buffer[len - 1] != '\n')
{
buffer[len++] = '\n';
buffer[len] = '\0';
}
//time_t t = time(0);
//struct tm* ti = localtime(&t);
//char timestr[256];
//sprintf(timestr, "%s", asctime(ti));
//timestr[strlen(timestr) - 1] = '\0';
//sprintf(buffer2, "[%s] %s", timestr, buffer);
if (g_logFile)
{
fwrite(buffer, sizeof(char), len, g_logFile);
fflush(g_logFile);
}
#ifdef WIN32
//OutputDebugStringA(buffer);
fwrite(buffer, sizeof(char), len, stdout);
#else
fwrite(buffer, sizeof(char), len, stdout);
#endif
}
void Msg(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
Logger::MsgArg(fmt, args);
va_end(args);
}

18
src/engine/log.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef LOG_H
#define LOG_H
#include <stdarg.h>
class Logger
{
public:
static void Open();
static void Close();
static void Msg(const char* fmt, ...);
static void MsgArg(const char* fmt, va_list args);
};
void Msg(const char* fmt, ...);
#endif // !LOG_H

View File

@@ -0,0 +1,13 @@
#include "physics/bullet_private.h"
glm::vec3 btVectorToGlm(const btVector3& vec)
{
return glm::vec3(vec.x(), vec.y(), vec.z());
}
btVector3 glmVectorToBt(const glm::vec3& vec)
{
return btVector3(vec.x, vec.y, vec.z);
}

View File

@@ -0,0 +1,19 @@
#ifndef BULLET_PRIVATE_H
#define BULLET_PRIVATE_H
#include <glm/glm.hpp>
// Bullet includes
#include "btBulletCollisionCommon.h"
#include "btBulletDynamicsCommon.h"
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
#include "BulletDynamics/Character/btKinematicCharacterController.h"
const int kCollisionFilterAllMask = btBroadphaseProxy::DefaultFilter | btBroadphaseProxy::StaticFilter | btBroadphaseProxy::KinematicFilter | btBroadphaseProxy::DebrisFilter;
glm::vec3 btVectorToGlm(const btVector3& vec);
btVector3 glmVectorToBt(const glm::vec3& vec);
#endif

View File

@@ -0,0 +1,40 @@
#include <glm/glm.hpp>
#include "engine/physics/physicsdebugdraw.h"
#include "render/render_shared.h"
#include "render/debugrender.h"
void PhysicsDebugDraw::drawLine(const btVector3& from, const btVector3& to, const btVector3& color)
{
glm::vec3 btFrom = glm::vec3(from.x(), from.y(), from.z());
glm::vec3 btTo = glm::vec3(to.x(), to.y(), to.z());
glm::vec3 btColor = glm::vec3(color.x(), color.y(), color.z());
g_pDebugRender->DrawLine(btFrom, btTo, btColor);
}
void PhysicsDebugDraw::drawContactPoint(const btVector3& PointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color)
{
btVector3 to = PointOnB + normalOnB * 1;//distance;
const btVector3& from = PointOnB;
drawLine(from, to, color);
}
void PhysicsDebugDraw::reportErrorWarning(const char* warningString)
{
}
void PhysicsDebugDraw::draw3dText(const btVector3& location, const char* textString)
{
}
void PhysicsDebugDraw::setDebugMode(int debugMode)
{
m_debugMode = debugMode;
}
int PhysicsDebugDraw::getDebugMode() const
{
return m_debugMode;
}

View File

@@ -0,0 +1,25 @@
#ifndef PHYSICSDEBUGDRAW_H
#define PHYSICSDEBUGDRAW_H
#include "LinearMath/btIDebugDraw.h"
class PhysicsDebugDraw : public btIDebugDraw
{
public:
void Init();
void Shutdown();
// btIDebugDraw inherit
void drawLine(const btVector3& from, const btVector3& to, const btVector3& color) override;
void drawContactPoint(const btVector3& PointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color) override;
void reportErrorWarning(const char* warningString) override;
void draw3dText(const btVector3& location, const char* textString) override;
void setDebugMode(int debugMode) override;
int getDebugMode() const override;
private:
int m_debugMode;
};
#endif

View File

@@ -0,0 +1,180 @@
#include "engine/core.h"
#include "engine/engine.h"
#include "engine/camera.h"
#include "engine/physics/physicsworld.h"
#include "engine/physics/physicsdebugdraw.h"
#include "engine/physics/rigidbody.h"
#include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>
#include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>
#include <BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h>
#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
static void InternalTickCallback(btDynamicsWorld* world, btScalar timeStep)
{
SDL_assert(world);
SDL_assert(world->getWorldUserInfo());
static_cast<PhysicsWorld*>(world->getWorldUserInfo())->InternalTick();
}
PhysicsWorld::PhysicsWorld()
{
m_debugDraw = false;
m_collisionConfiguration = new btDefaultCollisionConfiguration();
m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
m_overlappingPairCache = new btDbvtBroadphase();
m_solver = new btSequentialImpulseConstraintSolver();
m_world = new btDiscreteDynamicsWorld(m_dispatcher, m_overlappingPairCache, m_solver, m_collisionConfiguration);
m_btGhostPairCallback = new btGhostPairCallback();
m_world->getBroadphase()->getOverlappingPairCache()->setInternalGhostPairCallback(m_btGhostPairCallback);
m_world->setInternalTickCallback(InternalTickCallback, this);
m_world->setDebugDrawer(&m_physicsDebugDraw);
m_accumulatedTime = 0.0f;
m_stepTime = (1.0f / 30.0f); // 30 fps
// set the standart gravity
//m_world->setGravity(btVector3(0, 0, 0));
//getWorld()->setGravity(btVector3(0.0, -15.0, 0.0));
}
PhysicsWorld::~PhysicsWorld()
{
m_world->getBroadphase()->getOverlappingPairCache()->setInternalGhostPairCallback(nullptr);
// delete ghost pair callback
delete m_btGhostPairCallback;
// delete dynamics world
delete m_world;
// delete solver
delete m_solver;
// delete broadphase
delete m_overlappingPairCache;
// delete dispatcher
delete m_dispatcher;
// delete collision configuration
delete m_collisionConfiguration;
}
void PhysicsWorld::AddRigidBody(RigidBody* body)
{
SDL_assert(body->GetSDKBody() && "RigidBody is not properly initialized");
m_world->addRigidBody(body->GetSDKBody());
m_rigidbodies.push_back(body);
}
void PhysicsWorld::RemoveRigidBody(RigidBody* body)
{
SDL_assert(body->GetSDKBody() && "RigidBody is not properly initialized");
m_world->removeRigidBody(body->GetSDKBody());
auto it = std::find(m_rigidbodies.begin(), m_rigidbodies.end(), body);
if (it != m_rigidbodies.end())
m_rigidbodies.erase(it);
}
const std::vector<RigidBody*>& PhysicsWorld::GetRigidBodies()
{
return m_rigidbodies;
}
void PhysicsWorld::Step(float delta)
{
// m_world->stepSimulation(delta);
// m_world->stepSimulation(delta, 12, m_stepTime);
#if 1
if (delta < 0.01f)
{
m_accumulatedTime += delta;
if (m_accumulatedTime > m_stepTime)
{
m_world->stepSimulation(m_stepTime);
m_accumulatedTime -= m_stepTime;
}
}
else
{
m_world->stepSimulation(delta);
}
#endif
//m_debugDraw = true;
if (m_debugDraw)
{
//DebugDrawTriggers();
int debugDrawMode = 0;
debugDrawMode |= btIDebugDraw::DBG_DrawAabb;
debugDrawMode |= btIDebugDraw::DBG_DrawWireframe;
// debugDrawMode |= btIDebugDraw::DBG_DrawContactPoints;
m_physicsDebugDraw.setDebugMode(debugDrawMode);
m_world->debugDrawWorld();
}
/*if (m_world)
m_world->stepSimulation(1 / 60.0f);
int debugDrawMode = 0;
debugDrawMode |= btIDebugDraw::DBG_DrawAabb;
debugDrawMode |= btIDebugDraw::DBG_DrawWireframe;
debugDrawMode |= btIDebugDraw::DBG_DrawContactPoints;
m_physicsDebugDraw.setDebugMode(debugDrawMode);
m_world->debugDrawWorld();*/
}
void PhysicsWorld::ToggleDebugDraw()
{
m_debugDraw = !m_debugDraw;
}
void PhysicsWorld::InternalTick()
{
int numManifolds = m_world->getDispatcher()->getNumManifolds();
for (int i = 0; i < numManifolds; i++)
{
btPersistentManifold* contactManifold = m_world->getDispatcher()->getManifoldByIndexInternal(i);
btCollisionObject* obA = const_cast<btCollisionObject*>(contactManifold->getBody0());
btCollisionObject* obB = const_cast<btCollisionObject*>(contactManifold->getBody1());
int numContacts = contactManifold->getNumContacts();
for (int j = 0; j < numContacts; j++)
{
btManifoldPoint& pt = contactManifold->getContactPoint(j);
if (pt.getDistance() < 0.f)
{
const btVector3& ptA = pt.getPositionWorldOnA();
const btVector3& ptB = pt.getPositionWorldOnB();
const btVector3& normalOnB = pt.m_normalWorldOnB;
RigidBody* rbA = static_cast<RigidBody*>(obA->getUserPointer());
RigidBody* rbB = static_cast<RigidBody*>(obB->getUserPointer());
if (!rbA || !rbB)
continue;
SDL_assert(rbA);
SDL_assert(rbB);
/*if (TriggerComponent* trigger = rbA->GetEntity()->GetComponent<TriggerComponent>())
{
trigger->OnCollide(rbA, rbB);
}
else if (TriggerComponent* trigger = rbB->GetEntity()->GetComponent<TriggerComponent>())
{
trigger->OnCollide(rbB, rbA);
}*/
}
}
}
}

View File

@@ -0,0 +1,51 @@
#ifndef PHYSICSWORLD_H
#define PHYSICSWORLD_H
#include "engine/physics/bullet_private.h"
#include "engine/physics/physicsdebugdraw.h"
#include <vector>
class RigidBody;
class PhysicsWorld
{
public:
PhysicsWorld();
~PhysicsWorld();
void AddRigidBody(RigidBody* body);
void RemoveRigidBody(RigidBody* body);
const std::vector<RigidBody*>& GetRigidBodies();
void Step(float delta);
btDynamicsWorld* GetWorld() { return m_world; }
float GetFixedTimeStep() { return m_stepTime; }
void ToggleDebugDraw();
void InternalTick();
private:
btDefaultCollisionConfiguration* m_collisionConfiguration;
btCollisionDispatcher* m_dispatcher;
btBroadphaseInterface* m_overlappingPairCache;
btSequentialImpulseConstraintSolver* m_solver;
btDiscreteDynamicsWorld* m_world;
btGhostPairCallback* m_btGhostPairCallback;
PhysicsDebugDraw m_physicsDebugDraw;
std::vector<RigidBody*> m_rigidbodies;
float m_accumulatedTime;
float m_stepTime;
bool m_debugDraw;
};
#endif // !PHYSICSWORLD_H

View File

@@ -0,0 +1,301 @@
#include "engine/core.h"
#include "engine/log.h"
#include "engine/physics/rigidbody.h"
#include "engine/physics/physicsworld.h"
#include <glm/glm.hpp>
RigidBody::RigidBody()
{
m_rigidBody = nullptr;
m_compoundShape = nullptr;
m_physicsWorld = nullptr;
m_mass = 0.0f;
m_isEnable = false;
m_static = false;
m_isKinematic = false;
m_isTrigger = false;
m_inWorld = false;
m_disableBodyOrientUpdate = false;
}
RigidBody::~RigidBody()
{
DestroyBody();
m_physicsWorld = nullptr;
m_compoundShape = nullptr;
m_rigidBody = nullptr;
}
//
//void RigidBody::LoadXML(tinyxml2::XMLElement& element)
//{
// Component::LoadXML(element);
//
// tinyxml2::XMLElement* massElement = element.FirstChildElement("Mass");
// if (massElement)
// massElement->QueryFloatAttribute("value", &m_mass);
//
// tinyxml2::XMLElement* staticElement = element.FirstChildElement("Static");
// if (staticElement)
// staticElement->QueryBoolAttribute("value", &m_static);
//
// tinyxml2::XMLElement* triggerElement = element.FirstChildElement("Trigger");
// if (triggerElement)
// triggerElement->QueryBoolAttribute("value", &m_isTrigger);
//
// tinyxml2::XMLElement* kinematicElement = element.FirstChildElement("Kinematic");
// if (kinematicElement)
// kinematicElement->QueryBoolAttribute("value", &m_isKinematic);
//
// bool changeFilterUsableHack = false;
// bool bolvanHack = false;
// bool obstacleHack = false;
//
// tinyxml2::XMLElement* filterElement = element.FirstChildElement("Filter");
// if (filterElement)
// {
// const char* filterValue = nullptr;
// filterElement->QueryStringAttribute("value", &filterValue);
// if (filterValue)
// {
// if (strcmp(filterValue, "PhysicsFilter_Usable") == 0)
// {
// changeFilterUsableHack = true;
// }
// if (strcmp(filterValue, "PhysicsFilter_Bolvan") == 0)
// {
// bolvanHack = true;
// }
// if (strcmp(filterValue, "PhysicsFilter_Obstacle") == 0)
// {
// obstacleHack = true;
// }
// }
// }
//
// CreateBody();
// UpdateBodyTranslationDirty();
//
// if (changeFilterUsableHack)
// {
// m_rigidBody->getBroadphaseProxy()->m_collisionFilterGroup = PhysicsFilter_Usable;
// }
// else if (m_isTrigger)
// {
// m_rigidBody->getBroadphaseProxy()->m_collisionFilterGroup = PhysicsFilter_Triggers;
// }
//
// // HACKHACKHACK: bolvan body never sleep
// if (bolvanHack)
// {
// m_rigidBody->setActivationState(DISABLE_DEACTIVATION);
//
// m_rigidBody->getBroadphaseProxy()->m_collisionFilterGroup = PhysicsFilter_NPC;
// m_rigidBody->getBroadphaseProxy()->m_collisionFilterMask = kCollisionFilterAllMask;
// }
//
// if (obstacleHack)
// {
// m_rigidBody->getBroadphaseProxy()->m_collisionFilterGroup = PhysicsFilter_Obstacle;
// m_rigidBody->getBroadphaseProxy()->m_collisionFilterMask = PhysicsFilter_Obstacle | PhysicsFilter_Player;
// m_rigidBody->setCustomDebugColor(btVector3(173 / 255, 216 / 255, 230 / 255));
// }
//}
//
//void RigidBody::SaveXML(tinyxml2::XMLElement& element)
//{
// Component::SaveXML(element);
//
// tinyxml2::XMLElement* enableElement = element.InsertNewChildElement("Enable");
// enableElement->SetAttribute("value", m_isEnable);
//
// tinyxml2::XMLElement* massElement = element.InsertNewChildElement("Mass");
// massElement->SetAttribute("value", m_mass);
//
// tinyxml2::XMLElement* staticElement = element.InsertNewChildElement("Static");
// staticElement->SetAttribute("value", m_static);
//
// tinyxml2::XMLElement* kinematicElement = element.InsertNewChildElement("Kinematic");
// kinematicElement->SetAttribute("value", m_isKinematic);
//
// tinyxml2::XMLElement* triggerElement = element.InsertNewChildElement("Trigger");
// triggerElement->SetAttribute("value", m_isTrigger);
//}
//void RigidBody::UpdateEntityTranslationDirty()
//{
// btTransform trans = m_rigidBody->getWorldTransform();
// GetEntity()->SetPosition(btVectorToGlm(trans.getOrigin()));
//
// // calculate rotation
//
// if (!m_disableBodyOrientUpdate)
// {
// btQuaternion quaternion;
// trans.getBasis().getRotation(quaternion);
// GetEntity()->SetRotation(glm::quat(quaternion.getW(), quaternion.getX(), quaternion.getY(), quaternion.getZ()));
// }
//
// // calculate rotation based on euler angles
// //float roll, pitch, yaw;
// //trans.getRotation().getEulerZYX(yaw, pitch, roll);
// //GetEntity()->SetEulerRotation(glm::vec3(roll, pitch, yaw));
//}
//
//void RigidBody::UpdateBodyTranslationDirty()
//{
// if (!m_rigidBody)
// return;
//
// btTransform trans;
// trans.setIdentity();
//
// // origin
// trans.setOrigin(glmVectorToBt(GetEntity()->GetPosition()));
//
// // rotation
// glm::quat entityRot = GetEntity()->GetRotation();
// btQuaternion bodyRot;
// bodyRot.setX(entityRot.x);
// bodyRot.setY(entityRot.y);
// bodyRot.setZ(entityRot.z);
// bodyRot.setW(entityRot.w);
// trans.setRotation(bodyRot);
//
// // set transformation to body
// m_rigidBody->setWorldTransform(trans);
//}
#if 0
void RigidBody::getWorldTransform(btTransform& worldTrans) const
{
//throw std::logic_error("The method or operation is not implemented.");
}
void RigidBody::setWorldTransform(const btTransform& worldTrans)
{
//throw std::logic_error("The method or operation is not implemented.");
UpdateBodyTranslationDirty();
}
#endif
void RigidBody::CreateBody()
{
SDL_assert(m_physicsWorld);
if (m_rigidBody)
return;
m_compoundShape = new btCompoundShape();
btVector3 localInertia(0.0f, 0.0f, 0.0f);
btRigidBody::btRigidBodyConstructionInfo rigidBodyCInfo(m_mass, nullptr, m_compoundShape, localInertia);
m_rigidBody = new btRigidBody(rigidBodyCInfo);
m_rigidBody->setUserPointer(this);
UpdateBodyProperties();
m_physicsWorld->AddRigidBody(this);
m_inWorld = m_rigidBody->isInWorld();
}
void RigidBody::DestroyBody()
{
if (m_physicsWorld)
{
m_physicsWorld->RemoveRigidBody(this);
}
else
{
Logger::Msg("RigidBody::DestroyBody: Can't remove body. Physics world is already destroyed.");
//Logger::Msg("Entity=0x%p", GetEntity());
}
// remove user pointer from this component
m_rigidBody->setUserPointer(nullptr);
delete m_rigidBody;
m_rigidBody = nullptr;
delete m_compoundShape;
m_compoundShape = nullptr;
}
void RigidBody::UpdateBodyProperties()
{
int bodyCollisionFlags = m_rigidBody->getCollisionFlags();
if (m_static)
bodyCollisionFlags |= btRigidBody::CF_STATIC_OBJECT;
else
bodyCollisionFlags &= ~btRigidBody::CF_STATIC_OBJECT;
if (m_isKinematic)
bodyCollisionFlags |= btRigidBody::CF_KINEMATIC_OBJECT;
else
bodyCollisionFlags &= ~btRigidBody::CF_KINEMATIC_OBJECT;
if (m_isTrigger)
bodyCollisionFlags |= btRigidBody::CF_NO_CONTACT_RESPONSE;
else
bodyCollisionFlags &= ~btRigidBody::CF_NO_CONTACT_RESPONSE;
m_rigidBody->setCollisionFlags(bodyCollisionFlags);
// trigger custom color
if (m_isTrigger)
m_rigidBody->setCustomDebugColor(btVector3(1.0f, 0.5f, 0.0f));
// HACKHACKHACK: kinematic body never sleep
if (m_isKinematic)
m_rigidBody->setActivationState(DISABLE_DEACTIVATION);
}
void RigidBody::DisableBodyOrientUpdate()
{
m_disableBodyOrientUpdate = true;
}
void RigidBody::DisableBody()
{
m_rigidBody->setActivationState(DISABLE_SIMULATION);
}
void RigidBody::DisableCollide()
{
m_rigidBody->setCollisionFlags(m_rigidBody->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE);
}
void RigidBody::EnableCollide()
{
m_rigidBody->setCollisionFlags(m_rigidBody->getCollisionFlags() & ~btCollisionObject::CF_NO_CONTACT_RESPONSE);
}
void RigidBody::AttachShape(ShapeComponent* shape)
{
//shape->InitializeShape();
}
void RigidBody::DettachShape(ShapeComponent* shape)
{
//m_compoundShape->removeChildShape(shape->getSdkShape());
}
void RigidBody::ApplyImpulse(const glm::vec3& impulse)
{
m_rigidBody->applyCentralImpulse(glmVectorToBt(impulse));
}
void RigidBody::SetLinearVelocity(const glm::vec3& velocity)
{
m_rigidBody->setLinearVelocity(glmVectorToBt(velocity));
}
glm::vec3 RigidBody::GetLinearVelocity()
{
return btVectorToGlm(m_rigidBody->getLinearVelocity());
}

View File

@@ -0,0 +1,84 @@
#ifndef RIGIDBODYCOMPONENT_H
#define RIGIDBODYCOMPONENT_H
#include "engine/physics/bullet_private.h"
class PhysicsManager;
class PhysicsWorld;
class ShapeComponent;
enum PhysicsFilter
{
PhysicsFilter_None = 1 << 6,
PhysicsFilter_Player = 1 << 7,
PhysicsFilter_Triggers = 1 << 8,
PhysicsFilter_Usable = 1 << 9,
PhysicsFilter_NPC = 1 << 9,
PhysicsFilter_Obstacle = 1 << 9,
};
const int kPhysicsFilter_AllAux = PhysicsFilter_Player | PhysicsFilter_Triggers | PhysicsFilter_Usable;
//! Rigid body
class RigidBody
{
public:
RigidBody();
~RigidBody();
void UpdateEntityTranslationDirty();
void UpdateBodyTranslationDirty();
// Setter's and getter's
const float GetMass() { return m_mass; }
void SetMass(float value) { m_mass = value; }
const bool IsStatic() { return m_static; }
btRigidBody* GetSDKBody() { return m_rigidBody; }
btCompoundShape* GetCompoundShape() { return m_compoundShape; }
PhysicsWorld* GetPhysicsWorld() { return m_physicsWorld; }
//void getWorldTransform(btTransform& worldTrans) const override;
//void setWorldTransform(const btTransform& worldTrans) override;
void DisableBodyOrientUpdate();
void DisableBody();
void DisableCollide();
void EnableCollide();
void AttachShape(ShapeComponent* shape);
void DettachShape(ShapeComponent* shape);
void ApplyImpulse(const glm::vec3& impulse);
void SetLinearVelocity(const glm::vec3& velocity);
glm::vec3 GetLinearVelocity();
private:
void CreateBody();
void DestroyBody();
void UpdateBodyProperties();
private:
btRigidBody* m_rigidBody;
btCompoundShape* m_compoundShape;
PhysicsWorld* m_physicsWorld;
// Body mass
float m_mass;
bool m_isEnable;
bool m_static;
bool m_isKinematic;
bool m_isTrigger;
bool m_inWorld;
// engine properties
bool m_disableBodyOrientUpdate;
};
#endif // !RIGIDBODYCOMPONENT_H

176
src/engine/world.cpp Normal file
View File

@@ -0,0 +1,176 @@
#include <stdlib.h>
#include <string.h>
#include "core.h"
#include "log.h"
#include "ientity.h"
#include "entitymanager.h"
#include "world.h"
#include "camera.h"
// World
World::World()
{
m_isUpdating = false;
}
World::~World()
{
DestroyWorld();
}
void World::DestroyWorld()
{
for (int i = 0; i < m_entities.size(); ++i)
{
delete m_entities[i];
m_entities[i] = 0;
}
m_entities.clear();
}
void World::Update(float dt)
{
// Run destroyer
UpdateDestroyList();
m_isUpdating = true;
// Update entities
for (int i = 0; i < m_entities.size(); i++)
{
m_entities[i]->Update(dt);
}
for (int i = 0; i < m_entities.size(); i++)
{
m_entities[i]->UpdateTransform();
}
m_isUpdating = false;
}
void World::Render()
{
Camera* camera = g_cameraManager.GetActiveCamera();
if (!camera)
return;
Frustum& frustum = camera->GetFrustum();
// Render entities
for (int i = 0; i < m_entities.size(); i++)
{
if (frustum.CullBoundingBox(m_entities[i]->GetBoundingBox()))
continue;
m_entities[i]->Render();
}
}
void World::UpdateDestroyList()
{
// Update deleting list
if (!m_entitiesToDelete.empty())
{
for (int i = 0; i < m_entitiesToDelete.size(); i++)
{
DeleteEntity(FindEntityId(m_entitiesToDelete[i]));
}
m_entitiesToDelete.clear();
}
}
void World::AddEntity(IEntityBase* pEntity)
{
m_entities.push_back(pEntity);
}
IEntityBase* World::CreateEntity(const char* classname)
{
IEntityBase* entity = nullptr;
if (classname != nullptr)
{
// Get entity factory.
entity = g_entityManager->CreateEntity(classname);
}
if (entity == nullptr)
{
Core::Warning("World::CreateEntity: Can't create entity with unknown %s classname.", classname);
return nullptr;
}
m_entities.push_back(entity);
//entity->Spawn();
return entity;
}
void World::DeleteEntity(int index)
{
if (m_isUpdating)
{
Msg("World::DeleteEntity: illegal call in world update!");
SDL_assert_always(0);
return;
}
if (0 > index || index >= GetNumEntities())
{
Msg("World::DeleteEntity: invalid entity index %d", index);
SDL_assert_always(0);
return;
}
delete m_entities[index];
m_entities.erase(m_entities.begin() + index);
}
int World::GetNumEntities()
{
return (int)m_entities.size();
}
IEntityBase* World::GetEntity(int index)
{
return m_entities[index];
}
int World::FindEntityId(IEntityBase* pEntity)
{
for (int i = 0; i < GetNumEntities(); i++)
{
if (m_entities[i] == pEntity)
return i;
}
return -1;
}
IEntityBase* World::FindEntityByClassName(const char* pClassname)
{
for (int i = 0; i < GetNumEntities(); i++)
{
if (strcmp(m_entities[i]->GetClassname(), pClassname) == 0)
return m_entities[i];
}
return nullptr;
}
void World::MarkEntityToDelete(IEntityBase* pEntity)
{
m_entitiesToDelete.push_back(pEntity);
}
void World::MarkEntityToDeleteId(int index)
{
MarkEntityToDelete(GetEntity(index));
}
World* g_world = nullptr;

44
src/engine/world.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef WORLD_H
#define WORLD_H
#include <vector>
class IEntityBase;
class World
{
public:
World();
~World();
void DestroyWorld();
void Update(float dt);
void Render();
void AddEntity(IEntityBase* pEntity);
IEntityBase* CreateEntity(const char* pClassname);
void DeleteEntity(int index);
int GetNumEntities();
IEntityBase* GetEntity(int index);
int FindEntityId(IEntityBase* pEntity);
IEntityBase* FindEntityByClassName(const char* pClassname);
void MarkEntityToDelete(IEntityBase* pEntity);
void MarkEntityToDeleteId(int index);
private:
void UpdateDestroyList();
private:
std::vector<IEntityBase*> m_entities;
std::vector<IEntityBase*> m_entitiesToDelete;
bool m_isUpdating;
};
extern World* g_world;
#endif // !WORLD_H

172
src/game/game.cpp Normal file
View File

@@ -0,0 +1,172 @@
#include "ifilesystem.h"
#include "core.h"
#include "log.h"
#include "game.h"
#include "game_lua_help.h"
#include "ientity.h"
#include "entitymanager.h"
#include "world.h"
#include <pugixml.hpp>
static Game s_game;
Game* g_game = &s_game;
void consoleMsg(const char* msg)
{
Logger::Msg("%s", msg);
}
void engineError(const char* msg)
{
Core::Error("%s", msg);
}
void engineWarning(const char* msg)
{
Core::Warning("%s", msg);
}
LuaPlus::LuaObject engineCreateEntity(const char* classname)
{
LuaPlus::LuaObject entityObject;
entityObject.AssignNewTable(GetLuaState());
//GameObject* entity = (GameObject*)g_entityManager->createEntity(classname);
//entity->init_from_lua(entityObject);
//entity->init();
return entityObject;
}
void engineAddEntityToWorld(LuaPlus::LuaObject& object)
{
LuaPlus::LuaObject cppclass = object["__cpp_entity"];
// todo
}
void registerEngine()
{
using namespace LuaPlus;
// register engine functions
LuaObject engineTable = GetLuaState().GetGlobals().CreateTable("engine");
engineTable.RegisterDirect("error", &engineError);
engineTable.RegisterDirect("warning", &engineWarning);
engineTable.RegisterDirect("create_entity", &engineCreateEntity);
engineTable.RegisterDirect("add_entity_to_world", &engineAddEntityToWorld);
LuaObject consoleTable = GetLuaState().GetGlobals().CreateTable("console");
consoleTable.RegisterDirect("print", &consoleMsg);
}
void registerClasses()
{
using namespace LuaPlus;
// base thing
GetLuaState().GetGlobals().RegisterDirect("load_script", &luaLoadScript);
registerEngine();
}
void initializeEntityPrototypesFromLua()
{
using namespace LuaPlus;
LuaObject entitiesTable = GetLuaState().GetGlobal("g_entity_table");
for (LuaTableIterator it(entitiesTable); it; it.Next()) {
LuaObject entityTable = it.GetValue();
assert(entityTable.IsTable());
for (LuaTableIterator it2(entityTable); it2; it2.Next()) {
Logger::Msg("%s", it2.GetValue().ToString());
}
}
}
void initializeEntitiesTable()
{
using namespace LuaPlus;
LuaObject entitiesTable = GetLuaState().GetGlobal("g_entity_table");
if (entitiesTable.IsNil()) {
Core::Error("initializeEntitiesTable: failed initialize the entity table! g_entity_table is nil");
}
if (!entitiesTable.IsTable()) {
Core::Error("initializeEntitiesTable: failed initialize the entity table! g_entity_table is not a table");
}
initializeEntityPrototypesFromLua();
}
void initializeLua()
{
using namespace LuaPlus;
// register base classes
registerClasses();
// load game base script
luaLoadScript("game_init.lua");
}
void Game::Init()
{
initializeLua();
initializeEntitiesTable();
}
void Game::InitForNewMap(const char* mapname)
{
LoadLevelXML(mapname);
}
void Game::LoadLevelXML(const char* mapname)
{
// Load XML file of the level
char buffer[kMaxPathLength];
snprintf(buffer, kMaxPathLength, "data/levels/%s/%s.xml", mapname, mapname);
FileHandle_t file = GetFileSystem()->OpenFile(buffer, "rb");
SDL_assert_always(file != kInvalidFileHandleValue);
size_t length = GetFileSystem()->GetFileLength(file);
char* filedata = new char[length + 1];
GetFileSystem()->ReadFile(file, filedata, length);
filedata[length] = '\0';
GetFileSystem()->CloseFile(file);
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_buffer(filedata, length);
delete[] filedata;
if (!result) {
Core::Error("Game::LoadLevelXML: Error while reading level description file '%s'\nError: %s:%i",
buffer, result.description(), result.offset);
}
for (pugi::xml_node entity : doc.child("Entities").children("Entity")) {
pugi::xml_attribute entityname = entity.attribute("name");
pugi::xml_attribute classname = entity.attribute("classname");
if (classname.empty()) {
if (!entityname.empty()) {
Core::Error(" Game::LoadLevelXML: Classname is not specified for entity '%s'", entityname.as_string());
}
else {
Core::Error(" Game::LoadLevelXML: Classname is not specified for entity");
}
}
IEntityBase* entity = g_entityManager->CreateEntity(classname.as_string());
g_world->AddEntity(entity);
}
}
void Game::Shutdown()
{
}

18
src/game/game.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef GAME_H
#define GAME_H
class Game
{
public:
void Init();
void InitForNewMap(const char* mapname);
void LoadLevelXML(const char* mapname);
void Shutdown();
};
extern Game* g_game;
#endif

View File

@@ -0,0 +1,91 @@
#include <string>
#include "core.h"
#include "game_lua_help.h"
class LuaStateWrapper
{
public:
LuaStateWrapper()
{
m_luaState = LuaPlus::LuaState::Create();
m_luaState->OpenLibs();
}
~LuaStateWrapper()
{
LuaPlus::LuaState::Destroy(m_luaState);
}
LuaPlus::LuaState& operator()()
{
return *m_luaState;
}
private:
LuaPlus::LuaState* m_luaState;
};
LuaPlus::LuaState& GetLuaState()
{
static LuaStateWrapper instance;
return instance();
}
void luaError(int errorcode)
{
LuaPlus::LuaState* state = &GetLuaState();
LuaPlus::LuaStackObject stackObj(state, -1);
const char* errorStr = stackObj.GetString();
if (errorStr) {
GetLuaState().SetTop(0);
}
else {
errorStr = "Unknown lua error";
}
Core::Error(errorStr);
}
void luaError(int errorcode, const char* filename)
{
LuaPlus::LuaState* state = &GetLuaState();
LuaPlus::LuaStackObject stackObj(state, -1);
std::string errorStr = stackObj.GetString();
if (!errorStr.empty()) {
GetLuaState().SetTop(0);
}
else {
errorStr = "Unknown lua error";
}
Core::Error(errorStr.c_str());
}
void luaLoadScript(const char* filename)
{
char buffer[260];
snprintf(buffer, sizeof(buffer), "data/scripts/%s", filename);
int result = 0;
result = GetLuaState().DoFile(buffer);
if (result != 0) {
luaError(result, buffer);
}
}
void luaDoString(const char* str)
{
int result = 0;
result = GetLuaState().DoString(str);
if (result != 0) {
luaError(result);
}
}
glm::mat4 getMatrixFromLua(LuaPlus::LuaObject& matrix)
{
glm::mat4 return_matrix = glm::identity<glm::mat4>();
return return_matrix;
}

17
src/game/game_lua_help.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef GAME_LUA_HELP_H
#define GAME_LUA_HELP_H
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <LuaPlus.h>
LuaPlus::LuaState& GetLuaState();
void luaLoadScript(const char* filename);
void luaDoString(const char* str);
glm::mat4 getMatrixFromLua(LuaPlus::LuaObject& matrix);
#endif

122
src/game/game_object.cpp Normal file
View File

@@ -0,0 +1,122 @@
#include "inputmanager.h"
#include "game_object.h"
#include <SDL3/SDL.h>
REGISTER_ENTITY(Entity);
Entity::Entity() :
m_model(nullptr)
{
}
Entity::~Entity()
{
}
void Entity::Render()
{
if (m_model)
m_model->Draw(GetWorldTransform());
}
void Entity::LoadModel(const char* filename)
{
m_model = g_pModelSystem->LoadModel(filename);
}
REGISTER_ENTITY(TempPlayer);
TempPlayer::TempPlayer()
{
}
TempPlayer::~TempPlayer()
{
}
void TempPlayer::Update(float dt)
{
g_cameraManager.SetActiveCamera(&m_camera);
UpdateCameraLook();
UpdateCameraMovement(dt);
}
void TempPlayer::UpdateCameraMovement(float dt)
{
// calculate player movement
float speed = 19.0f * dt;
uint32_t movementDir = GenMovementDir();
if (movementDir & EMovementDir_Forward)
m_position += speed * m_camera.GetFront();
if (movementDir & EMovementDir_Backward)
m_position -= speed * m_camera.GetFront();
if (movementDir & EMovementDir_Left)
m_position -= glm::normalize(glm::cross(m_camera.GetFront(), m_camera.GetUp())) * speed;
if (movementDir & EMovementDir_Right)
m_position += glm::normalize(glm::cross(m_camera.GetFront(), m_camera.GetUp())) * speed;
// set position back to camera for calculation view matrix
m_camera.SetPosition(m_position);
}
void TempPlayer::UpdateCameraLook()
{
g_inputManager.SetRelativeMouseMode(true);
glm::ivec2 mousePos = g_inputManager.GetMousePos();
// calculate yaw and pitch
static float yaw = 0.0f, pitch = 0.0f;
int deltaX = mousePos.x;
int deltaY = mousePos.y;
float sensitivity = 0.15f;
yaw += deltaX * sensitivity;
pitch -= deltaY * sensitivity;
if (pitch > 89.0f) pitch = 89.0f;
if (pitch < -89.0f) pitch = -89.0f;
m_camera.SetYawPitch(yaw, pitch);
}
uint32_t TempPlayer::GenMovementDir()
{
uint32_t movementDir = EMovementDir_None;
if (g_inputManager.GetKeyboard().IsKeyDown(SDLK_W)) {
movementDir |= EMovementDir_Forward;
}
if (g_inputManager.GetKeyboard().IsKeyDown(SDLK_S)) {
movementDir |= EMovementDir_Backward;
}
if (g_inputManager.GetKeyboard().IsKeyDown(SDLK_A)) {
movementDir |= EMovementDir_Left;
}
if (g_inputManager.GetKeyboard().IsKeyDown(SDLK_D)) {
movementDir |= EMovementDir_Right;
}
return movementDir;
}
Weapon::Weapon()
{
}
Weapon::~Weapon()
{
}
void Weapon::Update(float dt)
{
}

60
src/game/game_object.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef GAME_OBJECT_H
#define GAME_OBJECT_H
#include "engine/ientity.h"
#include "engine/camera.h"
#include "render/modelsystem.h"
enum EMovmentDir
{
EMovementDir_None = 1 << 0,
EMovementDir_Forward = 1 << 1,
EMovementDir_Backward = 1 << 2,
EMovementDir_Left = 1 << 3,
EMovementDir_Right = 1 << 4
};
class Entity : public IEntityBase
{
public:
Entity();
virtual ~Entity();
virtual void Render();
virtual void LoadModel(const char* filename);
protected:
Model* m_model;
};
class TempPlayer : public Entity
{
public:
TempPlayer();
~TempPlayer();
virtual void Update(float dt);
void UpdateCameraMovement(float dt);
void UpdateCameraLook();
private:
uint32_t GenMovementDir();
private:
Camera m_camera;
};
class Weapon : public Entity
{
public:
Weapon();
~Weapon();
virtual void Update(float dt);
};
#endif // !GAME_OBJECT_H

120
src/render/debugrender.cpp Normal file
View File

@@ -0,0 +1,120 @@
#include "debugrender.h"
#include "render_shared.h"
#include "render.h"
#include "renderdevice.h"
#include "gpu_buffer.h"
#include "shadersystem.h"
#include "glad/glad.h"
struct DebugVertex
{
glm::vec3 position;
glm::vec3 color;
};
static InputLayoutDesc_t g_debugRenderLayout[] =
{
{ VERTEXATTR_VEC3, SHADERSEMANTIC_POSITION },
{ VERTEXATTR_VEC3, SHADERSEMANTIC_COLOR }
};
bool g_drawDebug = true;
DebugRender* g_pDebugRender;
DebugRender::DebugRender()
{
m_verticesBuffer = nullptr;
m_shader = nullptr;
}
DebugRender::~DebugRender()
{
m_verticesBuffer = nullptr;
m_shader = nullptr;
}
void DebugRender::Initialize()
{
//float points[12];
//m_verticesBuffer = g_pRenderDevice->CreateVertexBuffer(points, sizeof(points), true);
m_verticesBuffer = g_pRenderDevice->CreateVertexBuffer(NULL, kMaxDebugVBSize, true);
m_shader = g_pShaderSystem->CreateShader("debug_draw",
"data/shaders/debug_draw.vs",
"data/shaders/debug_draw.ps",
g_debugRenderLayout,
sizeof(g_debugRenderLayout) / sizeof(g_debugRenderLayout[0]));
}
void DebugRender::Shutdown()
{
if (m_verticesBuffer) {
delete m_verticesBuffer;
m_verticesBuffer = nullptr;
}
}
void DebugRender::DrawLine(const glm::vec3& from, const glm::vec3& to, const glm::vec3& color)
{
if (!g_drawDebug)
return;
Line line;
line.from = from;
line.color0 = color;
line.to = to;
line.color1 = color;
m_lines.push_back(line);
}
void DebugRender::RenderFrame()
{
if (!g_drawDebug)
return;
// draw lines
DrawLinesInternal();
// and clear them
m_lines.clear();
}
void DebugRender::DrawAxis(const glm::vec3& vec)
{
const float length = 0.2f;
DrawLine(vec, glm::vec3(vec.x + length, vec.y, vec.z), glm::vec3(1.0f, 0.0, 0.0f));
DrawLine(vec, glm::vec3(vec.x, vec.y + length, vec.z), glm::vec3(0.0f, 1.0f, 0.0f));
DrawLine(vec, glm::vec3(vec.x, vec.y, vec.z + length), glm::vec3(0.0f, 0.0f, 1.0f));
}
void DebugRender::DrawLinesInternal()
{
if (m_lines.empty())
return;
g_pRenderDevice->SetDepthTest(true);
g_pRenderDevice->SetDepthWrite(true);
g_pRenderDevice->SetVerticesBuffer(m_verticesBuffer);
m_verticesBuffer->UpdateBuffer(m_lines.data(), m_lines.size() * sizeof(Line));
// Bind our shader
g_pShaderSystem->SetShader(m_shader);
// #TODO: Fix stupid bug, when we get very far from wireframe and lines can start cliping
glm::mat4 proj = g_pRender->GetProjectionMatrix();
glm::mat4 view = g_pRender->GetViewMatrix();
proj[2][3] -= 0.0001f;
glm::mat4 mv = glm::mat4(1.0f);
mv = proj * view;
g_pShaderSystem->SetUniformMatrix(m_shader, UNIFORM_MVP_MATRIX, &mv[0]);
// draw stuff
g_pRenderDevice->DrawArrays(PT_LINES, 0, m_lines.size() * 2);
}

52
src/render/debugrender.h Normal file
View File

@@ -0,0 +1,52 @@
#ifndef DEBUGRENDER_H
#define DEBUGRENDER_H
#include <vector>
#include "render_shared.h"
const int kMaxDebugVBSize = 1024 * 1024 * 2;
class GPUBuffer;
class Shader;
class DebugRender
{
public:
DebugRender();
~DebugRender();
void Initialize();
void Shutdown();
void DrawAxis(const glm::vec3& vec);
void DrawLine(const glm::vec3& from, const glm::vec3& to, const glm::vec3& color);
void RenderFrame();
private:
void DrawLinesInternal();
private:
// Primitives
struct Line
{
glm::vec3 from;
glm::vec3 color0;
glm::vec3 to;
glm::vec3 color1;
};
std::vector<Line> m_lines;
private:
GPUBuffer* m_verticesBuffer;
Shader* m_shader;
};
extern DebugRender* g_pDebugRender;
extern bool g_drawDebug;
#endif // !DEBUGRENDER_H

215
src/render/gl_shared.cpp Normal file
View File

@@ -0,0 +1,215 @@
#include "gl_shared.h"
#include "log.h"
const char *GL_ErrorString( int err )
{
switch( err )
{
#ifdef GL_STACK_OVERFLOW
case GL_STACK_OVERFLOW:
return "GL_STACK_OVERFLOW";
#endif // GL_STACK_OVERFLOW
#ifdef GL_STACK_UNDERFLOW
case GL_STACK_UNDERFLOW:
return "GL_STACK_UNDERFLOW";
#endif // GL_STACK_UNDERFLOW
case GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
case GL_INVALID_VALUE:
return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION:
return "GL_INVALID_OPERATION";
case GL_OUT_OF_MEMORY:
return "GL_OUT_OF_MEMORY";
default:
return "UNKNOWN ERROR";
}
}
void GL_CheckError()
{
GLenum err;
if ( (err = glGetError()) != GL_NO_ERROR )
Msg( "OpenGL Error: %s", GL_ErrorString( err ) );// Msg("OpenGL Error: %s [%s]\n", GL_ErrorString(err), GL_TargetToString(tex->target));
}
void GL_CheckErrorEx( const char* filename, int line )
{
GLenum err;
if ( (err = glGetError()) != GL_NO_ERROR )
Msg( "OpenGL Error: %s at %s:%i", GL_ErrorString( err ), filename, line );// Msg("OpenGL Error: %s [%s]\n", GL_ErrorString(err), GL_TargetToString(tex->target));
}
void GL_CheckErrorFunction(const char* expression, const char* filename, int line)
{
GLenum err;
if ( (err = glGetError()) != GL_NO_ERROR )
Msg( "OpenGL Error: %s (%s) at %s:%i", expression, GL_ErrorString( err ), filename, line );// Msg("OpenGL Error: %s [%s]\n", GL_ErrorString(err), GL_TargetToString(tex->target));
}
// Conversion functions
#include "render_shared.h"
uint32_t GetGLInternalPF(PixelFormat pf)
{
switch (pf)
{
case PF_UNKNOWN:
return 0;
case PF_R8G8B8:
case PF_R8G8B8F:
return GL_RGB;
case PF_R8G8B8A8:
case PF_R8G8B8A8F:
return GL_RGBA;
}
return 0;
}
uint32_t GetGLTypePF(PixelFormat pf)
{
switch (pf)
{
case PF_UNKNOWN:
return 0;
case PF_R8G8B8:
case PF_R8G8B8A8:
return GL_UNSIGNED_BYTE;
case PF_R8G8B8F:
case PF_R8G8B8A8F:
return GL_FLOAT;
}
return 0;
}
uint32_t GetGLBlendFactor(BlendFactor factor)
{
switch (factor)
{
case BF_ZERO:
return GL_ZERO;
case BF_ONE:
return GL_ONE;
case BF_SRC_COLOR:
return GL_SRC_COLOR;
case BF_ONE_MINUS_SRC_COLOR:
return GL_ONE_MINUS_SRC_COLOR;
case BF_DST_COLOR:
return GL_DST_COLOR;
case BF_ONE_MINUS_DST_COLOR:
return GL_ONE_MINUS_DST_COLOR;
case BF_SRC_ALPHA:
return GL_SRC_ALPHA;
case BF_ONE_MINUS_SRC_ALPHA:
return GL_ONE_MINUS_SRC_ALPHA;
case BF_DST_ALPHA:
return GL_DST_ALPHA;
case BF_ONE_MINUS_DST_ALPHA:
return GL_ONE_MINUS_DST_ALPHA;
case BF_CONSTANT_COLOR:
return GL_CONSTANT_COLOR;
case BF_ONE_MINUS_CONSTANT_COLOR:
return GL_ONE_MINUS_CONSTANT_COLOR;
case BF_CONSTANT_ALPHA:
return GL_CONSTANT_ALPHA;
case BF_ONE_MINUS_CONSTANT_ALPHA:
return GL_ONE_MINUS_CONSTANT_ALPHA;
default:
break;
}
return 0;
}
uint32_t GetGLEquation(BlendEquation equation)
{
switch (equation)
{
case BE_FUNC_ADD:
return GL_FUNC_ADD;
case BE_FUNC_SUBTRACT:
return GL_FUNC_SUBTRACT;
case BE_FUNC_REVERSE_SUBTRACT:
return GL_FUNC_REVERSE_SUBTRACT;
case BE_MIN:
return GL_MIN;
case BE_MAX:
return GL_MAX;
default:
break;
}
return 0;
}
uint32_t GetGLDepthFunc(DepthFunc func)
{
switch (func)
{
case DF_NEVER:
return GL_NEVER;
case DF_LESS:
return GL_LESS;
case DF_EQUAL:
return GL_EQUAL;
case DF_LEQUAL:
return GL_LEQUAL;
case DF_GREATER:
return GL_GREATER;
case DF_NOTEQUAL:
return GL_NOTEQUAL;
case DF_GEQUAL:
return GL_GEQUAL;
case DF_ALWAYS:
return GL_ALWAYS;
default:
break;
}
SDL_assert(0 && "Should be never here");
return 0;
}
int getGlWrap(TextureWrap wrap)
{
int param = 0;
if (wrap == TextureWrap_Repeat)
param = GL_REPEAT;
else if (wrap == TextureWrap_MirroredRepeat)
param = GL_MIRRORED_REPEAT;
else if (wrap == TextureWrap_ClampToEdge)
param = GL_CLAMP_TO_EDGE;
else if (wrap == TextureWrap_ClampToBorder)
param = GL_CLAMP_TO_BORDER;
return param;
}
int getGlTexFilter(TextureFilter filter)
{
int param = 0;
if (filter == TextureFilter_Linear)
param = GL_LINEAR;
else if (filter == TextureFilter_Nearest)
param = GL_NEAREST;
else if (filter == TextureFilter_LinearMipmapLinear)
param = GL_LINEAR_MIPMAP_LINEAR;
else if (filter == TextureFilter_LinearMipmapNearest)
param = GL_LINEAR_MIPMAP_NEAREST;
else if (filter == TextureFilter_NearestMipmapLinear)
param = GL_NEAREST_MIPMAP_LINEAR;
else if (filter == TextureFilter_NearestMipmapNearest)
param = GL_NEAREST_MIPMAP_NEAREST;
return param;
}

18
src/render/gl_shared.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef GL_SHARED_H
#define GL_SHARED_H
#include <SDL3/SDL.h>
#include <glad/glad.h>
void GL_CheckError();
void GL_CheckErrorEx(const char* filename, int line);
void GL_CheckErrorFunction(const char* expression, const char* filename, int line);
#define GL_CHECK_ERROR() \
GL_CheckErrorEx(__FILE__, __LINE__)
#define GL_CHECK_FUNC_ERROR(expr) \
expr; \
GL_CheckErrorFunction(#expr, __FILE__, __LINE__)
#endif // !GL_SHARED_H

103
src/render/gpu_buffer.cpp Normal file
View File

@@ -0,0 +1,103 @@
#include "log.h"
#include "gl_shared.h"
#include "render_shared.h"
#include "gpu_buffer.h"
GLenum BufferTypeToTarget(BufferType type)
{
switch (type)
{
case BT_VERTEX:
return GL_ARRAY_BUFFER;
case BT_INDEX:
return GL_ELEMENT_ARRAY_BUFFER;
default:
break;
};
SDL_assert(0 && "Unknowed buffer type");
return 0;
}
GPUBuffer::GPUBuffer(BufferType type, void* data, size_t size, bool isStream /*= false*/)
{
m_type = type;
m_target = BufferTypeToTarget(m_type);
m_isStream = isStream;
m_size = size;
glGenBuffers(1, &m_buffer);
glBindBuffer(m_target, m_buffer);
glBufferData(m_target, size, data, isStream ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
GL_CHECK_ERROR();
glBindBuffer(m_target, 0);
}
GPUBuffer::~GPUBuffer()
{
glDeleteBuffers(1, &m_buffer);
}
void GPUBuffer::Bind()
{
glBindBuffer(m_target, m_buffer);
}
void* GPUBuffer::MapBuffer(BufferAccess access)
{
GLenum accessGl = 0;
switch (access)
{
case BA_READ_ONLY:
accessGl = GL_READ_ONLY;
break;
case BA_WRITE_ONLY:
accessGl = GL_WRITE_ONLY;
break;
case BA_READ_WRITE:
accessGl = GL_READ_WRITE;
break;
}
Bind();
void* ptr = glMapBuffer(m_target, accessGl);
GL_CHECK_ERROR();
return ptr;
}
void GPUBuffer::UnmapBuffer()
{
Bind();
glUnmapBuffer(m_target);
}
void GPUBuffer::UpdateBuffer(void* data, size_t size)
{
SDL_assert(m_isStream && "Trying to update static buffer.");
// Clear GL errors
glGetError();
// Bind buffer
Bind();
glBufferData(m_target, size, data, GL_DYNAMIC_DRAW);
GL_CHECK_ERROR();
GLenum error = glGetError();
if (error == GL_OUT_OF_MEMORY)
{
Msg("GPUBuffer::UpdateData: Out of memory. Couldn't allocate buffer with %u size", (unsigned)size);
return;
}
m_size = size;
}

32
src/render/gpu_buffer.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef GPU_BUFFER_H
#define GPU_BUFFER_H
#include "render_shared.h"
class RenderDevice;
class GPUBuffer
{
friend class RenderDevice;
public:
~GPUBuffer();
void Bind();
void* MapBuffer(BufferAccess access);
void UnmapBuffer();
void UpdateBuffer(void* data, size_t size);
private:
GPUBuffer(BufferType type, void* data, size_t size, bool isStream = false);
BufferType m_type;
uint32_t m_target;
uint32_t m_buffer;
size_t m_size;
bool m_isStream;
};
#endif // !GPU_BUFFER_H

404
src/render/modelsystem.cpp Normal file
View File

@@ -0,0 +1,404 @@
#include <assert.h>
#include <vector>
#include <string>
#include "ifilesystem.h"
#include "log.h"
#include "render.h"
#include "gpu_buffer.h"
#include "shader.h"
#include "shadersystem.h"
#include "renderdevice.h"
#include "modelsystem.h"
#include "texturesmanager.h"
ModelSystem* g_pModelSystem = nullptr;
static InputLayoutDesc_t g_staticVertexLayout[] = {
{ VERTEXATTR_VEC3, SHADERSEMANTIC_POSITION },
{ VERTEXATTR_VEC3, SHADERSEMANTIC_NORMAL },
{ VERTEXATTR_VEC2, SHADERSEMANTIC_TEXCOORD },
};
static InputLayoutDesc_t g_skinnedVertexLayout[] = {
{ VERTEXATTR_VEC3, SHADERSEMANTIC_POSITION },
{ VERTEXATTR_VEC3, SHADERSEMANTIC_NORMAL },
{ VERTEXATTR_VEC2, SHADERSEMANTIC_TEXCOORD }
};
Shader* g_litShader = nullptr;
std::string getFileNameWithoutExtension(const std::string& filename)
{
size_t lastindex = filename.find_last_of(".");
if (lastindex != std::string::npos) {
return filename.substr(0, lastindex);
}
return filename;
}
ModelSystem::ModelSystem()
{
}
ModelSystem::~ModelSystem()
{
}
void ModelSystem::Shutdown()
{
for (int i = 0; i < m_models.size(); i++)
{
if (m_models[i].model)
{
delete m_models[i].model;
m_models[i].model = nullptr;
}
}
m_models.clear();
}
Model* ModelSystem::LoadModel(const char* filename)
{
auto it = std::find_if(m_models.begin(), m_models.end(), [=](const ModelEntry& entry) { return strcmp(entry.filename, filename) == 0; });
if (it != m_models.end())
{
return it->model;
}
if (!GetFileSystem()->IsExist(filename))
{
Msg("ModelSystem::LoadModel: File '%s' not exist.", filename);
return nullptr;
}
Model* model = new Model();
if (strstr(filename, ".obj"))
{
model->LoadObj(filename);
}
else
{
// Find file extension
size_t stringLength = strlen(filename);
for (int i = stringLength; i > 0; --i)
{
if (filename[i] == '.')
{
stringLength = i;
break;
}
}
const char* fileExtension = &filename[stringLength];
Msg("ModelSystem::LoadModel: Unknowed file format '%s'", filename, fileExtension);
return nullptr;
}
ModelEntry entry = {};
strcpy(entry.filename, filename);
entry.model = model;
m_models.push_back(entry);
Msg("Loaded Model '%s'", filename);
return model;
}
Model::Model()
{
m_data.vb = nullptr;
m_data.ib = nullptr;
m_AlbedoTexture = nullptr;
//m_boundingBox.min = glm::vec3(0.0f);
//m_boundingBox.max = glm::vec3(0.0f);
}
Model::~Model()
{
m_AlbedoTexture = nullptr;
if (m_data.vb)
{
delete m_data.vb;
m_data.vb = nullptr;
}
}
void Model::LoadObj(const char* filename)
{
Msg("Loading OBJ file %s...", filename);
std::vector<unsigned int> vertexIndices, uvIndices, normalIndices;
std::vector<glm::vec3> temp_vertices;
std::vector<glm::vec2> temp_uvs;
std::vector<glm::vec3> temp_normals;
std::vector<glm::vec3> out_vertices;
std::vector<glm::vec2> out_uvs;
std::vector<glm::vec3> out_normals;
FILE* file = fopen(filename, "r");
if (file == NULL) {
Msg("Model::LoadObj: Impossible to open the file !");
return;
}
while (1) {
char lineHeader[128];
// read the first word of the line
int res = fscanf(file, "%s", lineHeader);
if (res == EOF)
break; // EOF = End Of File. Quit the loop.
// else : parse lineHeader
if (strcmp(lineHeader, "v") == 0) {
glm::vec3 vertex;
fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z);
temp_vertices.push_back(vertex);
}
else if (strcmp(lineHeader, "vt") == 0) {
glm::vec2 uv;
fscanf(file, "%f %f\n", &uv.x, &uv.y);
uv.y = -uv.y; // Invert V coordinate since we will only use DDS texture, which are inverted. Remove if you want to use TGA or BMP loaders.
temp_uvs.push_back(uv);
}
else if (strcmp(lineHeader, "vn") == 0) {
glm::vec3 normal;
fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z);
temp_normals.push_back(normal);
}
else if (strcmp(lineHeader, "f") == 0) {
std::string vertex1, vertex2, vertex3;
unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2]);
if (matches != 9) {
Msg("Model::LoadObj: File can't be read by our simple parser :-( Try exporting with other options");
fclose(file);
return;
}
vertexIndices.push_back(vertexIndex[0]);
vertexIndices.push_back(vertexIndex[1]);
vertexIndices.push_back(vertexIndex[2]);
uvIndices.push_back(uvIndex[0]);
uvIndices.push_back(uvIndex[1]);
uvIndices.push_back(uvIndex[2]);
normalIndices.push_back(normalIndex[0]);
normalIndices.push_back(normalIndex[1]);
normalIndices.push_back(normalIndex[2]);
}
else {
// Probably a comment, eat up the rest of the line
char stupidBuffer[1000];
fgets(stupidBuffer, 1000, file);
}
}
// For each vertex of each triangle
for (unsigned int i = 0; i < vertexIndices.size(); i++) {
// Get the indices of its attributes
unsigned int vertexIndex = vertexIndices[i];
unsigned int uvIndex = uvIndices[i];
unsigned int normalIndex = normalIndices[i];
// Get the attributes thanks to the index
glm::vec3 vertex = temp_vertices[vertexIndex - 1];
glm::vec2 uv = temp_uvs[uvIndex - 1];
glm::vec3 normal = temp_normals[normalIndex - 1];
// Put the attributes in buffers
out_vertices.push_back(vertex);
out_uvs.push_back(uv);
out_normals.push_back(normal);
}
fclose(file);
// Combine in to the one array
std::vector<StaticMeshVertex> vertices;
for (unsigned int i = 0; i < vertexIndices.size(); i++)
{
// Get the indices of its attributes
unsigned int vertexIndex = vertexIndices[i];
unsigned int uvIndex = uvIndices[i];
unsigned int normalIndex = normalIndices[i];
// Get the attributes thanks to the index
glm::vec3 vertex = temp_vertices[vertexIndex - 1];
glm::vec2 uv = temp_uvs[uvIndex - 1];
glm::vec3 normal = temp_normals[normalIndex - 1];
StaticMeshVertex vtx = {};
vtx.position = vertex;
vtx.normal = normal;
vtx.texcoord = uv;
vertices.push_back(vtx);
//m_boundingBox.min = glm::min(m_boundingBox.min, vertex);
//m_boundingBox.max = glm::max(m_boundingBox.max, vertex);
}
m_Vertices = vertices;
m_data.vb = g_pRenderDevice->CreateVertexBuffer(vertices.data(), (int)sizeof(StaticMeshVertex) * (int)vertices.size());
m_data.vbcount = vertices.size();
std::string mtlfilename = getFileNameWithoutExtension(filename);
mtlfilename += ".mtl";
LoadMtl(mtlfilename.c_str());
}
void Model::LoadMtl(const char* filename)
{
Msg("Loading MTL file %s...", filename);
FILE* file = fopen(filename, "r");
if (file == NULL) {
Msg("Model::LoadObj: Impossible to open the file !");
return;
}
while (1) {
char lineHeader[128];
// read the first word of the line
int res = fscanf(file, "%s", lineHeader);
if (res == EOF)
break; // EOF = End Of File. Quit the loop.
if (strcmp(lineHeader, "map_Kd") == 0) {
char stupidBuffer[1000];
fgets(stupidBuffer, 1000, file);
const char* textureFilename = stupidBuffer + 1;
m_AlbedoTexture = g_pTexturesManager->LoadTexture2D(textureFilename, true);
}
//if (strcmp(lineHeader, "v") == 0) {
// glm::vec3 vertex;
// fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z);
// temp_vertices.push_back(vertex);
//}
//else {
// // Probably a comment, eat up the rest of the line
// char stupidBuffer[1000];
// fgets(stupidBuffer, 1000, file);
//}
}
fclose(file);
}
void Model::Draw(const glm::mat4& model, bool isTransparent /*= false*/)
{
if (!g_litShader)
{
g_litShader = g_pShaderSystem->CreateShader("lit_generic", "data/shaders/lit_generic.vs", "data/shaders/lit_generic.ps",
g_staticVertexLayout, sizeof(g_staticVertexLayout) / sizeof(g_staticVertexLayout[0]));
}
glFrontFace(GL_CCW);
glDepthFunc(GL_LESS);
g_pRenderDevice->SetCullFace(true);
g_pRenderDevice->SetDepthTest(true);
g_pRenderDevice->SetDepthWrite(true);
if (isTransparent)
{
// Enable blending
g_pRenderDevice->SetBlending(true);
g_pRenderDevice->SetBlendingFunction(BF_SRC_ALPHA, BF_ONE_MINUS_SRC_ALPHA);
glm::vec4 color = glm::vec4(1.f, 1.f, 1.f, .5f);
g_pShaderSystem->SetUniformFloat4(g_litShader, UNIFORM_CUSTOM_COLOR, glm::value_ptr(color));
}
else
{
g_pRenderDevice->SetBlending(false);
//glm::vec4 color = glm::vec4(1.f, 1.f, 1.f, 1.f);
//g_pShaderSystem->SetUniformFloat4(g_litShader, UNIFORM_CUSTOM_COLOR, glm::value_ptr(color));
}
g_pRenderDevice->SetVerticesBuffer(m_data.vb);
g_pShaderSystem->SetShader(g_litShader);
g_pShaderSystem->SetUniformMatrix(g_litShader, UNIFORM_MODEL_MATRIX, &model[0]);
//static float test = 0.0f;
//test += g_systemTimer->GetDelta() * 6.0f;
//glm::mat4 model = glm::mat4(1.0f);
//int32_t x = 0, y = 0;
//g_inputSystem->GetMousePosition(&x, &y);
//
//glm::mat4 model = glm::mat4(1.0f);
//model = glm::rotate(model, test, glm::vec3(0.0f, 1.0f, 0.0f));
//
//float realY = (float)g_renderView.height - (float)y - 1;
//
//glm::vec4 viewport = glm::vec4(0.0f, 0.0f, (float)g_renderView.width, (float)g_renderView.height);
//glm::vec3 pos = glm::unProject(
// glm::vec3((float)x, realY, 0.0f),
// model,
// g_renderView.proj,
// viewport);
//
//pos *= 50.0f;
//
//model = glm::translate(model, pos);
glm::mat4 mvp = glm::identity<glm::mat4>();
mvp = g_pRender->GetProjectionMatrix() * g_pRender->GetViewMatrix() * model;
g_pShaderSystem->SetUniformMatrix(g_litShader, UNIFORM_MVP_MATRIX, &mvp[0]);
g_pTexturesManager->SetTexture(0, m_AlbedoTexture);
g_pShaderSystem->SetUniformSampler(g_litShader, SAMPLER_ALBEDO, 0);
g_pRenderDevice->DrawArrays(PT_TRIANGLES, 0, m_data.vbcount);
#if 0
glm::mat4 mvp = glm::identity<glm::mat4>();
mvp = g_renderView.proj * g_renderView.view * model;
g_shaderSystem->SetUniformMatrix(g_litShader, UNIFORM_MVP_MATRIX, &mvp[0]);
g_pTexturesManager->SetTexture(0, m_AlbedoTexture);
g_shaderSystem->SetUniformSampler(g_litShader, SAMPLER_ALBEDO, 0);
g_renderDevice->DrawArrays(PT_TRIANGLES, 0, m_data.vbcount);
BoundingBox bbox = m_boundingBox;
TransformBoundingBox(bbox, model);
g_pDebugRender->DrawBoundingBox(bbox, glm::vec3(1.0f));
#endif
}
void ReleaseModelData(ModelData_t& data)
{
if (data.ib)
{
delete data.ib;
data.ib = nullptr;
}
if (data.vb)
{
delete data.vb;
data.vb = nullptr;
}
}

70
src/render/modelsystem.h Normal file
View File

@@ -0,0 +1,70 @@
#ifndef MODELMANAGER_H
#define MODELMANAGER_H
#include <vector>
#include "render_shared.h"
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
class GPUBuffer;
class Texture2D;
struct ModelData_t
{
GPUBuffer* vb;
GPUBuffer* ib;
uint32_t vbcount;
uint32_t ibcount;
};
void ReleaseModelData(ModelData_t& data);
class Render;
class Model
{
friend class Render;
public:
Model();
~Model();
void LoadObj(const char* filename);
void LoadMtl(const char* filename);
void Draw(const glm::mat4& model, bool isTransparent = false);
//BoundingBox GetBoundingBox() { return m_boundingBox; }
private:
std::vector<StaticMeshVertex> m_Vertices;
ModelData_t m_data;
//BoundingBox m_boundingBox;
Texture2D* m_AlbedoTexture;
};
class ModelSystem
{
public:
ModelSystem();
~ModelSystem();
void Shutdown();
Model* LoadModel(const char* filename);
private:
struct ModelEntry
{
char filename[260];
Model* model;
};
std::vector< ModelEntry > m_models;
};
extern ModelSystem* g_pModelSystem;
#endif // !MODELMANAGER_H

266
src/render/render.cpp Normal file
View File

@@ -0,0 +1,266 @@
#include "core.h"
#include "log.h"
#include "render.h"
#include "renderdevice.h"
#include "texturesmanager.h"
#include "shadersystem.h"
#include "modelsystem.h"
#include "debugrender.h"
#include "ifilesystem.h"
#include <pugixml.hpp>
static GLuint g_VAO = 0;
// TEMP
glm::vec3 g_viewOrigin;
glm::vec3 g_viewOrient;
#ifdef GL_DEBUG_OUTPUT
#define GL_DEBUG_OUTPUT 0x92E0
#endif // GL_DEBUG_OUTPUT
#ifndef GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB
#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242
#endif // !GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB
#ifndef GL_DEBUG_TYPE_ERROR_ARB
#define GL_DEBUG_TYPE_ERROR_ARB 0x824C
#endif // !GL_DEBUG_TYPE_ERROR_ARB
#ifndef GL_ARB_debug_output
#define GL_ARB_debug_output 1
typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint* ids, GLboolean enabled);
PFNGLDEBUGMESSAGECONTROLARBPROC glDebugMessageControlARB;
typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* buf);
PFNGLDEBUGMESSAGEINSERTARBPROC glDebugMessageInsertARB;
typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC)(GLDEBUGPROCARB callback, const void* userParam);
PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARB;
typedef GLuint(APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC)(GLuint count, GLsizei bufSize, GLenum* sources, GLenum* types, GLuint* ids, GLenum* severities, GLsizei* lengths, GLchar* messageLog);
PFNGLGETDEBUGMESSAGELOGARBPROC glGetDebugMessageLogARB;
#endif
void APIENTRY GL_DebugOutput(GLenum source,
GLenum type,
unsigned int id,
GLenum severity,
GLsizei length,
const char* message,
const void* userParam)
{
// Nvidia spam too much about filenameBuffer mapping
if (type == 0x8251)
return;
Msg("OpenGL: %stype = 0x%x, severity = 0x%x, message = %s\n",
(type == GL_DEBUG_TYPE_ERROR_ARB ? "** GL ERROR ** " : ""),
type, severity, message);
if (type == GL_DEBUG_TYPE_ERROR_ARB)
{
bool debug = true;
}
}
Render* g_pRender = nullptr;
Render::Render() :
m_pWindow(nullptr),
m_pGLContext(nullptr),
m_pStretchedPicVBuf(nullptr),
m_sceneModel(nullptr),
m_bUsingVAO(false)
{
m_ViewMatrix = glm::identity<glm::mat4>();
m_ProjectionMatrix = glm::identity<glm::mat4>();
}
Render::~Render()
{
}
void Render::Init(SDL_Window* pWindow)
{
m_pWindow = pWindow;
SDL_assert(m_pWindow);
// Create OpenGL context
m_pGLContext = SDL_GL_CreateContext(m_pWindow);
SDL_GL_MakeCurrent(m_pWindow, m_pGLContext);
gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress);
if (GLVersion.major == 0 || GLVersion.minor == 0)
{
Core::Error("Failed to load OpenGL");
}
// Core profile probably, should use VAO
if (GLVersion.major >= 3 && GLVersion.minor >= 1)
m_bUsingVAO = true;
Msg("%s", (const char*)glGetString(GL_VENDOR));
Msg("%s", (const char*)glGetString(GL_RENDERER));
Msg("OpenGL ver. %s", (const char*)glGetString(GL_VERSION));
Msg("Context created with OpenGL version %d.%d", GLVersion.major, GLVersion.minor);
// Reset OpenGL error stack
glGetError();
// Load extension
glDebugMessageCallbackARB = (PFNGLDEBUGMESSAGECALLBACKARBPROC)SDL_GL_GetProcAddress("glDebugMessageCallbackARB");
// Enable debug output
if (glDebugMessageCallbackARB)
{
Msg("...found GL_ARB_debug_output");
//glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
glDebugMessageCallbackARB(GL_DebugOutput, NULL);
}
// Create render device
g_pRenderDevice = new RenderDevice();
// Create texture manager
g_pTexturesManager = new TexturesManager();
g_pTexturesManager->Init();
// Create shader system
g_pShaderSystem = new ShaderSystem();
g_pShaderSystem->Init();
// Create model system
g_pModelSystem = new ModelSystem();
// Create debug render
g_pDebugRender = new DebugRender();
g_pDebugRender->Initialize();
// Create stretched picture filenameBuffer
m_pStretchedPicVBuf = g_pRenderDevice->CreateVertexBuffer(nullptr, MAX_STRETCH_VX, true);
// Core profile require Vertex Array Object to render
if (m_bUsingVAO)
{
// Create global vertex array
glGenVertexArrays(1, &g_VAO);
glBindVertexArray(g_VAO);
}
}
void Render::Shutdown()
{
if (m_bUsingVAO)
{
glBindVertexArray(0);
glDeleteVertexArrays(1, &g_VAO);
}
delete m_pStretchedPicVBuf;
m_pStretchedPicVBuf = nullptr;
g_pDebugRender->Shutdown();
delete g_pDebugRender;
g_pDebugRender = nullptr;
g_pModelSystem->Shutdown();
delete g_pModelSystem;
g_pModelSystem = nullptr;
g_pShaderSystem->Shutdown();
delete g_pShaderSystem;
g_pShaderSystem = nullptr;
g_pTexturesManager->Shutdown();
delete g_pTexturesManager;
g_pTexturesManager = nullptr;
delete g_pRenderDevice;
g_pRenderDevice = nullptr;
// Destroy OpenGL context
SDL_GL_MakeCurrent(nullptr, nullptr);
SDL_GL_DestroyContext(m_pGLContext);
m_pGLContext = nullptr;
}
void Render::RenderScene()
{
if (m_sceneModel) {
static glm::mat4 s_identity = glm::mat4(1.0f);
m_sceneModel->Draw(s_identity);
}
g_pDebugRender->RenderFrame();
}
void Render::Present(bool vsync)
{
SDL_GL_SwapWindow(m_pWindow);
}
void Render::ResetStates()
{
g_pRenderDevice->SetDepthTest(true);
g_pRenderDevice->SetDepthWrite(true);
g_pRenderDevice->SetStencilTest(false);
g_pRenderDevice->SetScissorTest(false);
g_pRenderDevice->SetCullFace(false);
g_pRenderDevice->SetBlending(false);
}
void Render::SetViewMatrix(const glm::mat4& matView)
{
m_ViewMatrix = matView;
}
void Render::SetProjectionMatrix(const glm::mat4& matProjection)
{
m_ProjectionMatrix = matProjection;
}
void Render::LoadSceneXML(const char* filename)
{
char filenameBuffer[kMaxPathLength];
snprintf(filenameBuffer, kMaxPathLength, "data/levels/%s/%s.xml", filename, filename);
FileHandle_t file = GetFileSystem()->OpenFile(filenameBuffer, "rb");
SDL_assert_always(file != kInvalidFileHandleValue);
size_t length = GetFileSystem()->GetFileLength(file);
char* filedata = new char[length + 1];
GetFileSystem()->ReadFile(file, filedata, length);
filedata[length] = '\0';
GetFileSystem()->CloseFile(file);
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_buffer(filedata, length);
delete[] filedata;
if (!result) {
Core::Error("Render::LoadSceneXML: Error while reading level description file '%s'\nError: %s:%i",
filenameBuffer, result.description(), result.offset);
}
pugi::xml_node root = doc.document_element();
const char* scenefilename = root.child("SceneFile").attribute("filename").value();
sprintf(filenameBuffer, "data/levels/%s/%s", filename, scenefilename);
if (!GetFileSystem()->IsExist(filenameBuffer)) {
Core::Error("SceneManager::LoadScene: scene file '%s' doesnt exist", scenefilename);
}
m_sceneFilename = filenameBuffer;
m_sceneModel = g_pModelSystem->LoadModel(m_sceneFilename.c_str());
}
//IRender* GetRender()
//{
// return g_pRender;
//}

58
src/render/render.h Normal file
View File

@@ -0,0 +1,58 @@
#ifndef RENDER_H
#define RENDER_H
#include "gl_shared.h"
#include "render_shared.h"
#include <string>
class GPUBuffer;
class Model;
class Render
{
public:
Render();
~Render();
void Init(SDL_Window* pWindow);
void Shutdown();
void RenderScene();
void Present(bool vsync = false);
void ResetStates();
void SetViewMatrix(const glm::mat4& matView);
void SetProjectionMatrix(const glm::mat4& matProjection);
inline const glm::mat4& GetViewMatrix() { return m_ViewMatrix; }
inline const glm::mat4& GetProjectionMatrix() { return m_ProjectionMatrix; }
void LoadSceneXML(const char* filename);
private:
glm::mat4 m_ViewMatrix;
glm::mat4 m_ProjectionMatrix;
SDL_Window* m_pWindow;
SDL_GLContext m_pGLContext;
GPUBuffer* m_pStretchedPicVBuf;
std::string m_sceneFilename;
std::string m_sceneName;
Model* m_sceneModel;
bool m_bUsingVAO;
};
extern Render* g_pRender;
// TEMP
extern glm::vec3 g_viewOrigin;
extern glm::vec3 g_viewOrient;
#endif // !RENDER_H

240
src/render/render_shared.h Normal file
View File

@@ -0,0 +1,240 @@
// Shared header for render subsystem
#ifndef RENDER_SHARED_H
#define RENDER_SHARED_H
#include <stdint.h>
#include <glm/glm.hpp>
typedef uint32_t index_t;
enum BufferType
{
BT_VERTEX,
BT_INDEX,
BT_MAX
};
enum BufferAccess
{
BA_READ_ONLY,
BA_WRITE_ONLY,
BA_READ_WRITE,
BA_MAX
};
// texture format
enum PixelFormat
{
PF_UNKNOWN,
PF_R8G8B8,
PF_R8G8B8A8,
PF_R8G8B8F,
PF_R8G8B8A8F,
// Depth formats
PF_DEPTH32F
};
// Blending functions
enum BlendFactor
{
BF_ZERO,
BF_ONE,
BF_SRC_COLOR,
BF_ONE_MINUS_SRC_COLOR,
BF_DST_COLOR,
BF_ONE_MINUS_DST_COLOR,
BF_SRC_ALPHA,
BF_ONE_MINUS_SRC_ALPHA,
BF_DST_ALPHA,
BF_ONE_MINUS_DST_ALPHA,
BF_CONSTANT_COLOR,
BF_ONE_MINUS_CONSTANT_COLOR,
BF_CONSTANT_ALPHA,
BF_ONE_MINUS_CONSTANT_ALPHA
};
enum BlendEquation
{
BE_FUNC_ADD,
BE_FUNC_SUBTRACT,
BE_FUNC_REVERSE_SUBTRACT,
BE_MIN,
BE_MAX
};
enum DepthFunc
{
DF_NEVER, // Never passes.
DF_LESS, // Passes if the incoming depth value is less than the stored depth value.
DF_EQUAL, // Passes if the incoming depth value is equal to the stored depth value.
DF_LEQUAL, // Passes if the incoming depth value is less than or equal to the stored depth value.
DF_GREATER, // Passes if the incoming depth value is greater than the stored depth value.
DF_NOTEQUAL, // Passes if the incoming depth value is not equal to the stored depth value.
DF_GEQUAL, // Passes if the incoming depth value is greater than or equal to the stored depth value.
DF_ALWAYS, // Always passes.
DF_MAX
};
enum TextureWrap
{
TextureWrap_Repeat,
TextureWrap_MirroredRepeat,
TextureWrap_ClampToEdge,
TextureWrap_ClampToBorder
};
enum TextureFilter
{
TextureFilter_Nearest,
TextureFilter_Linear,
TextureFilter_NearestMipmapNearest,
TextureFilter_LinearMipmapNearest,
TextureFilter_NearestMipmapLinear,
TextureFilter_LinearMipmapLinear
};
enum TextureSurfaceType
{
TST_COLOR = 1 << 0,
TST_DEPTH = 1 << 1,
TST_STENCIL = 1 << 2,
};
enum PrimitiveType
{
PT_POINTS,
PT_LINES,
PT_TRIANGLES
};
// pixel format convertion
uint32_t GetGLPF(PixelFormat pf);
uint32_t GetGLInternalPF(PixelFormat pf);
uint32_t GetGLTypePF(PixelFormat pf);
// Wrapping and Filter convertion
int getGlWrap(TextureWrap wrap);
int getGlTexFilter(TextureFilter filter);
uint32_t GetGLDepthFunc(DepthFunc func);
// Blending factor convertion
uint32_t GetGLBlendFactor(BlendFactor factor);
// Blending equation convertion
uint32_t GetGLEquation(BlendEquation equation);
struct Viewport
{
int x;
int y;
int width;
int height;
};
// Base structure for render view (view and projection matrices, viewport settings)
struct View
{
//glm::mat4 view;
//glm::mat4 proj;
int x, y;
int width, height;
float fov;
bool ortho;
};
// Stretched picture vertex
struct StretchedVertex
{
glm::vec3 position;
glm::vec2 texcoord;
glm::vec4 color;
};
#define MAX_STRETCH_VX 12 * sizeof(StretchedVertex)
const int SHADERUNIFORM_MAX_COUNT = 16;
const int INPUT_LAYOUT_MAX_COUNT = 8;
enum VertexAttribute_t {
VERTEXATTR_VEC2,
VERTEXATTR_VEC3,
VERTEXATTR_VEC4,
VERTEXATTR_UINT,
VERTEXATTR_MAX
};
enum ShaderSemantic_t {
SHADERSEMANTIC_POSITION,
SHADERSEMANTIC_COLOR,
SHADERSEMANTIC_TEXCOORD,
SHADERSEMANTIC_TEXCOORD0,
SHADERSEMANTIC_TEXCOORD1,
SHADERSEMANTIC_NORMAL,
SHADERSEMANTIC_TANGENT,
SHADERSEMANTIC_BITANGENT,
SHADERSEMANTIC_BONEIDS,
SHADERSEMANTIC_WEIGHTS,
SHADERSEMANTIC_MAX
};
enum ShaderUniformType_t {
SHADERUNIFORM_FLOAT,
SHADERUNIFORM_VEC2,
SHADERUNIFORM_VEC3,
SHADERUNIFORM_VEC4,
SHADERUNIFORM_MAT4,
SHADERUNIFORM_MAX
};
enum ShaderUniform_t
{
UNIFORM_MODEL_MATRIX,
UNIFORM_VIEW_MATRIX,
UNIFORM_PROJ_MATRIX,
UNIFORM_MVP_MATRIX,
UNIFORM_CUSTOM_COLOR,
UNIFORM_SUN_DIRECTION,
UNIFORM_SUN_COLOR,
UNIFORM_SUN_AMBIENT,
UNIFORM_MAX,
};
enum ShaderSamplers_t
{
SAMPLER_ALBEDO,
SAMPLER_NORMAL,
SAMPLER_LIGHTMAP,
SAMPLER_MAX
};
struct InputLayoutDesc_t {
VertexAttribute_t attribute;
ShaderSemantic_t semantic;
};
struct ShaderUniformDesc_t {
ShaderUniformType_t type;
const char* name;
size_t size;
};
struct StaticMeshVertex
{
glm::vec3 position;
glm::vec3 normal;
glm::vec2 texcoord;
};
#endif

162
src/render/renderdevice.cpp Normal file
View File

@@ -0,0 +1,162 @@
#include <assert.h>
#include <stdio.h>
// OpenGL
#include "gl_shared.h"
#include "gpu_buffer.h"
#include "renderdevice.h"
static GLenum g_glPrimitiveMode[PT_TRIANGLES + 1] = {
GL_POINTS,
GL_LINES,
GL_TRIANGLES
};
// The render device instance.
RenderDevice* g_pRenderDevice = nullptr;
RenderDevice::RenderDevice()
{
m_activeVB = nullptr;
m_activeIB = nullptr;
m_blending = false;
m_activeReadRT = nullptr;
m_activeWriteRT = nullptr;
}
RenderDevice::~RenderDevice()
{
}
GPUBuffer* RenderDevice::CreateVertexBuffer(void* data, size_t size, bool isStream)
{
return new GPUBuffer(BT_VERTEX, data, size, isStream);
}
GPUBuffer* RenderDevice::CreateIndexBuffer(void* data, size_t size, bool isStream)
{
return new GPUBuffer(BT_INDEX, data, size, isStream);
}
void RenderDevice::SetVerticesBuffer(GPUBuffer* buffer)
{
if (buffer) {
if (m_activeVB != buffer) {
m_activeVB = buffer;
m_activeVB->Bind();
}
} else { // unbind buffer
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
void RenderDevice::SetIndicesBuffer(GPUBuffer* buffer)
{
if (buffer) {
if (m_activeIB != buffer) {
m_activeIB = buffer;
m_activeIB->Bind();
}
} else { // unbind buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
void RenderDevice::SetDepthTest(bool enable)
{
enable ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
}
void RenderDevice::SetDepthWrite(bool enable)
{
glDepthMask(enable ? GL_TRUE : GL_FALSE);
}
void RenderDevice::SetStencilTest(bool enable)
{
enable ? glEnable(GL_STENCIL_TEST) : glDisable(GL_STENCIL_TEST);
}
void RenderDevice::SetScissorTest(bool enable)
{
enable ? glEnable(GL_SCISSOR_TEST) : glDisable(GL_SCISSOR_TEST);
}
void RenderDevice::SetCullFace(bool enable)
{
enable ? glEnable(GL_CULL_FACE) : glDisable(GL_CULL_FACE);
}
void RenderDevice::SetBlending(bool value)
{
if (m_blending != value) {
m_blending = value;
value ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
}
}
void RenderDevice::SetBlendingFunction(BlendFactor srcFactor, BlendFactor destFactor)
{
// Switch state if one of two blending factors was changed
if (srcFactor != m_srcBlendFactor || destFactor != m_destBlendFactor)
{
m_srcBlendFactor = srcFactor;
m_destBlendFactor = destFactor;
// push to gl state
glBlendFunc(GetGLBlendFactor(srcFactor), GetGLBlendFactor(destFactor));
}
}
void RenderDevice::SetBlendFuncSeparate(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha)
{
glBlendFuncSeparate(GetGLBlendFactor(srcRGB), GetGLBlendFactor(dstRGB),
GetGLBlendFactor(srcAlpha), GetGLBlendFactor(dstAlpha));
}
void RenderDevice::SetBlendEquation(BlendEquation equation)
{
glBlendEquation(GetGLEquation(equation));
}
void RenderDevice::SetViewport(int x, int y, int w, int h)
{
glViewport(x, y, w, h);
}
void RenderDevice::Clear(uint32_t surfaces, float r, float g, float b, float a, float z, uint32_t stencil)
{
GLenum flags = 0;
if (surfaces & TST_COLOR)
{
flags |= GL_COLOR_BUFFER_BIT;
glClearColor(r, g, b, a);
}
if (surfaces & TST_DEPTH)
{
flags |= GL_DEPTH_BUFFER_BIT;
glClearDepth(z);
}
if (surfaces & TST_STENCIL)
{
flags |= GL_STENCIL_BUFFER_BIT;
glClearStencil((GLint)stencil);
}
glClear(flags);
}
void RenderDevice::DrawArrays(PrimitiveType primType, uint32_t startOf, size_t verticesCount)
{
glDrawArrays(g_glPrimitiveMode[primType], startOf, GLsizei(verticesCount));
}
void RenderDevice::DrawElements(PrimitiveType primType, size_t elementsCount, bool is16bitIndices)
{
glDrawElements(g_glPrimitiveMode[primType], GLsizei(elementsCount), is16bitIndices ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, NULL);
}

59
src/render/renderdevice.h Normal file
View File

@@ -0,0 +1,59 @@
#ifndef RENDERDEVICE_H
#define RENDERDEVICE_H
#include <stdint.h>
#include "render_shared.h"
class GPUBuffer;
class VertexFormat;
class RenderTarget;
class RenderDevice
{
public:
RenderDevice();
~RenderDevice();
GPUBuffer* CreateVertexBuffer(void* data, size_t size, bool isStream = false);
GPUBuffer* CreateIndexBuffer(void* data, size_t size, bool isStream = false);
void SetVerticesBuffer(GPUBuffer* buffer);
void SetIndicesBuffer(GPUBuffer* buffer);
void SetDepthTest(bool enable);
void SetDepthWrite(bool enable);
void SetStencilTest(bool enable);
void SetScissorTest(bool enable);
void SetCullFace(bool enable);
void SetBlending(bool value);
void SetBlendingFunction(BlendFactor srcFactor, BlendFactor destFactor);
void SetBlendFuncSeparate(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha);
void SetBlendEquation(BlendEquation equation);
void SetViewport(int x, int y, int w, int h);
void Clear(uint32_t surfaces, float r, float g, float b, float a, float z, uint32_t stencil);
// drawing
void DrawArrays(PrimitiveType primType, uint32_t startOf, size_t verticesCount);
void DrawElements(PrimitiveType primType, size_t elementsCount, bool is16bitIndices);
private:
GPUBuffer* m_activeVB;
GPUBuffer* m_activeIB;
RenderTarget* m_activeReadRT;
RenderTarget* m_activeWriteRT;
bool m_blending;
BlendFactor m_srcBlendFactor;
BlendFactor m_destBlendFactor;
};
extern RenderDevice* g_pRenderDevice;
#endif // !RENDERDEVICE_H

89
src/render/shader.cpp Normal file
View File

@@ -0,0 +1,89 @@
#include "shader.h"
#include "ifilesystem.h"
#include "log.h"
#include <string>
GLuint CreateShader(GLenum shaderType, const char* filename)
{
FileHandle_t file = GetFileSystem()->OpenFile(filename, "rb");
if (file == kInvalidFileHandleValue)
{
Msg("CreateShader: failed to open file %s", filename);
}
size_t length = GetFileSystem()->GetFileLength(file);
std::string content;
content.resize(length + 1);
GetFileSystem()->ReadFile(file, (void*)content.data(), length);
content[length] = '\0';
GetFileSystem()->CloseFile(file);
const char* contentCStr = content.c_str();
GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &contentCStr, NULL);
glCompileShader(shader);
int success;
char infoLog[512];
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 512, NULL, infoLog);
Msg("Failed to compile shader %s\n%s", filename, infoLog);
}
Msg("created shader from file %s", filename);
return shader;
}
Shader::Shader() :
m_stride(0),
m_layoutCount(0)
{
}
Shader::~Shader()
{
Destroy();
}
void Shader::Create(const char* name, const char* vsfilepath, const char* psfilepath)
{
m_name = name;
GLuint vertexShader = CreateShader(GL_VERTEX_SHADER, vsfilepath);
GLuint fragmentShader = CreateShader(GL_FRAGMENT_SHADER, psfilepath);
m_program = glCreateProgram();
glAttachShader(m_program, vertexShader);
glAttachShader(m_program, fragmentShader);
AllocateAttributes();
glLinkProgram(m_program);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
int success;
char infoLog[512];
glGetProgramiv(m_program, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(m_program, 512, NULL, infoLog);
Msg("Failed to link program %s", infoLog);
}
AllocateUniforms();
}
void Shader::Destroy()
{
glDeleteProgram(m_program);
}

65
src/render/shader.h Normal file
View File

@@ -0,0 +1,65 @@
#ifndef SHADER_H
#define SHADER_H
#include <stdint.h>
#include <string>
#include "render_shared.h"
#include "gl_shared.h"
class ShaderSystem;
class Shader
{
friend class ShaderSystem;
public:
Shader();
~Shader();
void Create(const char* name, const char* vsfilepath, const char* psfilepath);
void Destroy();
void AllocateAttributes();
void AllocateUniforms();
bool HasUniform(ShaderUniform_t uniform) { return m_glUniformLocation[uniform] != -1; }
const std::string& GetName() const { return m_name; }
private:
// TEMP SOLUTION
ShaderUniformDesc_t m_uniformDesc[SHADERUNIFORM_MAX_COUNT];
size_t m_uniformCount;
InputLayoutDesc_t m_layouts[INPUT_LAYOUT_MAX_COUNT];
size_t m_layoutCount;
GLint m_glUniformLocation[SHADERSEMANTIC_MAX];
GLint m_glSamplerLocation[SAMPLER_MAX];
GLuint m_glLayouts[INPUT_LAYOUT_MAX_COUNT];
std::string m_name;
int m_stride;
GLuint m_program;
};
inline int GetVertexAttributeSize( VertexAttribute_t attrib )
{
switch ( attrib )
{
case VERTEXATTR_VEC2:
return 2;
case VERTEXATTR_VEC3:
return 3;
case VERTEXATTR_VEC4:
return 4;
default:
break;
}
return 0;
}
#endif // !SHADER_H

188
src/render/shadersystem.cpp Normal file
View File

@@ -0,0 +1,188 @@
#include "shadersystem.h"
#include "shader.h"
#include "gl_shared.h"
static const char* g_uniformNameTable[UNIFORM_MAX] =
{
"u_modelMatrix",
"u_viewMatrix",
"u_projectionMatrix",
"u_modelViewProjection",
"u_customColor",
"u_sunDirection",
"u_sunColor",
"u_sunAmbientColor",
};
static const char* g_samplersNameTable[SAMPLER_MAX] =
{
"u_albedoTexture",
"u_normalTexture",
"u_lightmapTexture",
};
static const char* g_attributeNameTable[SHADERSEMANTIC_MAX] =
{
"a_position",//SHADERSEMANTIC_POSITION,
"a_color", //SHADERSEMANTIC_COLOR
"a_texcoord",//SHADERSEMANTIC_TEXCOORD,
"a_texcoord0", //SHADERSEMANTIC_TEXCOORD0,
"a_texcoord1",//SHADERSEMANTIC_TEXCOORD1,
"a_normal",//SHADERSEMANTIC_NORMAL,
"a_tangent",//SHADERSEMANTIC_TANGENT,
"a_bitangent",//SHADERSEMANTIC_BITANGENT,
"a_boneIds",//SHADERSEMANTIC_BONEIDS,
"a_weights"//SHADERSEMANTIC_WEIGHTS,
};
static size_t g_attributeSizeTable[VERTEXATTR_MAX] =
{
sizeof(float) * 2,//VERTEXATTR_VEC2,
sizeof(float) * 3,//VERTEXATTR_VEC3,
sizeof(float) * 4,//VERTEXATTR_VEC4,
sizeof(unsigned int)//VERTEXATTR_UINT
};
size_t g_vertexAttribsSizeTable[VERTEXATTR_MAX] =
{
2, // VERTEXATTR_VEC2
3, // VERTEXATTR_VEC3
4, // VERTEXATTR_VEC4
4, // VERTEXATTR_UINT
};
size_t g_vertexAttribsRealSizeTable[VERTEXATTR_MAX] =
{
8, // VERTEXATTR_VEC2
12, // VERTEXATTR_VEC3
16, // VERTEXATTR_VEC4
4, // VERTEXATTR_UINT
};
ShaderSystem* g_pShaderSystem = nullptr;
ShaderSystem::ShaderSystem()
{
}
ShaderSystem::~ShaderSystem()
{
}
void ShaderSystem::Init()
{
}
void ShaderSystem::Shutdown()
{
for (int i = 0; i < m_shaders.size(); i++)
{
if (m_shaders[i])
{
delete m_shaders[i];
m_shaders[i] = nullptr;
}
}
m_shaders.clear();
}
Shader* ShaderSystem::CreateShader(const char* name, const char* vsfilepath, const char* psfilepath, InputLayoutDesc_t* inputLayout /*= nullptr*/, int inputLayoutCount/* = 0*/)
{
auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [=](const Shader* shader) { return shader->GetName() == name; });
if ( it != m_shaders.end() )
{
return *it;
}
Shader* pShader = new Shader();
if ( inputLayout && inputLayoutCount > 0 )
{
memcpy( pShader->m_layouts, inputLayout, inputLayoutCount * sizeof( InputLayoutDesc_t ) );
pShader->m_layoutCount = inputLayoutCount;
}
pShader->Create( name, vsfilepath, psfilepath );
m_shaders.push_back(pShader);
return pShader;
}
void ShaderSystem::SetShader(const Shader* shader)
{
SDL_assert( shader );
glUseProgram( shader->m_program );
// apply input layout
size_t appliedOffset = 0;
for (int i = 0; i < shader->m_layoutCount; i++)
{
const InputLayoutDesc_t* layoutEntry = &shader->m_layouts[i];
glEnableVertexAttribArray(GLuint(i));
if (layoutEntry->attribute == VERTEXATTR_UINT)
{
glVertexAttribPointer(GLuint(i), GLint(g_vertexAttribsSizeTable[layoutEntry->attribute]),
GL_UNSIGNED_BYTE, GL_TRUE, static_cast<GLsizei>(shader->m_stride),
(appliedOffset > 0) ? (void*)(appliedOffset * sizeof(unsigned int)) : (void*)0);
}
else
{
glVertexAttribPointer(GLuint(i), GLint(g_vertexAttribsSizeTable[layoutEntry->attribute]),
GL_FLOAT, GL_FALSE, static_cast<GLsizei>(shader->m_stride),
(appliedOffset > 0) ? (void*)(appliedOffset * sizeof(float)) : (void*)0);
}
appliedOffset += g_vertexAttribsSizeTable[layoutEntry->attribute];
}
}
void ShaderSystem::SetUniformSampler(const Shader* shader, ShaderSamplers_t sampler, int index)
{
glUniform1i( shader->m_glSamplerLocation[ sampler ], static_cast< GLint >(index) );
}
void ShaderSystem::SetUniformFloat4(const Shader* shader, ShaderUniform_t uniform, const void* data)
{
glUniform4fv( shader->m_glUniformLocation[ uniform ], 1, ( const GLfloat* )data );
}
void ShaderSystem::SetUniformMatrix( const Shader* shader, ShaderUniform_t uniform, const void* data )
{
glUniformMatrix4fv( shader->m_glUniformLocation[ uniform ], 1, GL_FALSE, ( const GLfloat* )data );
}
void Shader::AllocateAttributes()
{
// Allocate input layout
for ( int i = 0; i < m_layoutCount; i++ )
{
const InputLayoutDesc_t& layout = m_layouts[ i ];
glBindAttribLocation( m_program, i, g_attributeNameTable[ layout.semantic ] );
GL_CHECK_ERROR();
// add element size to stride
m_stride += (int)g_attributeSizeTable[ layout.attribute ];
}
}
void Shader::AllocateUniforms()
{
// parse shader uniforms
for (int i = 0; i < UNIFORM_MAX; i++)
{
m_glUniformLocation[i] = glGetUniformLocation(m_program, g_uniformNameTable[i]);
}
// parse shader samplers
for (int i = 0; i < SAMPLER_MAX; i++)
{
m_glSamplerLocation[i] = glGetUniformLocation(m_program, g_samplersNameTable[i]);
}
}

32
src/render/shadersystem.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef SHADERSYSTEM_H
#define SHADERSYSTEM_H
#include <string>
#include <vector>
#include "shader.h"
class ShaderSystem
{
public:
ShaderSystem();
~ShaderSystem();
void Init();
void Shutdown();
Shader* CreateShader(const char* name, const char* vsfilepath, const char* psfilepath, InputLayoutDesc_t* inputLayout = nullptr, int inputLayoutCount = 0);
void SetShader(const Shader* shader);
void SetUniformSampler( const Shader* shader, ShaderSamplers_t sampler, int index );
void SetUniformFloat4( const Shader* shader, ShaderUniform_t uniform, const void* data );
void SetUniformMatrix( const Shader* shader, ShaderUniform_t uniform, const void* data );
private:
std::vector<Shader*> m_shaders;
};
extern ShaderSystem* g_pShaderSystem;
#endif // !SHADERSYSTEM_H

206
src/render/texture2d.cpp Normal file
View File

@@ -0,0 +1,206 @@
#include "log.h"
#include "ifilesystem.h"
#include "texture2d.h"
#include "texturesmanager.h"
#include "gl_shared.h"
Texture2D* Texture2D::Create()
{
return new Texture2D;
}
Texture2D::Texture2D()
{
m_pf = PF_UNKNOWN;
m_width = 0;
m_height = 0;
m_channels = 0;
m_handle = -1;
}
Texture2D::~Texture2D()
{
m_pf = PF_UNKNOWN;
m_width = 0;
m_height = 0;
m_channels = 0;
m_handle = -1;
}
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
// STB Image loading code
void Texture2D::CreateFromFile(const char* filename)
{
int width, height, channels;
m_textureFileName = filename;
FileHandle_t file = GetFileSystem()->OpenFile(filename, "rb");
size_t imageSize = GetFileSystem()->GetFileLength(file);
uint8_t* fileData = (uint8_t*)malloc(imageSize);
GetFileSystem()->ReadFile(file, fileData, imageSize);
GetFileSystem()->CloseFile(file);
uint8_t* imageData = stbi_load_from_memory(fileData, int(imageSize), &width, &height, &channels, 0);
if (imageData == NULL) {
free(fileData);
Msg("Texture loading error: %s (%s)", filename, stbi_failure_reason());
SDL_assert(imageData);
}
CreateFromExistedData(imageData, width, height, channels);
m_textureFileName = filename;
free(fileData);
stbi_image_free(imageData);
}
void Texture2D::CreateBlackTexture(int width, int height, int channels)
{
size_t textureSize = width * height * channels;
uint8_t* data = new uint8_t[textureSize];
assert(data);
for (int i = 0; i < (int)textureSize; i++) {
data[i] = 0;
}
CreateFromExistedData(data, width, height, channels);
delete[] data;
}
void Texture2D::CreateWhiteTexture(int width, int height, int channels)
{
size_t textureSize = width * height * channels;
uint8_t* data = new uint8_t[textureSize];
assert(data);
for (int i = 0; i < (int)textureSize; i++) {
data[i] = 255;
}
CreateFromExistedData(data, width, height, channels);
delete[] data;
}
void Texture2D::CreateGrayTexture(int width, int height, int channels)
{
size_t textureSize = width * height * channels;
uint8_t* data = new uint8_t[textureSize];
assert(data);
for (int i = 0; i < (int)textureSize; i++) {
data[i] = 255 / 2;
}
CreateFromExistedData(data, width, height, channels);
delete[] data;
}
void Texture2D::CreateTexture_Generator(int width, int height, int channels, int color)
{
size_t textureSize = width * height * channels;
uint8_t* data = new uint8_t[textureSize];
assert(data);
m_textureFileName = "$generator_texture$";
for (int i = 0; i < (int)textureSize; i++) {
data[i] = color;
}
CreateFromExistedData(data, width, height, channels);
delete[] data;
}
void Texture2D::CreateFromExistedData(void* data, int width, int height, int channels)
{
//assert(data);
m_width = width;
m_height = height;
m_channels = channels;
glGenTextures(1, &m_handle);
glBindTexture(GL_TEXTURE_2D, m_handle);
glTexImage2D(GL_TEXTURE_2D, 0, (channels == 3) ? GL_RGB : GL_RGBA, width, height, 0, (channels == 3) ? GL_RGB : GL_RGBA, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
}
void Texture2D::CreateRaw(void* data, int width, int height, PixelFormat pf)
{
m_width = width;
m_height = height;
m_channels = (pf == PF_R8G8B8) ? 3 : 4;
m_pf = pf;
glGenTextures(1, &m_handle);
glBindTexture(GL_TEXTURE_2D, m_handle);
glTexImage2D(GL_TEXTURE_2D, 0, GetGLInternalPF(pf), width, height, 0, GetGLInternalPF(pf), GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
}
#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
void Texture2D::GenerateMipmaps()
{
glBindTexture(GL_TEXTURE_2D, m_handle);
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//if (g_texAnisoFilter.getValueB()) {
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, g_texAnisoLevel.getValueI());
//}
glBindTexture(GL_TEXTURE_2D, 0);
}
void Texture2D::Bind()
{
glBindTexture(GL_TEXTURE_2D, m_handle);
}
void Texture2D::setWrapS(TextureWrap wrap)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, getGlWrap(wrap));
}
void Texture2D::setWrapT(TextureWrap wrap)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, getGlWrap(wrap));
}
void Texture2D::setMin(TextureFilter filter)
{
GLint param = 0;
param = getGlTexFilter(filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, param);
}
void Texture2D::setMag(TextureFilter filter)
{
GLint param = 0;
param = getGlTexFilter(filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, param);
}

52
src/render/texture2d.h Normal file
View File

@@ -0,0 +1,52 @@
#ifndef TEXTURE2D_H
#define TEXTURE2D_H
#include <string>
#include "render_shared.h"
class TexturesManager;
class Texture2D
{
friend class TexturesManager;
public:
static Texture2D* Create();
public:
Texture2D();
~Texture2D();
void CreateBlackTexture(int width, int height, int channels);
void CreateWhiteTexture(int width, int height, int channels);
void CreateGrayTexture(int width, int height, int channels);
void CreateTexture_Generator(int width, int height, int channels, int color);
void CreateFromExistedData(void* data, int width, int height, int channels);
void CreateFromFile(const char* filename);
void CreateRaw(void* data, int width, int height, PixelFormat pf);
void GenerateMipmaps();
void Bind();
void setWrapS(TextureWrap wrap);
void setWrapT(TextureWrap wrap);
void setMin(TextureFilter filter);
void setMag(TextureFilter filter);
uint32_t GetHandle() { return m_handle; }
int GetWidth() { return m_width; }
int GetHeight() { return m_height; }
private:
std::string m_textureFileName;
PixelFormat m_pf;
int m_width;
int m_height;
int m_channels;
uint32_t m_handle;
};
#endif // !TEXTURE2D_H

View File

@@ -0,0 +1,197 @@
#include "core.h"
#include "log.h"
#include "ifilesystem.h"
#include "texture2d.h"
#include "texturesmanager.h"
#include "gl_shared.h"
#include <stb_image.h>
static const char* g_texFileExtensions[] = { ".png", ".jpeg", ".jpg", ".tga", ".bmp" };
const int kTexFileExtensionsSize = sizeof(g_texFileExtensions) / sizeof(g_texFileExtensions[0]);
TexturesManager* g_pTexturesManager = nullptr;
static std::string getFileExtension(const std::string& filename)
{
size_t whereIsDot = filename.find_last_of('.');
if (whereIsDot != std::string::npos) {
return filename.substr(whereIsDot);
}
return "";
}
static std::string getFileNameWithoutExtension(const std::string& filename)
{
size_t lastindex = filename.find_last_of(".");
if (lastindex != std::string::npos) {
return filename.substr(0, lastindex);
}
return filename;
}
static std::string getFilenameWithoutPathAndExtension(const std::string& filename)
{
size_t whereIsDot = filename.find_last_of('.');
size_t whereIsSlash = filename.find_last_of('/');
if (whereIsSlash == std::string::npos) {
whereIsSlash = filename.find_last_of('\\');
}
if (whereIsDot == std::string::npos && whereIsSlash == std::string::npos) {
return filename;
}
std::string string = filename.substr(whereIsSlash + 1);
whereIsDot = string.find_last_of('.');
string = string.substr(0, whereIsDot);
return string;
}
TexturesManager::TexturesManager()
{
m_notex = nullptr;
}
TexturesManager::~TexturesManager()
{
}
void TexturesManager::Init()
{
stbi_set_flip_vertically_on_load(false);
m_notex = LoadTexture2D("data/textures/notex.png", true);
if (!m_notex) {
Core::Error("TexturesManager::Init: Failed to initialize system texture! 'system/notex.png' is not exist.");
}
}
void TexturesManager::Shutdown()
{
if (!m_textures.empty()) {
Msg("--- unfreed textures ---");
for (std::vector<Texture2D*>::iterator it = m_textures.begin(); it != m_textures.end(); ++it) {
Msg("%s", (*it)->m_textureFileName.c_str());
delete* it;
*it = nullptr;
}
m_textures.clear();
}
}
void TexturesManager::SetTexture(int slot, Texture2D* texture)
{
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, texture ? texture->GetHandle() : 0);
}
Texture2D* TexturesManager::CreateManual2D(const char* name, uint32_t width, uint32_t height, PixelFormat format, void* pData, bool useAsRenderTarget)
{
for (std::vector<Texture2D*>::iterator it = m_textures.begin(); it != m_textures.end(); ++it) {
if ((*it)->m_textureFileName == name) {
Msg("TexturesManager::CreateManual2D: texture %s is already created!", name);
return nullptr;
}
}
// allocate
Texture2D* texture = Texture2D::Create();
texture->CreateRaw(pData, width, height, format);
texture->m_textureFileName = name;
if (useAsRenderTarget)
Msg("Created rt texture [%s]", name);
m_textures.push_back(texture);
return texture;
}
bool IsSupportedExtension(const char* filename)
{
std::string ext = getFileExtension(filename);
for (int i = 0; i < kTexFileExtensionsSize; i++) {
if (ext == g_texFileExtensions[i]) {
return true;
}
}
return false;
}
Texture2D* TexturesManager::LoadTexture2D(const char* texturename, bool useMipmaps /*= false*/)
{
int texturesNbr = m_textures.size();
for (int i = 0; i < texturesNbr; i++) {
if (m_textures[i]->m_textureFileName == texturename)
return m_textures[i];
}
if (strcmp(texturename, "$white$") == 0) {
Texture2D* tex = Texture2D::Create();
tex->m_textureFileName = "$white$";
tex->CreateWhiteTexture(16, 16, 3);
m_textures.push_back(tex);
return tex;
}
if (strcmp(texturename, "$black$") == 0) {
Texture2D* tex = Texture2D::Create();
tex->m_textureFileName = "$black$";
tex->CreateBlackTexture(16, 16, 3);
m_textures.push_back(tex);
return tex;
}
if (strcmp(texturename, "$gray$") == 0) {
Texture2D* tex = Texture2D::Create();
tex->m_textureFileName = "$gray$";
tex->CreateGrayTexture(16, 16, 3);
m_textures.push_back(tex);
return tex;
}
if (strlen(texturename) <= 0) {
return m_notex;
}
std::string texnamebuf;
// find texture from disk
for (int i = 0; i < kTexFileExtensionsSize; i++)
{
std::string textureFilename = getFileNameWithoutExtension(texturename);
textureFilename += g_texFileExtensions[i];
if (GetFileSystem()->IsExist(textureFilename.c_str()))
{
texnamebuf = textureFilename;
break;
}
}
if (!texnamebuf.empty()) {
Texture2D* texture = Texture2D::Create();
texture->CreateFromFile(texnamebuf.c_str());
if (useMipmaps)
texture->GenerateMipmaps();
Msg("loaded %s", getFilenameWithoutPathAndExtension(texturename).c_str());
m_textures.push_back(texture);
return texture;
}
else if (texnamebuf.empty() && m_notex) {
Msg("not found %s", getFilenameWithoutPathAndExtension(texturename).c_str());
return m_notex;
}
return nullptr;
}

View File

@@ -0,0 +1,40 @@
#ifndef TEXTURESMANAGER_H
#define TEXTURESMANAGER_H
#include <stdint.h>
#include <vector>
#include "render_shared.h"
class Texture2D;
class TexturesManager
{
public:
TexturesManager();
~TexturesManager();
void Init();
void Shutdown();
void SetTexture(int slot, Texture2D* texture);
Texture2D* CreateManual2D(
const char* name,
uint32_t width,
uint32_t height,
PixelFormat format,
void* pData = nullptr,
bool useAsRenderTarget = false);
Texture2D* LoadTexture2D(const char* texturename, bool useMipmaps = false);
private:
std::vector<Texture2D*> m_textures;
Texture2D* m_notex;
};
extern TexturesManager* g_pTexturesManager;
#endif // !TEXTURESMANAGER_H

124
src/sound/soundsystem.cpp Normal file
View File

@@ -0,0 +1,124 @@
#include "core.h"
#include "log.h"
#define MINIAUDIO_IMPLEMENTATION
#include "soundsystem.h"
SoundSystem g_soundSystem;
SoundSystem::SoundSystem()
{
memset(&m_engine, 0, sizeof(m_engine));
memset(&m_sounds, 0, sizeof(m_sounds));
m_inited = false;
m_dwSoundsNum = 0;
}
SoundSystem::~SoundSystem()
{
}
void SoundSystem::Init()
{
ma_result result;
Msg("Sound initialization\n");
result = ma_engine_init(NULL, &m_engine);
if ( result != MA_SUCCESS )
{
Msg("Failed to initialize the miniaudio engine. %s\n", ma_result_description(result));
//DError("Failed to initialize the miniaudio engine. %s\n", ma_result_description(result));
return; // Failed to initialize the engine.
}
ma_uint32 uiChannels = ma_engine_get_channels(&m_engine);
Msg("%d channel(s)\n", uiChannels);
ma_uint32 uiSampleRate = ma_engine_get_sample_rate(&m_engine);
Msg("Sound sampling rate: %d\n", uiSampleRate);
m_inited = true;
}
void SoundSystem::Shutdown()
{
if ( !m_inited )
return;
for ( int i = 0; i < m_dwSoundsNum; i++ )
{
ma_sound_uninit(&m_sounds[i]);
}
memset(&m_sounds, 0, sizeof(m_sounds));
ma_engine_uninit(&m_engine);
memset(&m_engine, 0, sizeof(m_engine));
}
SoundHandle SoundSystem::LoadSound(const char* filename)
{
if ( !m_inited )
return -1;
ma_result result;
SoundHandle handle = m_dwSoundsNum;
result = ma_sound_init_from_file(&m_engine, filename, 0, NULL, NULL, &m_sounds[handle]);
if ( result != MA_SUCCESS )
{
Msg("Failed to load sound %s\n", filename);
return -1;
}
m_dwSoundsNum++;
return handle;
}
void SoundSystem::Play(SoundHandle handle, bool bLoop)
{
if ( !m_inited )
return;
assert(handle <= -1 || handle <= m_dwSoundsNum);
ma_sound_start(&m_sounds[handle]);
if ( bLoop )
ma_sound_set_looping(&m_sounds[handle], true);
}
void SoundSystem::Stop(SoundHandle handle)
{
if ( !m_inited )
return;
assert(handle <= -1 || handle <= m_dwSoundsNum);
ma_sound_stop(&m_sounds[handle]);
}
bool SoundSystem::IsPlaying(SoundHandle handle)
{
if ( !m_inited )
return false;
assert(handle <= -1 || handle <= m_dwSoundsNum);
return ma_sound_is_playing(&m_sounds[handle]);
}
void SoundSystem::SetMasterVolume(float volume)
{
if ( !m_inited )
return;
ma_engine_set_volume(&m_engine, volume);
}
float SoundSystem::GetMasterVolume()
{
return ma_engine_get_volume(&m_engine);
}

38
src/sound/soundsystem.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef SOUNDSYSTEM_H
#define SOUNDSYSTEM_H
#include "miniaudio.h"
#define MAX_SOUNDS 64
typedef int SoundHandle;
class SoundSystem
{
public:
SoundSystem();
~SoundSystem();
void Init();
void Shutdown();
SoundHandle LoadSound(const char* filename);
void Play(SoundHandle handle, bool bLoop);
void Stop(SoundHandle handle);
bool IsPlaying(SoundHandle handle);
void SetMasterVolume(float volume);
float GetMasterVolume();
private:
ma_sound m_sounds[MAX_SOUNDS];
ma_engine m_engine;
int m_dwSoundsNum;
bool m_inited;
};
extern SoundSystem g_soundSystem;
#endif // !SOUNDSYSTEM_H