Initial Commit
This commit is contained in:
63
src/CMakeLists.txt
Normal file
63
src/CMakeLists.txt
Normal 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)
|
||||
30
src/engine/boundingbox.cpp
Normal file
30
src/engine/boundingbox.cpp
Normal 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
41
src/engine/boundingbox.h
Normal 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
92
src/engine/camera.cpp
Normal 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
232
src/engine/camera.h
Normal 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
121
src/engine/core.cpp
Normal 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
25
src/engine/core.h
Normal 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
308
src/engine/engine.cpp
Normal 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
36
src/engine/engine.h
Normal 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
|
||||
64
src/engine/entitymanager.cpp
Normal file
64
src/engine/entitymanager.cpp
Normal 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;
|
||||
}
|
||||
31
src/engine/entitymanager.h
Normal file
31
src/engine/entitymanager.h
Normal 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
|
||||
10
src/engine/entityregistrator.cpp
Normal file
10
src/engine/entityregistrator.cpp
Normal 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;
|
||||
}
|
||||
30
src/engine/entityregistrator.h
Normal file
30
src/engine/entityregistrator.h
Normal 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
121
src/engine/filesystem.cpp
Normal 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
46
src/engine/filesystem.h
Normal 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
38
src/engine/ientity.cpp
Normal 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
46
src/engine/ientity.h
Normal 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
32
src/engine/ifilesystem.h
Normal 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
|
||||
68
src/engine/inputmanager.cpp
Normal file
68
src/engine/inputmanager.cpp
Normal 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
66
src/engine/inputmanager.h
Normal 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
143
src/engine/log.cpp
Normal 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
18
src/engine/log.h
Normal 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
|
||||
13
src/engine/physics/bullet_private.cpp
Normal file
13
src/engine/physics/bullet_private.cpp
Normal 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);
|
||||
}
|
||||
|
||||
19
src/engine/physics/bullet_private.h
Normal file
19
src/engine/physics/bullet_private.h
Normal 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
|
||||
40
src/engine/physics/physicsdebugdraw.cpp
Normal file
40
src/engine/physics/physicsdebugdraw.cpp
Normal 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;
|
||||
}
|
||||
|
||||
25
src/engine/physics/physicsdebugdraw.h
Normal file
25
src/engine/physics/physicsdebugdraw.h
Normal 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
|
||||
180
src/engine/physics/physicsworld.cpp
Normal file
180
src/engine/physics/physicsworld.cpp
Normal 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);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
51
src/engine/physics/physicsworld.h
Normal file
51
src/engine/physics/physicsworld.h
Normal 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
|
||||
301
src/engine/physics/rigidbody.cpp
Normal file
301
src/engine/physics/rigidbody.cpp
Normal 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());
|
||||
}
|
||||
84
src/engine/physics/rigidbody.h
Normal file
84
src/engine/physics/rigidbody.h
Normal 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
176
src/engine/world.cpp
Normal 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
44
src/engine/world.h
Normal 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
172
src/game/game.cpp
Normal 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
18
src/game/game.h
Normal 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
|
||||
91
src/game/game_lua_help.cpp
Normal file
91
src/game/game_lua_help.cpp
Normal 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
17
src/game/game_lua_help.h
Normal 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
122
src/game/game_object.cpp
Normal 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
60
src/game/game_object.h
Normal 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
120
src/render/debugrender.cpp
Normal 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
52
src/render/debugrender.h
Normal 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
215
src/render/gl_shared.cpp
Normal 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
18
src/render/gl_shared.h
Normal 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
103
src/render/gpu_buffer.cpp
Normal 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
32
src/render/gpu_buffer.h
Normal 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
404
src/render/modelsystem.cpp
Normal 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
70
src/render/modelsystem.h
Normal 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
266
src/render/render.cpp
Normal 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
58
src/render/render.h
Normal 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
240
src/render/render_shared.h
Normal 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
162
src/render/renderdevice.cpp
Normal 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
59
src/render/renderdevice.h
Normal 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
89
src/render/shader.cpp
Normal 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
65
src/render/shader.h
Normal 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
188
src/render/shadersystem.cpp
Normal 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
32
src/render/shadersystem.h
Normal 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
206
src/render/texture2d.cpp
Normal 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
52
src/render/texture2d.h
Normal 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
|
||||
197
src/render/texturesmanager.cpp
Normal file
197
src/render/texturesmanager.cpp
Normal 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;
|
||||
}
|
||||
40
src/render/texturesmanager.h
Normal file
40
src/render/texturesmanager.h
Normal 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
124
src/sound/soundsystem.cpp
Normal 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
38
src/sound/soundsystem.h
Normal 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
|
||||
Reference in New Issue
Block a user