Initial commit

This commit is contained in:
Arnau Alier Torres 2025-04-16 17:30:03 +02:00
commit d1a99961b1
1163 changed files with 634915 additions and 0 deletions

15
.gitignore vendored Executable file
View File

@ -0,0 +1,15 @@
*.vcxproj
*.vcxproj.filters
*.vcxproj.user
*.sln
*.dbscn
*.make
*.code-workspace
Makefile
.vscode/
bin/
/vendor/premake-core/
.vs/
AS_DEBUG/
Tasks/

22
Build-Service.lua Executable file
View File

@ -0,0 +1,22 @@
require("premakeExtensions/premake-vscode")
workspace "Deer"
architecture "x64"
configurations { "Debug", "Release", "Dist" }
startproject "App"
OutputDir = "%{cfg.system}-%{cfg.architecture}/%{cfg.buildcfg}"
group "core"
include "Deer/Build-Service.lua"
group ""
group "external"
include "Deer/vendor/spdlog/Build.lua"
include "Deer/vendor/angelScript/Build.lua"
group ""
group "Runtime"
include "DeerServer/Build.lua"
group ""

57
Deer/Build-Service.lua Normal file
View File

@ -0,0 +1,57 @@
project "DeerService"
kind "StaticLib"
language "C++"
cppdialect "C++20"
targetdir "bin/%{cfg.buildcfg}"
staticruntime "off"
files {
"src/Deer/**.h",
"src/Deer/**.cpp"
}
includedirs
{
"src",
"Include",
"vendor/spdlog/include",
"vendor/glm",
"vendor/entt/include",
"vendor/cereal/include",
"vendor/angelScript/include"
}
targetdir ("../bin/" .. OutputDir .. "/%{prj.name}")
objdir ("../bin/int/" .. OutputDir .. "/%{prj.name}")
filter "system:windows"
systemversion "latest"
defines { "WINDOWS"}
defines { }
filter "system:linux"
toolset "clang"
buildoptions { "-std=c++20" }
defines { "LINUX"}
files {
"src/Plattform/linux/**.h",
"src/Plattform/linux/**.cpp"
}
filter "configurations:Debug"
defines { "DEBUG" }
runtime "Debug"
symbols "On"
filter "configurations:Release"
defines { "RELEASE" }
runtime "Release"
optimize "On"
symbols "On"
filter "configurations:Dist"
defines { "DIST" }
runtime "Release"
optimize "On"
symbols "Off"

112
Deer/Build.lua Executable file
View File

@ -0,0 +1,112 @@
project "Deer"
kind "StaticLib"
language "C++"
cppdialect "C++20"
targetdir "bin/%{cfg.buildcfg}"
staticruntime "off"
files {
"src/Deer/**.h",
"src/Deer/**.cpp",
"src/DeerRender/**.h",
"src/DeerRender/**.cpp",
"src/Plattform/OpenGL/**.h",
"src/Plattform/OpenGL/**.cpp",
"src/Plattform/ImGUI/**.h",
"src/Plattform/ImGUI/**.cpp",
"vendor/ImGuizmo/**.cpp",
"vendor/stb/stb_image.cpp"
}
links { "spdlog", "GLFW", "glad", "ImGui", "angelScript" }
defines { "DEER_RENDER" }
includedirs
{
"src",
"Include",
"vendor/spdlog/include",
"vendor/GLFW/include",
"vendor/glad/include",
"vendor/imgui",
"vendor/glm",
"vendor/stb",
"vendor/ImGuizmo",
"vendor/entt/include",
"vendor/cereal/include",
"vendor/objload/include/objload",
"vendor/angelScript/include"
}
targetdir ("../bin/" .. OutputDir .. "/%{prj.name}")
objdir ("../bin/int/" .. OutputDir .. "/%{prj.name}")
filter "system:windows"
systemversion "latest"
defines {"WINDOWS"}
files {
"src/Plattform/windows/**.h",
"src/Plattform/windows/**.cpp"
}
filter "system:linux"
toolset "clang"
defines {"LINUX"}
buildoptions { "-std=c++20" }
local gtk_libs = os.outputof("pkg-config --libs gtk+-3.0")
links { "GL" }
-- Files for GTK
includedirs {
"/usr/include/gtk-3.0", -- GTK3 headers
"/usr/include/gdk-pixbuf-2.0", -- GDK Pixbuf headers
"/usr/include/cairo", -- Cairo graphics library headers
"/usr/include/pango-1.0", -- Pango text layout and rendering headers
"/usr/include/atk-1.0", -- ATK accessibility toolkit headers
"/usr/include/gdk-3.0", -- GDK headers
"/usr/include/glib-2.0", -- GLib headers
"/usr/lib/x86_64-linux-gnu/glib-2.0/include", -- GLib additional headers
"/usr/include/harfbuzz"
}
-- Link libraries for GTK3 and its dependencies
links {
"gtk-3", -- GTK3 library
"gdk-3", -- GDK library
"glib-2.0", -- GLib library
"pango-1.0", -- Pango library
"atk-1.0", -- ATK library
"cairo", -- Cairo graphics library
"gdk_pixbuf-2.0", -- GDK Pixbuf library
"gio-2.0", -- GIO library
"gobject-2.0", -- GObject library
"pthread" -- POSIX threads library
}
links { "pthread" }
buildoptions { "-pthread" }
linkoptions { "-pthread" }
files {
"src/Plattform/Linux/**.h",
"src/Plattform/Linux/**.cpp"
}
filter "configurations:Debug"
defines { "DEBUG" }
runtime "Debug"
symbols "On"
filter "configurations:Release"
defines { "RELEASE" }
runtime "Release"
optimize "On"
symbols "On"
filter "configurations:Dist"
defines { "DIST" }
runtime "Release"
optimize "On"
symbols "Off"

65
Deer/Include/Deer/Application.h Executable file
View File

@ -0,0 +1,65 @@
#pragma once
#include "Deer/Memory.h"
#ifdef DEER_RENDER
#include "DeerRender/Events/ApplicationEvent.h"
#include "DeerRender/Events/Event.h"
#include "DeerRender/Window.h"
#endif
namespace Deer {
class ImGuiLayer;
namespace Core {
extern int argc;
extern char** argv;
} // namespace Core
class Timestep {
public:
Timestep(float time = 0.0f) : m_time(time) {}
float getSeconds() const { return m_time; }
float getMilliseconds() const { return m_time * 1000; }
private:
float m_time;
};
class Application {
public:
Application();
~Application();
static Application* s_application;
int run();
virtual int onInit() { return 0; }
virtual int onPreInit() { return 0; }
virtual void onShutdown() {}
virtual void onUpdate(Timestep delta) {}
private:
bool m_running;
float m_lastFrameTime = 0.0f;
#ifdef DEER_RENDER
public:
Application(const WindowProps& props = WindowProps());
virtual void onRender(Timestep delta) {}
virtual void onImGUI() {}
virtual void onEvent(Event& event) {}
Scope<Window> m_window;
private:
Scope<ImGuiLayer> m_imGuiLayer;
const WindowProps m_windowProps;
virtual void onEventCallback(Event& e);
void initializeWindow();
bool onWindowClose(WindowCloseEvent& e);
#endif
};
} // namespace Deer

82
Deer/Include/Deer/Asset.h Executable file
View File

@ -0,0 +1,82 @@
#pragma once
#include <string>
#include <vector>
#include "Deer/DataStore.h"
#include "Deer/Log.h"
#include "Deer/Path.h"
namespace Deer {
template <typename T>
class Asset {
public:
Asset() : m_assetID(0), m_assetLocation("null") {}
Asset(uint32_t id, const std::filesystem::path& assetLocation)
: m_assetID(id), m_assetLocation(assetLocation) {
try {
uint32_t size;
uint8_t* data = DataStore::readFile(assetLocation, &size);
value = T::create(data, size);
delete[] data;
} catch (const std::string& error) {
DEER_CORE_ERROR("Error loading asset {0}\n{1}",
assetLocation.generic_string().c_str(),
error.c_str());
}
}
inline uint32_t getAssetID() const { return m_assetID; }
inline Path& getAssetLocation() { return m_assetLocation; }
Ref<T> value;
private:
uint32_t m_assetID;
Path m_assetLocation;
};
template <>
class Asset<void> {
public:
Asset() : m_assetID(0), m_assetLocation("null") {}
Asset(uint32_t id, const std::filesystem::path& assetLocation)
: m_assetID(id), m_assetLocation(assetLocation) {}
inline uint32_t getAssetID() const { return m_assetID; }
inline Path& getAssetLocation() { return m_assetLocation; }
Ref<void> value;
private:
uint32_t m_assetID;
Path m_assetLocation;
};
namespace AssetManager {
extern std::vector<Asset<void>> assets;
template <typename T>
inline Asset<T>& getAsset(uint32_t assetID) {
return *(Asset<T>*)&(assets[assetID]);
}
template <typename T>
inline uint32_t loadAsset(const std::filesystem::path& assetLocation) {
for (size_t id = 0; id < assets.size(); ++id) {
if (assets[id].getAssetLocation() == assetLocation) return id;
}
uint32_t assetID = assets.size();
Asset<T> asset(assetID, assetLocation);
assets.push_back(*(Asset<void>*)&(asset));
return assetID;
}
inline const std::filesystem::path getAssetLocation(uint32_t assetID) {
return assets[assetID].getAssetLocation();
}
} // namespace AssetManager
} // namespace Deer

View File

@ -0,0 +1,68 @@
#pragma once
#include <string>
#include <unordered_map>
class asITypeInfo;
class asIScriptObject;
class asIScriptFunction;
class asIScriptContext;
namespace Deer {
struct ScriptAttribute;
using ScriptAttributeMap = std::unordered_map<std::string, ScriptAttribute>;
struct ScriptAttribute {
std::string name;
int typeID;
int location;
int internalID;
bool isPrivate;
ScriptAttribute(const char* _name = "", int _type = 0,
bool _isPrivate = 0, int _location = 0,
int _internalID = 0)
: typeID(_type),
name(_name),
isPrivate(_isPrivate),
location(_location),
internalID(_internalID) {}
};
class ComponentScriptInstance {
public:
ComponentScriptInstance() = default;
~ComponentScriptInstance();
void updateInternalVars();
void start();
asIScriptObject* m_object;
asIScriptFunction* m_updateFunction;
asIScriptFunction* m_startFuction;
};
class ComponentScript {
public:
ComponentScript() : m_typeInfo(nullptr) {}
ComponentScript(asITypeInfo* typeInfo);
inline const std::string& getName() { return m_scriptID; }
inline const ScriptAttributeMap& getAttributes() {
return m_attributes;
}
inline const ScriptAttribute getAttribute(
const std::string& attributeID) {
return m_attributes[attributeID];
}
inline asITypeInfo* getTypeInfo() { return m_typeInfo; }
private:
asITypeInfo* m_typeInfo;
ScriptAttributeMap m_attributes;
std::string m_scriptID;
};
ScriptAttributeMap extractAttributes(asITypeInfo* typeInfo);
} // namespace Deer

62
Deer/Include/Deer/Components.h Executable file
View File

@ -0,0 +1,62 @@
#pragma once
#include "Deer/Memory.h"
#define GLM_ENABLE_EXPERIMENTAL
#include <stdint.h>
#include <string>
#include <vector>
#include "glm/glm.hpp"
#include "glm/gtc/quaternion.hpp"
namespace Deer {
class ComponentScriptInstance;
struct TagComponent {
std::string tag;
uint32_t entityUID;
TagComponent() = default;
TagComponent(const TagComponent&) = default;
TagComponent(std::string name, uint32_t _id = 0)
: tag(name), entityUID(_id) {}
};
struct ScriptComponent {
std::string scriptID;
Ref<ComponentScriptInstance> roeInstance;
ScriptComponent() = default;
ScriptComponent(const ScriptComponent&) = default;
ScriptComponent(std::string _scriptID) : scriptID(_scriptID) {}
};
struct RelationshipComponent {
uint32_t parent_UID = 0;
std::vector<uint32_t> children;
RelationshipComponent() = default;
RelationshipComponent(const RelationshipComponent&) = default;
RelationshipComponent(uint32_t parent) : parent_UID(parent) {}
};
struct TransformComponent {
glm::vec3 position = glm::vec3(0.0f);
glm::vec3 scale = glm::vec3(1.0f);
glm::quat rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
TransformComponent() = default;
TransformComponent(glm::vec3 _position) : position(_position) {}
TransformComponent(const TransformComponent&) = default;
inline const glm::vec3 getEulerAngles() {
return glm::degrees(glm::eulerAngles(rotation));
}
inline void setEulerAngles(const glm::vec3& eulerAngles) {
rotation = glm::quat(glm::radians(eulerAngles));
}
glm::mat4 getMatrix() const;
};
} // namespace Deer

37
Deer/Include/Deer/DataStore.h Executable file
View File

@ -0,0 +1,37 @@
#pragma once
#include <filesystem>
#include <string>
#include <vector>
#include "Deer/Path.h"
#define DEER_SCENE_PATH "scenes"
#define DEER_SCRIPT_PATH "scripts"
#define DEER_SHADER_PATH "shaders"
#define DEER_VOXEL_PATH "voxels"
#define DEER_VOXEL_DATA_PATH "voxels/data"
#define DEER_VOXEL_ASPECT_PATH "voxels/aspect"
#define DEER_VOXEL_TEXTURE_PATH "voxels/textures"
#define DEER_VOXEL_SHADER_PATH "voxels/shaders"
#define DEER_OBJECT_PATH "objects"
#define DEER_BIN_PATH "bin"
#define DEER_TEMP_PATH "tmp"
namespace Deer {
namespace DataStore {
void createFolder(const Path& path);
void saveFile(const Path&, uint8_t* data, uint32_t size);
uint8_t* readFile(const Path&, uint32_t* size);
void deleteFile(const Path&);
// Refactor----
void compressFiles(std::vector<Path> files, const Path& path);
std::vector<Path> getFiles(const Path& path,
const std::string& extension);
// Refactor----
extern Path rootPath;
} // namespace DataStore
} // namespace Deer

27
Deer/Include/Deer/EntryPoint.h Executable file
View File

@ -0,0 +1,27 @@
#pragma once
#include "Deer/Application.h"
#include "Deer/Log.h"
extern Deer::Application* createApplication(int argc, char** argv);
namespace Deer {
int Main(int argc, char** argv) {
Core::argc = argc;
Core::argv = argv;
Log::init();
DEER_CORE_TRACE("Initializing");
Application* app = createApplication(argc, argv);
int runResult = app->run();
delete app;
DEER_CORE_TRACE("Deinitializing");
Log::shutdown();
return runResult;
}
} // namespace Deer
int main(int argc, char** argv) { return Deer::Main(argc, argv); }

141
Deer/Include/Deer/Enviroment.h Executable file
View File

@ -0,0 +1,141 @@
#pragma once
#include <algorithm>
#include <string>
#include <unordered_map>
#include <vector>
#include "Deer/Components.h"
#include "Deer/Log.h"
#include "entt/entt.hpp"
#ifdef DEER_RENDER
#include "DeerRender/Render/FrameBuffer.h"
#include "DeerRender/SceneCamera.h"
#endif
namespace Deer {
class Entity;
using EntityMap = std::unordered_map<uint32_t, Entity>;
class Environment {
public:
Environment();
~Environment();
void clear();
#ifdef DEER_RENDER
void render(SceneCamera& camera);
#endif
Entity& getEntity(uint32_t id);
Entity& createEntity(const std::string& name = std::string());
Entity createEmptyEntity();
// FEO
uint32_t tryGetMainCamera();
void setMainCamera(Entity& entity);
Entity& getRoot();
public:
entt::registry m_registry;
EntityMap m_entities;
uint32_t m_rootEntity = 0;
uint32_t m_mainCamera = 0;
private:
uint32_t m_idCreationOffset = 0;
inline uint32_t pullEntityID() {
m_idCreationOffset++;
return m_idCreationOffset;
}
friend class Entity;
};
class Entity {
public:
Entity() {}
static Entity nullEntity;
template <typename T, typename... Args>
T& addComponent(Args&&... args) const {
DEER_CORE_ASSERT(
!m_environment->m_registry.all_of<T>(m_entityHandle),
"Entity already have component {0}", typeid(T).name());
return m_environment->m_registry.emplace<T>(
m_entityHandle, std::forward<Args>(args)...);
}
template <typename T>
T& getComponent() const {
DEER_CORE_ASSERT(
m_environment->m_registry.all_of<T>(m_entityHandle),
"Entity has no component {0}", typeid(T).name());
return m_environment->m_registry.get<T>(m_entityHandle);
}
template <typename T>
bool hasComponent() const {
return m_environment->m_registry.all_of<T>(m_entityHandle);
}
template <typename T>
void removeComponent() const {
DEER_CORE_ASSERT(
m_environment->m_registry.all_of<T>(m_entityHandle),
"Entity does not have component {0}", typeid(T).name());
m_environment->m_registry.remove<T>(m_entityHandle);
}
Entity& duplicate();
void destroy();
Entity& getParent();
inline uint32_t getParentUID() { return m_parentUID; }
// TODO, enable transfer entitys from difrent enviroments
void setParent(Entity& parent);
bool isDescendant(Entity& parent);
uint32_t getParentUID() const { return m_parentUID; }
uint32_t getUID() const { return m_entityUID; }
Environment* getEnvironment() const { return m_environment; }
std::vector<uint32_t>& getChildren();
bool isRoot() { return m_isRoot; }
glm::mat4 getWorldMatrix();
glm::mat4 getRelativeMatrix();
void updateInternalVars();
inline bool isValid() const {
return m_entityUID != 0 && m_environment != nullptr &&
m_environment->m_registry.valid(m_entityHandle);
}
inline bool operator==(const Entity& b) const {
return m_environment == b.m_environment &&
m_entityUID == b.m_entityUID;
}
private:
Entity(entt::entity handle, Environment* scene);
bool removeChild(Entity& child);
entt::entity m_entityHandle = entt::null;
Environment* m_environment = nullptr;
uint32_t m_entityUID = 0;
uint32_t m_parentUID = 0;
bool m_isRoot = false;
friend class Environment;
friend class std::unordered_map<uint32_t, Entity>;
};
} // namespace Deer

66
Deer/Include/Deer/Log.h Executable file
View File

@ -0,0 +1,66 @@
#pragma once
#include "Deer/Memory.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/spdlog.h"
namespace spdlog {
class logger;
}
namespace Deer {
class Log {
public:
static void init();
static void shutdown();
static void coreTrace(const char* msg);
static inline Ref<spdlog::logger>& getCoreLogger() {
return coreLogger;
}
static inline Ref<spdlog::logger>& getClientLogger() {
return clientLogger;
}
static inline Ref<spdlog::logger>& getScriptLogger() {
return scriptLogger;
}
private:
static Ref<spdlog::logger> coreLogger;
static Ref<spdlog::logger> clientLogger;
static Ref<spdlog::logger> scriptLogger;
};
} // namespace Deer
#define DEER_CORE_TRACE(...) Deer::Log::getCoreLogger()->trace(__VA_ARGS__)
#define DEER_CORE_INFO(...) Deer::Log::getCoreLogger()->info(__VA_ARGS__)
#define DEER_CORE_WARN(...) Deer::Log::getCoreLogger()->warn(__VA_ARGS__)
#define DEER_CORE_ERROR(...) Deer::Log::getCoreLogger()->error(__VA_ARGS__)
#define DEER_SCRIPT_TRACE(...) Deer::Log::getScriptLogger()->trace(__VA_ARGS__)
#define DEER_SCRIPT_INFO(...) Deer::Log::getScriptLogger()->info(__VA_ARGS__)
#define DEER_SCRIPT_WARN(...) Deer::Log::getScriptLogger()->warn(__VA_ARGS__)
#define DEER_SCRIPT_ERROR(...) Deer::Log::getScriptLogger()->error(__VA_ARGS__)
#ifdef LINUX
#define DEER_CORE_ASSERT(condition, ...) \
if (!(condition)) { \
Deer::Log::getCoreLogger()->error(__VA_ARGS__); \
}
#define DEER_SCRIPT_ASSERT(condition, ...) \
if (!(condition)) { \
Deer::Log::getScriptLogger()->error(__VA_ARGS__); \
}
#endif
#ifdef WINDOWS
#define DEER_CORE_ASSERT(condition, ...) \
if (!(condition)) { \
Deer::Log::getCoreLogger()->error(__VA_ARGS__); \
__debugbreak(); \
}
#define DEER_SCRIPT_ASSERT(condition, ...) \
if (!(condition)) { \
Deer::Log::getScriptLogger()->error(__VA_ARGS__); \
__debugbreak(); \
}
#endif

View File

@ -0,0 +1,18 @@
#pragma once
#include <memory>
namespace Deer {
template <typename T>
using Scope = std::unique_ptr<T>;
template <typename T, typename... Args>
constexpr auto MakeScope(Args&&... args) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
template <typename T>
using Ref = std::shared_ptr<T>;
template <typename T>
using WeakRef = std::weak_ptr<T>;
} // namespace Deer

8
Deer/Include/Deer/Path.h Executable file
View File

@ -0,0 +1,8 @@
#pragma once
#include <filesystem>
namespace Deer {
using Path = std::filesystem::path;
Path toLowerCasePath(const Path& inputPath);
} // namespace Deer

61
Deer/Include/Deer/Scene.h Executable file
View File

@ -0,0 +1,61 @@
#pragma once
#include "Deer/DataStore.h"
#include "Deer/Memory.h"
#include "Deer/Path.h"
#ifdef DEER_RENDER
#include "DeerRender/GizmoRenderer.h"
#endif
#include <string>
#include <vector>
namespace Deer {
class VoxelWorld;
class VoxelWorldProps;
class Environment;
class Scene {
public:
Scene();
void createVoxelWorld(const VoxelWorldProps&);
void deleteVoxelWorld();
void clear();
void beginExecution();
void updateInternalVars();
void endExecution();
public:
inline Ref<Environment>& getMainEnviroment() { return m_enviroment; }
inline Ref<VoxelWorld>& getVoxelWorld() { return m_voxelWorld; }
inline bool getExecutingState() { return m_isExecuting; }
private:
Ref<Environment> m_enviroment;
Ref<VoxelWorld> m_voxelWorld;
bool m_isExecuting = false;
#ifdef DEER_RENDER
public:
void render();
void render(SceneCamera);
inline GizmoRenderer& getMainGizmoRenderer() { return m_gizmoRenderer; }
private:
GizmoRenderer m_gizmoRenderer;
#endif
};
namespace SceneDataStore {
Scene loadScene(const Path& name);
void deleteSceneJson(const Path& name);
void exportSceneJson(Scene& scene, const Path& name);
void exportScenesBin();
void exportRuntimeScene(Scene& scene);
Scene importRuntimeScene();
} // namespace SceneDataStore
} // namespace Deer

View File

@ -0,0 +1,49 @@
#pragma once
#include <filesystem>
#include <string>
#include <unordered_map>
#include <vector>
#include "Deer/ComponentScript.h"
#include "Deer/Memory.h"
class asIScriptEngine;
class asIScriptModule;
class asIScriptContext;
class asIScriptFunction;
class asITypeInfo;
namespace Deer {
class Scene;
class Entity;
class ComponentScript;
using ComponentScriptMap = std::unordered_map<std::string, ComponentScript>;
namespace ScriptEngine {
extern asIScriptContext* m_context;
extern bool m_isCompilationValid;
extern ComponentScriptMap m_componentScripts;
extern Scene* m_scene;
void compileScriptEngine(const std::filesystem::path& scriptPath);
void shutdownScriptEngine();
void beginExecutionContext(Scene* executingScene);
void endExecutionContext();
inline asIScriptContext* getExecutionContext() { return m_context; }
inline bool isCompilationValid() { return m_isCompilationValid; }
inline ComponentScriptMap& getComponentScripts() {
return m_componentScripts;
}
inline ComponentScript& getComponentScript(
const std::string& scriptID) {
return m_componentScripts[scriptID];
}
Ref<ComponentScriptInstance> createComponentScriptInstance(
const std::string& scriptID, Entity& scriptEntity);
} // namespace ScriptEngine
} // namespace Deer

295
Deer/Include/Deer/Voxel.h Executable file
View File

@ -0,0 +1,295 @@
// Structure definition for voxel and voxel manipulation
// copyright Copyright (c) 2025 Deer
#pragma once
#include <stdint.h>
#include <array>
#include <string>
#include <vector>
#include "Deer/Memory.h"
#ifdef DEER_RENDER
#include "DeerRender/VoxelAspect.h"
namespace Deer {
class Texture2D;
class Shader;
} // namespace Deer
#endif
#define VOXEL_INFO_TYPE_AIR "air"
#define VOXEL_INFO_TYPE_VOXEL "voxel"
#define VOXEL_INFO_TYPE_TRANSPARENT_VOXEL "transparentVoxel"
#define VOXEL_INFO_TYPE_CUSTOM "custom"
#define CHUNK_SIZE_X 32
#define CHUNK_SIZE_Y 32
#define CHUNK_SIZE_Z 32
#define CHUNK_SIZE(axis) \
((axis == 0) ? CHUNK_SIZE_X : (axis == 1) ? CHUNK_SIZE_Y : CHUNK_SIZE_Z)
#define LAYER_VOXELS CHUNK_SIZE_X* CHUNK_SIZE_Z
#define CHUNK_VOXELS CHUNK_SIZE_X* CHUNK_SIZE_Y* CHUNK_SIZE_Z
// TODO: Change this to be a inline function
#define VOXEL_POSITION(id) \
id.z + id.y* CHUNK_SIZE_Z + id.x* CHUNK_SIZE_Z* CHUNK_SIZE_Y
#define LAYER_VOXEL_POSITION(id) id.z + id.x* CHUNK_SIZE_Z
#define X_AXIS 0
#define Y_AXIS 1
#define Z_AXIS 2
// TODO: Change this to be a inline function
#define NORMAL_DIR(axis, normal) normalDirs[axis + normal * 3]
namespace Deer {
struct Voxel;
struct LayerVoxel;
extern Voxel nullVoxel;
extern Voxel emptyVoxel;
extern LayerVoxel nullLayerVoxel;
extern int normalDirs[3 * 6];
enum NormalDirection : uint8_t {
NORMAL_LEFT = 0,
NORMAL_RIGHT = 1,
NORMAL_DOWN = 2,
NORMAL_UP = 3,
NORMAL_BACK = 4,
NORMAL_FRONT = 5
};
enum class VoxelInfoType : uint8_t {
Air = 0,
Voxel = 1,
TransparentVoxel = 2,
Custom = 3
};
// Defines the general data of a voxel id stored in the array
// VoxelData::voxelsInfo
struct VoxelInfo {
std::string name;
VoxelInfoType type = VoxelInfoType::Air;
};
// Namespace to load and manage voxel data
namespace VoxelData {
// List of the voxels loaded with loadVoxelsData()
extern std::vector<VoxelInfo> voxelsInfo;
// Loads basic voxel data from folder DEER_VOXEL_DATA_PATH defined in
// DataStore.h
void loadVoxelsData();
void createExampleVoxelData();
int32_t getVoxelID(const std::string&);
#ifdef DEER_RENDER
// List of the voxels Aspect loaded with loadVoxelsAspect()
extern std::vector<VoxelAspect> voxelsAspect;
// Loads voxel aspect from folder DEER_VOXEL_ASPECT_PATH defined in
// DataStore.h
void loadVoxelsAspect();
void createExampleVoxelAspect();
// Generates the texture atlas that the voxels demanded from folder
// DEER_VOXEL_TEXTURE_PATH defined in DataStore.h Warning : This
// function must be called with a render context, otherwise this will
// crash
void generateTextureAtlas();
// Loads the shaders for rendering chunks from folder
// DEER_VOXEL_SHADER_PATH defined in DataStore.h
void loadVoxelsShaders();
// Returns with & height of the texture atlas generated
// Warning: If you call this before generate Texture Atlas the return
// value will be 0
int getVoxelTextureAtlasSize();
// Texture atlas created with generateTextureAtlas() call
// Warning: You must have called generateTextureAtlas() in order to work
Ref<Texture2D>& getVoxelColorTextureAtlas();
// Returns the shader created with loadVoxelsShaders()
// Warning: You must have called loadVoxelsShaders() in order to work
Ref<Shader>& getSolidVoxelShader();
#endif
} // namespace VoxelData
// Structure to define what a voxel inside a world must have
struct Voxel {
// Reference to the voxel id
uint16_t id = 0;
Voxel() = default;
Voxel(uint16_t _id) : id(_id) {}
inline bool operator==(const Voxel& b) const { return id == b.id; }
inline bool isVoxelType() const {
return VoxelData::voxelsInfo[id].type == VoxelInfoType::Voxel;
}
};
// Structure to define the general cordinates of a voxel in the world
struct VoxelCordinates {
union {
struct {
int32_t x, y, z;
};
std::array<int32_t, 3> data;
};
VoxelCordinates(int32_t _x = 0, int32_t _y = 0, int32_t _z = 0)
: x(_x), y(_y), z(_z) {}
inline int32_t& operator[](int id) { return data[id]; }
inline bool operator==(const VoxelCordinates& b) const {
return x == b.x && y == b.y && z == b.z;
}
inline bool isNull() const { return x < 0 || y < 0 || z < 0; }
inline void makeNull() { x = -1; }
};
// Stucture that defines the info of a layer voxel
struct LayerVoxel {
uint16_t height = 0;
#ifdef DEER_RENDER
uint16_t ambient_light_height = 0;
#endif
LayerVoxel() = default;
LayerVoxel(uint16_t _height) : height(_height) {}
};
// Returning info of a raycast
struct VoxelRayResult {
float distance = 0;
VoxelCordinates hitPos;
uint8_t face = 0;
};
// Coordinates of a chunk
struct ChunkID {
union {
struct {
uint16_t x;
uint16_t y;
uint16_t z;
};
std::array<uint16_t, 3> axis;
};
ChunkID(uint16_t _x = 0, uint16_t _y = 0, uint16_t _z = 0)
: x(_x), y(_y), z(_z) {}
inline bool operator==(const ChunkID& b) const {
return x == b.x && y == b.y && z == b.z;
}
inline uint16_t& operator[](size_t i) { return axis[i]; }
};
struct ChunkIDHash {
size_t operator()(const ChunkID& chunk) const {
size_t h1 = std::hash<uint16_t>{}(chunk.x);
size_t h2 = std::hash<uint16_t>{}(chunk.y);
size_t h3 = std::hash<uint16_t>{}(chunk.z);
size_t result = h1;
result = result * 31 + h2;
result = result * 31 + h3;
return result;
}
};
// Cordinates of a Layer
struct LayerID {
uint16_t x = 0;
uint16_t z = 0;
LayerID() = default;
LayerID(uint16_t _x, uint16_t _z) : x(_x), z(_z) {}
inline bool operator==(const LayerID& b) const {
return x == b.x && z == b.z;
}
};
// Coordinates of a layer voxel relative to the Layer Chunk
struct LayerVoxelID {
uint8_t x = 0;
uint8_t z = 0;
LayerVoxelID() = default;
LayerVoxelID(uint8_t _x, uint8_t _z = 0) : x(_x), z(_z) {}
};
// Coordinates of a voxel inside a Chunk
struct ChunkVoxelID {
union {
struct {
uint8_t x;
uint8_t y;
uint8_t z;
};
std::array<uint8_t, 3> axis;
};
ChunkVoxelID(uint8_t _x = 0, uint8_t _y = 0, uint8_t _z = 0)
: x(_x), y(_y), z(_z) {}
inline uint8_t& operator[](size_t i) { return axis[i]; }
};
// Extracts the chunk coordinaes and the chunk voxel coordinates from a
// world position
inline void extractChunkCordinates(uint32_t x, uint32_t y, uint32_t z,
ChunkID& _chunkID,
ChunkVoxelID& _chunkVoxelID) {
uint16_t posX = x;
uint16_t posY = y;
uint16_t posZ = z;
_chunkID.x = posX >> 5;
_chunkID.y = posY >> 5;
_chunkID.z = posZ >> 5;
_chunkVoxelID.x = posX & 31;
_chunkVoxelID.y = posY & 31;
_chunkVoxelID.z = posZ & 31;
}
// Extracts the chunk coordinaes and the chunk voxel chunk coordinates from
// a world position
inline void extractChunkCordinates(VoxelCordinates coords,
ChunkID& _chunkID,
ChunkVoxelID& _chunkVoxelID) {
uint16_t posX = coords.x;
uint16_t posY = coords.y;
uint16_t posZ = coords.z;
_chunkID.x = posX >> 5;
_chunkID.y = posY >> 5;
_chunkID.z = posZ >> 5;
_chunkVoxelID.x = posX & 31;
_chunkVoxelID.y = posY & 31;
_chunkVoxelID.z = posZ & 31;
}
// Extracts the layer chunk coordinaes and the layer chunk voxel coordinates
// from a world position
inline void extractLayerCordinates(uint32_t x, uint32_t z,
LayerID& _layerID,
LayerVoxelID& _layerVoxelID) {
uint16_t posX = x;
uint16_t posZ = z;
_layerID.x = posX >> 5;
_layerID.z = posZ >> 5;
_layerVoxelID.x = posX & 31;
_layerVoxelID.z = posZ & 31;
}
} // namespace Deer

229
Deer/Include/Deer/VoxelWorld.h Executable file
View File

@ -0,0 +1,229 @@
// copyright Copyright (c) 2025 Deer
#pragma once
#include <array>
#include "Deer/Memory.h"
#include "Deer/Voxel.h"
#ifdef DEER_RENDER
#include "DeerRender/LightVoxel.h"
#endif
#include "glm/glm.hpp"
namespace Deer {
class Chunk;
class Layer;
struct SceneCamera;
struct VoxelWorldProps;
struct VoxelWorldRenderData;
// Properties of a Voxel World
struct VoxelWorldProps {
union {
struct {
uint8_t chunkSizeX;
uint8_t chunkSizeY;
uint8_t chunkSizeZ;
};
std::array<uint8_t, 3> axis;
};
VoxelWorldProps() = default;
VoxelWorldProps(uint8_t _chunkSizeX, uint8_t _chunkSizeY,
uint8_t _chunkSizeZ)
: chunkSizeX(_chunkSizeX),
chunkSizeY(_chunkSizeY),
chunkSizeZ(_chunkSizeZ) {}
inline uint8_t &operator[](size_t i) { return axis[i]; }
// Returns the count of chunks
inline int getChunkCount() const {
return chunkSizeX * chunkSizeY * chunkSizeZ;
}
// Returns the count of layers
inline int getLayerCount() const { return chunkSizeX * chunkSizeZ; }
// Returns the internal id of a chunk relative to a Voxel World Props
// from a chunk id
inline int getWorldChunkID(ChunkID chunkID) const {
return chunkID.z + chunkID.y * chunkSizeZ +
chunkID.x * chunkSizeZ * chunkSizeY;
}
// Returns the internal id of a layer relative to a Voxel World Props
// from a Layer id
inline int getWorldLayerID(LayerID layerID) const {
return layerID.z + layerID.x * chunkSizeZ;
}
// Extracts the LayerID from a internal Layer id relative to Voxel World
// Props
inline LayerID getLayerID(int id) const {
LayerID l_id;
l_id.x = id / chunkSizeZ;
id -= l_id.x * chunkSizeZ;
l_id.z = id;
return l_id;
}
// Extracts the ChunkID from a internal Chunk id relative to Voxel World
// Props
inline ChunkID getChunkID(int id) const {
ChunkID c_id;
c_id.x = id / (chunkSizeZ * chunkSizeY);
id -= c_id.x * (chunkSizeZ * chunkSizeY);
c_id.y = id / chunkSizeZ;
id -= c_id.y * chunkSizeZ;
c_id.z = id;
return c_id;
}
// Checks if the Chunk id is inside the voxel World bounds
inline bool isValid(ChunkID chunkID) const {
return chunkID.x >= 0 && chunkID.x < chunkSizeX && chunkID.y >= 0 &&
chunkID.y < chunkSizeY && chunkID.z >= 0 &&
chunkID.z < chunkSizeZ;
}
// Checks if the Layer id is inside the voxel World bounds
inline bool isValid(LayerID layerID) const {
return layerID.x >= 0 && layerID.x < chunkSizeX && layerID.z >= 0 &&
layerID.z < chunkSizeZ;
}
// Returns the max amount of voxels in the Voxel World Props
inline int getMaxVoxelCount() const {
return getChunkCount() * CHUNK_VOXELS;
}
// Clamps the coordinates of a Voxel World Coordinates to be inside the
// voxel world props
inline void clampCordinates(VoxelCordinates &coords) const {
if (coords.x < 0)
coords.x = 0;
else if (coords.x >= chunkSizeX * CHUNK_SIZE_X)
coords.x = chunkSizeX * CHUNK_SIZE_X - 1;
if (coords.y < 0)
coords.y = 0;
else if (coords.y >= chunkSizeY * CHUNK_SIZE_Y)
coords.y = chunkSizeY * CHUNK_SIZE_Y - 1;
if (coords.z < 0)
coords.z = 0;
else if (coords.z >= chunkSizeZ * CHUNK_SIZE_Z)
coords.z = chunkSizeZ * CHUNK_SIZE_Z - 1;
}
// Takes 2 Voxel coordinates and outputs them in the same variables
// being the min with the min values and the max with the max This is
// useful for loops
inline void clampAndSetMinMax(VoxelCordinates &min,
VoxelCordinates &max) const {
VoxelCordinates a_cache = min;
VoxelCordinates b_cache = max;
for (int x = 0; x < 3; x++) {
if (a_cache[x] > b_cache[x]) {
max[x] = a_cache[x];
min[x] = b_cache[x];
} else {
min[x] = a_cache[x];
max[x] = b_cache[x];
}
}
clampCordinates(min);
clampCordinates(max);
}
};
// Class to manage the voxels
class VoxelWorld {
public:
// Warning: Do not change the voxel data content since that could make
// undefined behaviour
VoxelWorld(const VoxelWorldProps &props);
// This class can not be copyed
// TODO: Make a function to duplicate a voxel World
VoxelWorld(const VoxelWorld &) = delete;
VoxelWorld &operator=(VoxelWorld &) = delete;
// Returns the voxel in a voxel coordinates
Voxel readVoxel(VoxelCordinates);
// Sets the voxel in the coordinates to the value
void setVoxel(VoxelCordinates, Voxel value);
// Fills a space with the voxel value inside the 2 coordinates
// Note that you don't have to give then ordeered by min and max
void fillVoxels(VoxelCordinates, VoxelCordinates, Voxel value);
// Remplaces the ref voxel with the value of a space inside the 2
// coordinates
// Note that you don't have to give then ordeered by min and max
void remplaceVoxels(VoxelCordinates, VoxelCordinates, Voxel ref,
Voxel value);
// Returns the layer data of a woorld coordinates
// Note out of bounds will return a default Layer Voxel
LayerVoxel readLayerVoxel(int x, int z);
// Calculates the max height of a layer in a space
// Note out of bounds will return a 0 of height
// Tip: this will calculate, you should use the cached height in a layer
// voxel
uint16_t calculateLayerVoxelHeight(int x, int z);
// Raycast a ray from a source and dir
VoxelRayResult rayCast(glm::vec3 position, glm::vec3 dir,
float maxDistance = 10.0f);
// Raycast a ray from a source and dir ignoring if the ray stats inside
// a voxel
VoxelRayResult rayCast_editor(glm::vec3 position, glm::vec3 dir,
float maxDistance = 10.0f);
// Returns the voxel world props used in the voxel world
// Note that you can't change the world size unless you create a new
// Voxel World
inline const VoxelWorldProps &getVoxelWorldProps() const {
return m_worldProps;
}
#ifdef DEER_RENDER
public:
// Renders the current voxel world with a specified scene camera
void render(const SceneCamera &);
// Generates the next chunk mesh
void bakeNextChunk();
// Light data
VoxelLight readLight(VoxelCordinates);
VoxelLight &modLight(VoxelCordinates);
private:
Scope<VoxelWorldRenderData> m_renderData;
// Chunk vertex creation
void genSolidVoxel(ChunkID chunkID, ChunkVoxelID chunkVoxelID);
// --- Light propagation ---
// Warning: This function is private and needs to have min and max
// clamped and in order
void bakeVoxelLight(VoxelCordinates min, VoxelCordinates max);
void bakeVoxelLightFromPoint(VoxelCordinates);
void bakeAmbientLight(int minX, int maxX, int minZ, int maxZ);
void bakeAmbientLightFromPoint(int x, int z);
void resolveNextAmbientLightPropagation();
void resolveNextVoxelLightPropagation();
#endif
private:
VoxelWorldProps m_worldProps;
Scope<Chunk[]> m_chunks;
Scope<Layer[]> m_layers;
LayerVoxel &modLayerVoxel(int x, int z);
};
} // namespace Deer

View File

@ -0,0 +1,47 @@
#pragma once
#include "Deer/Components.h"
#include "DeerRender/Render/VertexArray.h"
#include "DeerRender/Render/Shader.h"
#define GLM_ENABLE_EXPERIMENTAL
#include "glm/gtc/quaternion.hpp"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#define MAX_TEXTURE_BINDINGS 4
namespace Deer {
struct MeshRenderComponent {
MeshRenderComponent() = default;
MeshRenderComponent(const MeshRenderComponent&) = default;
MeshRenderComponent(uint32_t _mesh, uint32_t _shader) : shaderAssetID(_shader), meshAssetID(_mesh) { }
uint32_t shaderAssetID = 0;
uint32_t meshAssetID = 0;
};
struct TextureBindingComponent {
TextureBindingComponent() {
for (int x = 0; x < MAX_TEXTURE_BINDINGS; x++) {
textureAssetID[x] = 0;
textureBindID[x] = 0;
}
}
TextureBindingComponent(const TextureBindingComponent&) = default;
uint32_t textureAssetID[MAX_TEXTURE_BINDINGS];
unsigned char textureBindID[MAX_TEXTURE_BINDINGS];
};
struct CameraComponent {
CameraComponent() = default;
CameraComponent(const CameraComponent&) = default;
CameraComponent(float _fov, float _aspect, float _nearZ, float _farZ) : fov(_fov), aspect(_aspect), nearZ(_nearZ), farZ(_farZ) { }
inline glm::mat4 getMatrix() const { return glm::perspective(fov, aspect, nearZ, farZ); }
float fov = glm::radians(50.0f), aspect = 16 / 9, nearZ = 0.1f, farZ = 1000;
};
}

View File

@ -0,0 +1,36 @@
#pragma once
#include "DeerRender/Events/Event.h"
#include <sstream>
namespace Deer {
class WindowResizeEvent : public Event {
public:
WindowResizeEvent(unsigned int width, unsigned int height)
: m_Width(width), m_Height(height) {}
inline unsigned int getWidth() const { return m_Width; }
inline unsigned int getHeight() const { return m_Height; }
std::string toString() const override {
std::stringstream ss;
ss << "WindowResizeEvent: " << m_Width << ", " << m_Height;
return ss.str();
}
EVENT_CLASS_TYPE(WindowResize)
EVENT_CLASS_CATEGORY(EventCategoryApplication)
private:
unsigned int m_Width, m_Height;
};
class WindowCloseEvent : public Event {
public:
WindowCloseEvent() = default;
std::string toString() const override {
return "Window close";
}
EVENT_CLASS_TYPE(WindowClose)
EVENT_CLASS_CATEGORY(EventCategoryApplication)
};
}

View File

@ -0,0 +1,68 @@
#pragma once
#include <string>
#include <functional>
namespace Deer {
enum class EventType
{
None = 0,
WindowClose, WindowMinimize, WindowResize, WindowFocus, WindowLostFocus, WindowMoved,
AppUpdate, AppRender,
KeyPressed, KeyReleased, KeyTyped,
MouseButtonPressed, MouseButtonReleased, MouseButtonDown, MouseMoved, MouseScrolled
};
enum EventCategory {
None = 0,
EventCategoryApplication = 1 << 0,
EventCategoryInput = 1 << 1,
EventCategoryKeyboard = 1 << 2,
EventCategoryMouse = 1 << 3,
EventCategoryMouseButton = 1 << 4
};
#define EVENT_CLASS_TYPE(type) static EventType getStaticType() { return EventType::type; }\
virtual EventType getEventType() const override { return getStaticType(); }\
virtual const char* getName() const override { return #type; }
#define EVENT_CLASS_CATEGORY(category) virtual int getCategoryFlags() const override { return category; }
class Event
{
public:
bool handled = false;
virtual ~Event() {}
virtual EventType getEventType() const = 0;
virtual const char* getName() const = 0;
virtual int getCategoryFlags() const = 0;
virtual std::string toString() const { return getName(); }
inline bool isInCategory(EventCategory category)
{
return getCategoryFlags() & category;
}
};
class EventDispatcher
{
template<typename T>
using EventFn = std::function<bool(T&)>;
public:
EventDispatcher(Event& event)
: m_event(event) {
}
template<typename T>
bool dispatch(EventFn<T> func) {
if (m_event.getEventType() == T::getStaticType() && !m_event.handled)
{
m_event.handled = func(*(T*)&m_event);
return true;
}
return false;
}
private:
Event& m_event;
};
}

View File

@ -0,0 +1,71 @@
#pragma once
#include "DeerRender/Events/Event.h"
#include <sstream>
namespace Deer {
class KeyEvent : public Event
{
public:
inline unsigned short getKeyCode() const { return m_KeyCode; }
EVENT_CLASS_CATEGORY(EventCategoryKeyboard | EventCategoryInput)
protected:
KeyEvent(unsigned short keycode)
: m_KeyCode(keycode) {}
unsigned short m_KeyCode;
};
class KeyPressedEvent : public KeyEvent
{
public:
KeyPressedEvent(unsigned int keycode, int repeatCount)
: KeyEvent(keycode), m_RepeatCount(repeatCount) {}
inline int getRepeatCount() const { return m_RepeatCount; }
std::string toString() const override
{
std::stringstream ss;
ss << "KeyPressedEvent: " << m_KeyCode << " (" << m_RepeatCount << " repeats)";
return ss.str();
}
EVENT_CLASS_TYPE(KeyPressed)
private:
int m_RepeatCount;
};
class KeyReleasedEvent : public KeyEvent
{
public:
KeyReleasedEvent(unsigned int keycode)
: KeyEvent(keycode) {}
std::string toString() const override
{
std::stringstream ss;
ss << "KeyReleasedEvent: " << m_KeyCode;
return ss.str();
}
EVENT_CLASS_TYPE(KeyReleased)
};
class KeyTypedEvent : public KeyEvent
{
public:
KeyTypedEvent(unsigned int keycode)
: KeyEvent(keycode) {}
std::string toString() const override
{
std::stringstream ss;
ss << "KeyTypedEvent: " << m_KeyCode;
return ss.str();
}
EVENT_CLASS_TYPE(KeyTyped)
};
}

View File

@ -0,0 +1,108 @@
#pragma once
#include "DeerRender/Events/Event.h"
#include <sstream>
namespace Deer {
class MouseMovedEvent : public Event
{
public:
MouseMovedEvent(float x, float y)
: m_MouseX(x), m_MouseY(y) {}
inline float getX() const { return m_MouseX; }
inline float getY() const { return m_MouseY; }
std::string toString() const override
{
std::stringstream ss;
ss << "MouseMovedEvent: " << m_MouseX << ", " << m_MouseY;
return ss.str();
}
EVENT_CLASS_TYPE(MouseMoved)
EVENT_CLASS_CATEGORY(EventCategoryMouse | EventCategoryInput)
private:
float m_MouseX, m_MouseY;
};
class MouseScrolledEvent : public Event
{
public:
MouseScrolledEvent(float xOffset, float yOffset)
: m_XOffset(xOffset), m_YOffset(yOffset) {}
inline float getXOffset() const { return m_XOffset; }
inline float getYOffset() const { return m_YOffset; }
std::string toString() const override
{
std::stringstream ss;
ss << "MouseScrolledEvent: " << getXOffset() << ", " << getYOffset();
return ss.str();
}
EVENT_CLASS_TYPE(MouseScrolled)
EVENT_CLASS_CATEGORY(EventCategoryMouse | EventCategoryInput)
private:
float m_XOffset, m_YOffset;
};
class MouseButtonEvent : public Event
{
public:
inline int getMouseButton() const { return m_Button; }
EVENT_CLASS_CATEGORY(EventCategoryMouse | EventCategoryInput)
protected:
MouseButtonEvent(int button)
: m_Button(button) {}
int m_Button;
};
class MouseButtonPressedEvent : public MouseButtonEvent
{
public:
MouseButtonPressedEvent(int button)
: MouseButtonEvent(button) {}
std::string toString() const override {
std::stringstream ss;
ss << "MouseButtonPressedEvent: " << m_Button;
return ss.str();
}
EVENT_CLASS_TYPE(MouseButtonPressed)
};
class MouseButtonReleasedEvent : public MouseButtonEvent
{
public:
MouseButtonReleasedEvent(int button)
: MouseButtonEvent(button) {}
std::string toString() const override {
std::stringstream ss;
ss << "MouseButtonReleasedEvent: " << m_Button;
return ss.str();
}
EVENT_CLASS_TYPE(MouseButtonReleased)
};
class MouseButtonDownEvent : public MouseButtonEvent
{
public:
MouseButtonDownEvent(int button)
: MouseButtonEvent(button) {
}
std::string toString() const override {
std::stringstream ss;
ss << "MouseButtonDownEvent: " << m_Button;
return ss.str();
}
EVENT_CLASS_TYPE(MouseButtonDown)
};
}

View File

@ -0,0 +1,33 @@
#pragma once
#include "glm/glm.hpp"
#include <vector>
#include <array>
#define GIZMO_DEPTH 8
namespace Deer {
struct SceneCamera;
struct GizmoFace {
glm::vec3 positions[4];
uint16_t textureID;
uint8_t face;
};
class GizmoRenderer {
public:
void drawLine(glm::vec3 a, glm::vec3 b, glm::vec3 color = glm::vec3(1.0f, 1.0f, 1.0f));
void drawVoxelLine(int x, int y, int z, glm::vec3 color = glm::vec3(1.0f, 1.0f, 1.0f));
void drawVoxelLineFace(int x, int y, int z, uint8_t face, glm::vec3 color = glm::vec3(1.0f, 1.0f, 1.0f));
void drawVoxelFace(int x, int y, int z, uint16_t voxelID, uint8_t face, uint8_t priority = 0);
void drawVoxelFaceInverted(int x, int y, int z, uint16_t voxelID, uint8_t face, uint8_t priority = 0);
void render(const SceneCamera& camera);
void refresh();
private:
std::vector<std::array<glm::vec3, 3>> m_lines;
std::array<std::vector<GizmoFace>, GIZMO_DEPTH> m_faces;
};
}

12
Deer/Include/DeerRender/Input.h Executable file
View File

@ -0,0 +1,12 @@
#pragma once
#include "Deer/Application.h"
#include "DeerRender/KeyCodes.h"
namespace Deer {
class Input {
public:
static bool isKeyPressed(unsigned int key);
static bool isMouseButtonPressed(int button);
static void getMousePos(float& x, float& y);
};
}

View File

@ -0,0 +1,138 @@
#pragma once
// From GLFW
#define DEER_KEY_SPACE 32
#define DEER_KEY_APOSTROPHE 39 /* ' */
#define DEER_KEY_COMMA 44 /* , */
#define DEER_KEY_MINUS 45 /* - */
#define DEER_KEY_PERIOD 46 /* . */
#define DEER_KEY_SLASH 47 /* / */
#define DEER_KEY_0 48
#define DEER_KEY_1 49
#define DEER_KEY_2 50
#define DEER_KEY_3 51
#define DEER_KEY_4 52
#define DEER_KEY_5 53
#define DEER_KEY_6 54
#define DEER_KEY_7 55
#define DEER_KEY_8 56
#define DEER_KEY_9 57
#define DEER_KEY_SEMICOLON 59 /* ; */
#define DEER_KEY_EQUAL 61 /* = */
#define DEER_KEY_A 65
#define DEER_KEY_B 66
#define DEER_KEY_C 67
#define DEER_KEY_D 68
#define DEER_KEY_E 69
#define DEER_KEY_F 70
#define DEER_KEY_G 71
#define DEER_KEY_H 72
#define DEER_KEY_I 73
#define DEER_KEY_J 74
#define DEER_KEY_K 75
#define DEER_KEY_L 76
#define DEER_KEY_M 77
#define DEER_KEY_N 78
#define DEER_KEY_O 79
#define DEER_KEY_P 80
#define DEER_KEY_Q 81
#define DEER_KEY_R 82
#define DEER_KEY_S 83
#define DEER_KEY_T 84
#define DEER_KEY_U 85
#define DEER_KEY_V 86
#define DEER_KEY_W 87
#define DEER_KEY_X 88
#define DEER_KEY_Y 89
#define DEER_KEY_Z 90
#define DEER_KEY_LEFT_BRACKET 91 /* [ */
#define DEER_KEY_BACKSLASH 92 /* \ */
#define DEER_KEY_RIGHT_BRACKET 93 /* ] */
#define DEER_KEY_GRAVE_ACCENT 96 /* ` */
#define DEER_KEY_WORLD_1 161 /* non-US #1 */
#define DEER_KEY_WORLD_2 162 /* non-US #2 */
/* Function keys */
#define DEER_KEY_ESCAPE 256
#define DEER_KEY_ENTER 257
#define DEER_KEY_TAB 258
#define DEER_KEY_BACKSPACE 259
#define DEER_KEY_INSERT 260
#define DEER_KEY_DELETE 261
#define DEER_KEY_RIGHT 262
#define DEER_KEY_LEFT 263
#define DEER_KEY_DOWN 264
#define DEER_KEY_UP 265
#define DEER_KEY_PAGE_UP 266
#define DEER_KEY_PAGE_DOWN 267
#define DEER_KEY_HOME 268
#define DEER_KEY_END 269
#define DEER_KEY_CAPS_LOCK 280
#define DEER_KEY_SCROLL_LOCK 281
#define DEER_KEY_NUM_LOCK 282
#define DEER_KEY_PRINT_SCREEN 283
#define DEER_KEY_PAUSE 284
#define DEER_KEY_F1 290
#define DEER_KEY_F2 291
#define DEER_KEY_F3 292
#define DEER_KEY_F4 293
#define DEER_KEY_F5 294
#define DEER_KEY_F6 295
#define DEER_KEY_F7 296
#define DEER_KEY_F8 297
#define DEER_KEY_F9 298
#define DEER_KEY_F10 299
#define DEER_KEY_F11 300
#define DEER_KEY_F12 301
#define DEER_KEY_F13 302
#define DEER_KEY_F14 303
#define DEER_KEY_F15 304
#define DEER_KEY_F16 305
#define DEER_KEY_F17 306
#define DEER_KEY_F18 307
#define DEER_KEY_F19 308
#define DEER_KEY_F20 309
#define DEER_KEY_F21 310
#define DEER_KEY_F22 311
#define DEER_KEY_F23 312
#define DEER_KEY_F24 313
#define DEER_KEY_F25 314
#define DEER_KEY_KP_0 320
#define DEER_KEY_KP_1 321
#define DEER_KEY_KP_2 322
#define DEER_KEY_KP_3 323
#define DEER_KEY_KP_4 324
#define DEER_KEY_KP_5 325
#define DEER_KEY_KP_6 326
#define DEER_KEY_KP_7 327
#define DEER_KEY_KP_8 328
#define DEER_KEY_KP_9 329
#define DEER_KEY_KP_DECIMAL 330
#define DEER_KEY_KP_DIVIDE 331
#define DEER_KEY_KP_MULTIPLY 332
#define DEER_KEY_KP_SUBTRACT 333
#define DEER_KEY_KP_ADD 334
#define DEER_KEY_KP_ENTER 335
#define DEER_KEY_KP_EQUAL 336
#define DEER_KEY_LEFT_SHIFT 340
#define DEER_KEY_LEFT_CONTROL 341
#define DEER_KEY_LEFT_ALT 342
#define DEER_KEY_LEFT_SUPER 343
#define DEER_KEY_RIGHT_SHIFT 344
#define DEER_KEY_RIGHT_CONTROL 345
#define DEER_KEY_RIGHT_ALT 346
#define DEER_KEY_RIGHT_SUPER 347
#define DEER_KEY_MENU 348
#define DEER_MOUSE_BUTTON_1 0
#define DEER_MOUSE_BUTTON_2 1
#define DEER_MOUSE_BUTTON_3 2
#define DEER_MOUSE_BUTTON_4 3
#define DEER_MOUSE_BUTTON_5 4
#define DEER_MOUSE_BUTTON_6 5
#define DEER_MOUSE_BUTTON_7 6
#define DEER_MOUSE_BUTTON_8 7
#define DEER_MOUSE_BUTTON_LAST DEER_MOUSE_BUTTON_8
#define DEER_MOUSE_BUTTON_LEFT DEER_MOUSE_BUTTON_1
#define DEER_MOUSE_BUTTON_RIGHT DEER_MOUSE_BUTTON_2
#define DEER_MOUSE_BUTTON_MIDDLE DEER_MOUSE_BUTTON_3

View File

@ -0,0 +1,33 @@
#pragma once
#include "Deer/Voxel.h"
#define LIGHT_PROPAGATION_COMPLEX_DIRS 18
#define LIGHT_PROPAGATION_COMPLEX_DIR(id, dir) lightPropagationComplexDir[id + dir * 2]
#define LIGHT_PROPAGATION_SIMPLE_FALL 16
#define LIGHT_PROPAGATION_COMPLEX_FALL 23
#define NORMAL_VERTEX_POS(axis, id, normal) normalFacePositions[axis + id * 3 + normal * 3 * 4]
#define VERTEX_UV(axis, id) uvFace[axis + id * 2]
#define AMBIENT_OCCLUSION_VERTEX(axis, id, vertex, normal) ambientOcclusionVertex[axis + id * 3 + vertex * 3 * 2 + normal * 3 * 2 * 4]
#define LAYER_CHECK_DIRS(axis, id) layerCheckDirections[axis + id * 2]
namespace Deer {
struct VoxelLight;
extern VoxelLight lightVoxel;
extern int lightPropagationComplexDir[12 * 2];
extern int normalFacePositions[3 * 4 * 6];
extern int uvFace[2 * 4];
// 6 Dirs * 4 vertices * 2 checks * 3 dirs
extern int ambientOcclusionVertex[6 * 4 * 2 * 3];
extern int layerCheckDirections[2 * 8];
struct VoxelLight {
uint8_t r_light;
uint8_t g_light;
uint8_t b_light;
uint8_t ambient_light;
VoxelLight(uint8_t _ambient_light = 0) : r_light(0), g_light(0), b_light(0), ambient_light(_ambient_light) { }
};
}

View File

@ -0,0 +1,97 @@
#pragma once
#include "Deer/Memory.h"
#include <vector>
#include <string>
namespace Deer {
enum class ShaderDataType {
None = 0,
FloatingPoint,
NormalizedFloatingPoint,
Integer
};
enum class DataType {
None = 0,
Half, Half2, Half3, Half4,
Float, Float2, Float3, Float4,
Byte, Byte2, Byte3, Byte4,
Short, Short2, Short3, Short4,
Int, Int2, Int3, Int4,
Unsigned_Byte, Unsigned_Byte2, Unsigned_Byte3, Unsigned_Byte4,
Unsigned_Short, Unsigned_Short2, Unsigned_Short3, Unsigned_Short4,
Unsigned_Int, Unsigned_Int2, Unsigned_Int3, Unsigned_Int4
};
enum class IndexDataType {
None = 0,
Unsigned_Byte,
Unsigned_Short,
Unsigned_Int,
};
unsigned int dataTypeSize(DataType type);
unsigned int dataTypeCount(DataType type);
unsigned int indexDataTypeSize(IndexDataType type);
struct BufferElement {
std::string name;
DataType type;
ShaderDataType shaderType;
int offset;
BufferElement(std::string _name, DataType _type , ShaderDataType _shaderType = ShaderDataType::FloatingPoint,
int _offset = -1)
: name(_name), type(_type), shaderType(_shaderType), offset(_offset){
}
};
class BufferLayout {
public:
BufferLayout() { }
BufferLayout(const std::initializer_list<BufferElement>& elements, int _stride = -1)
: m_bufferElements(elements), m_stride(_stride) {
calculateOffsetAndStride();
}
BufferLayout(const std::vector<BufferElement> elements)
: m_bufferElements(elements) {
calculateOffsetAndStride();
}
inline std::vector<BufferElement>::iterator begin() { return m_bufferElements.begin(); }
inline std::vector<BufferElement>::iterator end() { return m_bufferElements.end(); }
inline std::vector<BufferElement> getElements() { return m_bufferElements; }
inline int getStride() { return m_stride; }
private:
void calculateOffsetAndStride();
std::vector<BufferElement> m_bufferElements;
int m_stride;
};
class VertexBuffer {
public:
virtual ~VertexBuffer() = default;
virtual void bind() const = 0;
virtual void setLayout(BufferLayout& layout) = 0;
virtual BufferLayout& getLayout() = 0;
static Ref<VertexBuffer> create(void* data, unsigned int size);
};
class IndexBuffer {
public:
virtual ~IndexBuffer() = default;
virtual void bind() const = 0;
virtual unsigned int getCount() const = 0;
virtual IndexDataType getIndexDataType() const = 0;
static Ref<IndexBuffer> create(void* data, unsigned int size, IndexDataType indexDataType);
};
}

View File

@ -0,0 +1,35 @@
#pragma once
#define GLM_ENABLE_EXPERIMENTAL
#include "glm/glm.hpp"
#include "glm/gtc/quaternion.hpp"
namespace Deer {
class Camera {
public:
Camera(float aspect, float fov = 60, float nearZ = 0.1f, float farZ = 500);
void setPosition(const glm::vec3& position) { m_position = position; }
const glm::vec3& getPosition() { return m_position; }
void setRotation(const glm::quat& rotation) { m_rotation = rotation; }
const glm::quat& getRotation() { return m_rotation; }
void setAspect(float aspect) { m_aspect = aspect; }
float getAspect() { return m_aspect; }
void setFov(float fov) { m_fov = fov; }
float getFov() { return m_fov; }
void recalculateMatrices();
const glm::mat4& getProjectionMatrix() { return m_projectionMatrix; }
const glm::mat4& getViewMatrix() { return m_viewMatrix; }
private:
float m_fov, m_aspect, m_nearZ, m_farZ;
glm::mat4 m_projectionMatrix;
glm::mat4 m_viewMatrix;
glm::vec3 m_position;
glm::quat m_rotation;
};
}

View File

@ -0,0 +1,44 @@
#pragma once
#include "Deer/Log.h"
#include <vector>
#include <initializer_list>
namespace Deer {
enum class TextureBufferType {
RGBA8,
RED_INTEGER
};
struct FrameBufferSpecification {
unsigned int width, height;
unsigned int samples;
std::vector<TextureBufferType> frameBufferTextures;
bool swapChainTarget = false;
FrameBufferSpecification(unsigned int _width, unsigned int _height, std::initializer_list<TextureBufferType> _frameBufferTextures, unsigned int _samples = 1, bool _swapChainTarget = false)
: width(_width), height(_height), samples(_samples), frameBufferTextures(_frameBufferTextures), swapChainTarget(_swapChainTarget) {
}
};
class FrameBuffer {
public:
virtual ~FrameBuffer() = default;
virtual const FrameBufferSpecification& getSpecification() = 0;
virtual void bind() = 0;
virtual void unbind() = 0;
virtual void clear() = 0;
virtual void resize(unsigned int width, unsigned int height) = 0;
virtual unsigned int getTextureBufferID(int id = 0) = 0;
virtual void clearBuffer(unsigned int bufferId, void* data) = 0;
virtual int getTextureBufferPixel(int id, unsigned int x, unsigned int y) = 0;
static Ref<FrameBuffer> create(const FrameBufferSpecification& spec);
};
}

View File

@ -0,0 +1,26 @@
#pragma once
#include "Deer/Memory.h"
#include "glm/glm.hpp"
#include <string>
namespace Deer {
class Shader {
public:
virtual void bind() const = 0;
virtual ~Shader() = default;
virtual void uploadUniformFloat(const std::string& name, float value) = 0;
virtual void uploadUniformFloat2(const std::string& name, const glm::vec2& value) = 0;
virtual void uploadUniformFloat3(const std::string& name, const glm::vec3& value) = 0;
virtual void uploadUniformFloat4(const std::string& name, const glm::vec4& value) = 0;
virtual void uploadUniformInt(const std::string& name, int value) = 0;
virtual void uploadUniformMat4(const std::string& name, const glm::mat4 mat) = 0;
static Ref<Shader> create(const std::string& filePath);
static Ref<Shader> create(uint8_t* data, uint32_t size);
static Ref<Shader> create(const std::string& vertexSrc, const std::string& fragmentSrc);
};
}

View File

@ -0,0 +1,26 @@
#pragma once
#include "Deer/Memory.h"
#include <string>
namespace Deer {
class Texture {
public:
virtual ~Texture() = default;
virtual unsigned int getWidth() = 0;
virtual unsigned int getHeight() = 0;
virtual unsigned int getTextureID() = 0;
virtual void bind(unsigned int slot) = 0;
virtual void unbind(unsigned int slot) = 0;
};
class Texture2D : public Texture {
public:
static Ref<Texture2D> create(const std::string&);
static Ref<Texture2D> create(uint8_t* data, uint32_t size);
static Ref<Texture2D> create(uint8_t* data, uint32_t width, uint32_t height, int channels);
};
}

View File

@ -0,0 +1,27 @@
#pragma once
#include "DeerRender/Render/Buffer.h"
#include <memory>
#include <string>
namespace Deer {
class VertexArray;
using Mesh = VertexArray;
class VertexArray {
public:
virtual ~VertexArray() = default;
virtual void bind() const = 0;
virtual void unbind() const = 0;
virtual void addVertexBuffer(const Ref<VertexBuffer>& vertexBuffer) = 0;
virtual void setIndexBuffer(const Ref<IndexBuffer>& indexBuffer) = 0;
virtual const Ref<IndexBuffer>& getIndexBuffer() = 0;
static Ref<VertexArray> create();
static Ref<VertexArray> create(uint8_t* data, uint32_t size);
};
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "DeerRender/Components.h"
namespace Deer {
struct SceneCamera {
TransformComponent transform;
CameraComponent camera;
SceneCamera() {}
SceneCamera(TransformComponent _transform, CameraComponent _camera) : transform(_transform), camera(_camera) { }
};
}

View File

@ -0,0 +1,54 @@
/**
* @file VoxelAspect.h
* @author chewico@frostdeer.com
* @brief File to save the voxel aspect data
*
* @copyright Copyright (c) 2025
*/
#pragma once
#include "Deer/Voxel.h"
// TEMP
#define VOXEL_TEXTURE_SIZE_X 128
#define VOXEL_TEXTURE_SIZE_Y 128
namespace Deer {
struct VoxelTextureFaceDefinition {
std::string textureFaces[6];
inline std::string& operator[](size_t index) {
return textureFaces[index];
}
};
struct VoxelColorEmission {
uint8_t r_value = 0;
uint8_t g_value = 0;
uint8_t b_value = 0;
};
struct VoxelAspectDefinition {
std::string voxelName;
VoxelTextureFaceDefinition textureFaces;
VoxelColorEmission colorEmission;
VoxelAspectDefinition() = default;
};
struct VoxelAspect {
VoxelAspectDefinition definition;
uint16_t textureFacesIDs[6]{};
inline bool isLightSource() {
return definition.colorEmission.r_value || definition.colorEmission.g_value || definition.colorEmission.b_value;
}
/**
* @brief Get the texture id for the voxel face
*
* @param face face of the texture defined in the enum NormalDirection of Voxel.h
* @return uint16_t texture id in the texture atlas
*/
inline uint16_t getTextureID(uint8_t face) { return textureFacesIDs[face]; }
};
}

View File

@ -0,0 +1,46 @@
#pragma once
#include "DeerRender/Events/Event.h"
#include "Deer/Path.h"
#include <string>
#include <functional>
namespace Deer {
struct WindowProps {
std::string title;
unsigned int width;
unsigned int height;
WindowProps(const std::string& _title = "Deer Engine",
unsigned int _width = 1280,
unsigned int _height = 720)
: title(_title), width(_width), height(_height) {
}
};
class Window {
public:
virtual ~Window() = default;
virtual void initWindow() = 0;
virtual void onRender() = 0;
virtual void clear() = 0;
virtual void resolveEvents() = 0;
inline virtual int getWitdth() const = 0;
inline virtual int getHeight() const = 0;
virtual void setEventCallback(std::function<void(Event&)>) = 0;
virtual void setVSync(bool enabled) = 0;
inline virtual bool isVSync() const = 0;
virtual bool getKeyPressed(unsigned int key) = 0;
virtual bool getMouseButton(int button) = 0;
virtual void getMousePos(float& x, float& y) = 0;
virtual void initImGUI() = 0;
virtual Path folderDialog(const char*) = 0;
static Window* create(const WindowProps& props = WindowProps());
};
}

View File

@ -0,0 +1,6 @@
#include "Deer/Asset.h"
namespace Deer {
std::vector<Asset<void>> AssetManager::assets;
}

View File

@ -0,0 +1,140 @@
#include "Deer/Application.h"
#include "Deer/Log.h"
#ifdef DEER_RENDER
#include "DeerRender/Render/RenderCommand.h"
#include "DeerRender/Render/Render.h"
#include "DeerRender/Render/RenderUtils.h"
#include "DeerRender/ImGui/ImGuiLayer.h"
#include "imgui.h"
#include <functional>
#endif
namespace Deer {
namespace Core {
int argc;
char **argv;
}
Application* Application::s_application;
Application::Application() : m_running(false) {
#ifdef DEER_RENDER
m_window = Scope<Window>(Window::create(m_windowProps));
m_imGuiLayer = MakeScope<ImGuiLayer>();
#endif
}
Application::~Application() { }
#ifdef DEER_RENDER
Application::Application(const WindowProps& props)
: m_running(false), m_windowProps(props) {
m_window = Scope<Window>(Window::create(m_windowProps));
m_imGuiLayer = MakeScope<ImGuiLayer>();
}
void Application::initializeWindow() {
m_window->initWindow();
m_window->setEventCallback(std::bind(&Application::onEventCallback, this, std::placeholders::_1));
}
#endif
int Application::run() {
s_application = this;
m_running = true;
const double targetUpdateTime = 1.0 / 60.0; // Fixed 60 FPS update
double targetRenderTime = 1.0 / 120.0; // User-defined render FPS
auto previousTime = std::chrono::high_resolution_clock::now();
double accumulatedUpdateTime = 0.0;
double accumulatedRenderTime = 0.0;
int res = onPreInit();
if (res != 0)
return res;
#ifdef DEER_RENDER
initializeWindow();
m_imGuiLayer->onAttach();
RenderUtils::initializeRenderUtils();
RenderCommand::init();
#endif
res = onInit();
if (res != 0){
#ifdef DEER_RENDER
m_imGuiLayer->onDetach();
#endif
return res;
}
while (m_running) {
// Time handling
auto currentTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> deltaTime = currentTime - previousTime;
previousTime = currentTime;
accumulatedUpdateTime += deltaTime.count();
accumulatedRenderTime += deltaTime.count();
// Fixed Update loop (60 FPS)
while (accumulatedUpdateTime >= targetUpdateTime) {
Timestep timestep = (float)targetUpdateTime;
onUpdate(timestep);
accumulatedUpdateTime -= targetUpdateTime;
}
#ifdef DEER_RENDER
// Render loop (User-defined FPS)
if (accumulatedRenderTime >= targetRenderTime) {
RenderCommand::setClearColor({ 0.2f, 0.2f, 0.3f, 1.0f });
RenderCommand::clear();
Render::beginExecution();
onRender(Timestep((float)targetRenderTime));
Render::endExecution();
ImGuiIO& io = ImGui::GetIO();
io.DeltaTime = (float)targetRenderTime;
m_imGuiLayer->begin();
onImGUI();
m_imGuiLayer->end();
accumulatedRenderTime -= targetRenderTime;
m_window->onRender();
}
m_window->resolveEvents();
#endif
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
#ifdef DEER_RENDER
m_imGuiLayer->onDetach();
#endif
onShutdown();
return 0;
}
#ifdef DEER_RENDER
void Application::onEventCallback(Event& e) {
onEvent(e);
m_imGuiLayer->onEvent(e);
EventDispatcher dispatcher(e);
dispatcher.dispatch<WindowCloseEvent>(std::bind(&Application::onWindowClose, this, std::placeholders::_1));
}
bool Application::onWindowClose(WindowCloseEvent& e)
{
m_running = false;
return true;
}
#endif
}

33
Deer/src/Deer/Core/Log.cpp Executable file
View File

@ -0,0 +1,33 @@
#include "Deer/Log.h"
namespace Deer {
std::shared_ptr<spdlog::logger> Log::coreLogger;
std::shared_ptr<spdlog::logger> Log::clientLogger;
std::shared_ptr<spdlog::logger> Log::scriptLogger;
void Log::init()
{
spdlog::set_pattern("%^[%T] %n: %v%$");
coreLogger = spdlog::stdout_color_mt("Core");
clientLogger = spdlog::stdout_color_mt("Client");
scriptLogger = spdlog::stdout_color_mt("Script");
coreLogger->set_level(spdlog::level::level_enum::trace);
clientLogger->set_level(spdlog::level::level_enum::trace);
scriptLogger->set_level(spdlog::level::level_enum::trace);
}
void Log::shutdown() {
coreLogger.reset();
clientLogger.reset();
scriptLogger.reset();
spdlog::drop_all();
}
void Log::coreTrace(const char* msg)
{
//coreLogger->trace(msg);
}
}

View File

@ -0,0 +1,113 @@
#include "Deer/DataStore.h"
#include "Deer/Log.h"
#include "Deer/Path.h"
#include "cereal/cereal.hpp"
#include "cereal/types/unordered_map.hpp"
#include "cereal/archives/portable_binary.hpp"
#include "Deer/DataStore/DataStructure.h"
#include "Deer/DataStore/DataStructureSerialization.h"
#include <fstream>
#include <sstream>
#include <unordered_map>
#include <ostream>
#include <streambuf>
namespace Deer {
Path DataStore::rootPath;
void DataStore::deleteFile(const Path& path) {
Path filePath = rootPath / toLowerCasePath(path);
std::filesystem::remove(filePath);
}
uint8_t* DataStore::readFile(const Path& path, uint32_t* size) {
Path filePath = rootPath / toLowerCasePath(path);
std::ifstream file(filePath, std::ios::in | std::ios::binary);
if (!file) {
file.close();
return nullptr;
}
file.seekg(0, std::ios::end);
*size = (size_t)file.tellg();
file.seekg(0, std::ios::beg);
uint8_t* buffer = new uint8_t[*size];
if (!file.read(reinterpret_cast<char*>(buffer), *size)) {
DEER_CORE_ERROR("Failed to read file: {0}", filePath.generic_string().c_str());
delete[] buffer;
return nullptr;
}
file.close();
return buffer;
}
void DataStore::saveFile(const Path& path, uint8_t* data, uint32_t size) {
Path filePath = rootPath / toLowerCasePath(path);
std::filesystem::create_directories(filePath.parent_path());
std::ofstream file(filePath, std::ios::out | std::ios::binary);
DEER_CORE_ASSERT(file, "Error when writing file {0}", filePath.generic_string().c_str());
file.write(reinterpret_cast<const char*>(data), size);
}
void DataStore::compressFiles(std::vector<Path> files, const Path& path) {
std::unordered_map<Path, DataStructure> dataStructure;
std::vector<uint8_t> combinedData;
for (const Path& inputPath : files) {
uint32_t fileSize = 0;
uint8_t* fileData = readFile(inputPath, &fileSize);
uint32_t start = combinedData.size();
combinedData.insert(combinedData.end(), fileData, fileData + fileSize);
dataStructure[inputPath] = DataStructure{
.dataPath = inputPath,
.dataStart = start,
.dataSize = fileSize
};
delete[] fileData;
}
Path compressedPath = path;
compressedPath += ".deer";
Path metaPath = path;
metaPath += ".deer.meta";
std::stringstream buffer;
{
cereal::PortableBinaryOutputArchive archive(buffer);
archive(dataStructure);
}
saveFile(compressedPath, combinedData.data(), combinedData.size());
saveFile(metaPath, (uint8_t*)buffer.str().c_str(), buffer.str().size());
}
std::vector<Path> DataStore::getFiles(const Path& path, const std::string& extension) {
std::vector<Path> files;
Path lookPath = rootPath / path;
for (const auto& entry : std::filesystem::recursive_directory_iterator(lookPath)) {
if (std::filesystem::is_regular_file(entry) && entry.path().extension() == extension) {
files.push_back(entry.path().lexically_relative(rootPath));
}
}
return files;
}
void DataStore::createFolder(const Path& path) {
std::filesystem::create_directories(path);
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "Deer/Path.h"
namespace Deer {
struct DataStructure {
Path dataPath;
uint32_t dataStart;
uint32_t dataSize;
};
}

View File

@ -0,0 +1,36 @@
#pragma once
#include "Deer/DataStore/DataStructure.h"
#include "Deer/Path.h"
#include "cereal/cereal.hpp"
#include "cereal/types/string.hpp"
#include <string>
namespace cereal {
template<class Archive>
void save(Archive& archive,
std::filesystem::path const& path)
{
archive(path.generic_string());
}
template<class Archive>
void load(Archive& archive,
std::filesystem::path& path)
{
std::string _path;
archive(_path);
path = std::filesystem::path(_path);
}
}
namespace Deer {
template<class Archive>
void serialize(Archive& archive,
DataStructure& ds)
{
archive(ds.dataPath, ds.dataStart, ds.dataSize);
}
}

View File

@ -0,0 +1,9 @@
#include "Deer/Path.h"
#include <algorithm>
Deer::Path Deer::toLowerCasePath(const Path& inputPath) {
std::string pathStr = inputPath.generic_string();
std::transform(pathStr.begin(), pathStr.end(), pathStr.begin(),
[](unsigned char c) { return std::tolower(c); });
return Path(pathStr);
}

View File

@ -0,0 +1,14 @@
#include "Deer/Components.h"
#include "glm/gtc/matrix_transform.hpp"
#include "Deer/Log.h"
namespace Deer {
glm::mat4 TransformComponent::getMatrix() const{
glm::mat4 scaleMat = glm::scale(glm::mat4(1.0f), scale);
glm::mat4 roatationMat = glm::mat4(rotation);
glm::mat4 positionMat = glm::translate(glm::mat4(1.0f), position);
return positionMat * roatationMat * scaleMat;
}
}

127
Deer/src/Deer/Scene/Entity.cpp Executable file
View File

@ -0,0 +1,127 @@
#include "Deer/Enviroment.h"
#include "Deer/Components.h"
namespace Deer {
Entity Entity::nullEntity = Entity();
Entity::Entity(entt::entity handle, Environment* scene)
: m_entityHandle(handle), m_environment(scene) {
}
bool Entity::removeChild(Entity& child) {
DEER_CORE_ASSERT(child.m_environment == m_environment, "Can not remove childrens from diferent enviroments");
std::vector<uint32_t>& children = getChildren();
auto it = std::find(children.begin(), children.end(), child.m_entityUID);
if (it != children.end())
{
children.erase(it);
return true;
}
return false;
}
void Entity::setParent(Entity& parent) {
DEER_CORE_ASSERT(parent.m_environment == m_environment , "Can not set parent from diferent enviroments");
DEER_CORE_ASSERT(!isRoot(), "Can not set parent to root");
DEER_CORE_ASSERT(parent.isValid(), "Parent is not valid");
if (m_parentUID == parent.m_entityUID)
return;
if (m_parentUID != 0){
Entity& current_parent = getParent();
if (parent.isDescendant(*this)) {
return;
}
current_parent.removeChild(*this);
}
m_parentUID = parent.m_entityUID;
getComponent<RelationshipComponent>().parent_UID = parent.m_entityUID;
parent.getChildren().push_back(m_entityUID);
}
bool Entity::isDescendant(Entity& parent) {
if (m_entityUID == parent.m_entityUID)
return true;
if (isRoot())
return false;
return getParent().isDescendant(parent);
}
Entity& Entity::duplicate() {
Entity& creation = m_environment->createEntity(getComponent<TagComponent>().tag + "(d)");
creation.getComponent<TransformComponent>() = getComponent<TransformComponent>();
Entity& parent = m_environment->getEntity(m_parentUID);
creation.setParent(parent);
#ifdef DEER_RENDER
if (m_environment->m_registry.any_of<MeshRenderComponent>(m_entityHandle))
creation.addComponent<MeshRenderComponent>(getComponent<MeshRenderComponent>());
if (m_environment->m_registry.any_of<CameraComponent>(m_entityHandle))
creation.addComponent<CameraComponent>(getComponent<CameraComponent>());
if (m_environment->m_registry.any_of<TextureBindingComponent>(m_entityHandle))
creation.addComponent<TextureBindingComponent>(getComponent<TextureBindingComponent>());
#endif
return creation;
}
void Entity::destroy() {
DEER_CORE_ASSERT(!isRoot(), "Can not destroy the root");
getParent().removeChild(*this);
if (m_environment->tryGetMainCamera() == m_entityUID)
m_environment->setMainCamera(nullEntity);
for (auto entt : getChildren()) {
m_environment->getEntity(entt).destroy();
}
m_environment->m_registry.destroy(m_entityHandle);
m_entityHandle = entt::null;
m_environment = nullptr;
m_entityUID = 0;
}
Entity& Entity::getParent() {
return m_environment->getEntity(m_parentUID);
}
std::vector<uint32_t>& Entity::getChildren() {
return getComponent<RelationshipComponent>().children;
}
glm::mat4 Entity::getWorldMatrix() {
if (isRoot())
return glm::mat4(1.0f);
return getParent().getWorldMatrix() * getRelativeMatrix();
}
glm::mat4 Entity::getRelativeMatrix() {
return getComponent<TransformComponent>().getMatrix();
}
void Entity::updateInternalVars() {
TagComponent& tag = getComponent<TagComponent>();
RelationshipComponent& relation = getComponent<RelationshipComponent>();
m_entityUID = tag.entityUID;
m_parentUID = relation.parent_UID;
m_isRoot = relation.parent_UID == 0;
if (m_isRoot)
m_environment->m_rootEntity = tag.entityUID;
m_environment->m_entities[tag.entityUID] = *this;
}
}

View File

@ -0,0 +1,88 @@
#include "Deer/Enviroment.h"
#include "Deer/Application.h"
#include "Deer/Asset.h"
#include "Deer/Components.h"
#include "DeerRender/Render/Render.h"
#include "DeerRender/Render/RenderUtils.h"
#include "DeerRender/Render/Texture.h"
#include "Deer/Log.h"
namespace Deer {
Environment::Environment() {
clear();
}
Environment::~Environment() { }
void Environment::clear() {
// Clear all existing entities and map
m_registry.clear();
m_entities.clear();
m_rootEntity = 0;
m_mainCamera = 0;
m_idCreationOffset = 0;
m_rootEntity = pullEntityID();
entt::entity rootEntity = m_registry.create();
Entity entity = { rootEntity, this };
entity.addComponent<TagComponent>("root", m_rootEntity);
entity.addComponent<RelationshipComponent>();
entity.addComponent<TransformComponent>();
entity.m_isRoot = true;
entity.m_entityUID = m_rootEntity;
m_entities.insert({ m_rootEntity, entity });
}
Entity& Environment::getEntity(uint32_t id) {
DEER_CORE_ASSERT(m_entities.contains(id), "Entity id : {0} does not exist", id);
return m_entities[id];
}
Entity& Environment::createEntity(const std::string& name)
{
uint32_t id;
do {
id = pullEntityID();
} while (m_entities.contains(id));
entt::entity entityID = m_registry.create();
Entity entity = { entityID, this };
entity.addComponent<TagComponent>(name, id);
entity.addComponent<RelationshipComponent>();
entity.addComponent<TransformComponent>();
entity.m_entityUID = id;
entity.setParent(getRoot());
m_entities.insert({ id, entity });
return m_entities[id];
}
Entity Environment::createEmptyEntity() {
entt::entity entityID = m_registry.create();
Entity entity = { entityID, this };
return entity;
}
uint32_t Environment::tryGetMainCamera() {
return m_mainCamera;
}
void Environment::setMainCamera(Entity& entity) {
if (!entity.isValid())
m_mainCamera = 0;
m_mainCamera = entity.m_entityUID;
}
Entity& Environment::getRoot() {
return m_entities[m_rootEntity];
}
}

77
Deer/src/Deer/Scene/Scene.cpp Executable file
View File

@ -0,0 +1,77 @@
#include "Deer/Scene.h"
#include "Deer/Memory.h"
#include "Deer/Log.h"
#include "Deer/VoxelWorld.h"
#include "Deer/Enviroment.h"
#include "Deer/Voxels/Chunk.h"
#include "Deer/Voxels/Layer.h"
#include "Deer/ScriptEngine.h"
#include "Deer/Components.h"
#include "Deer/ComponentScript.h"
#ifdef DEER_RENDER
#include "DeerRender/Voxels/VoxelWorldRenderData.h"
#endif
namespace Deer {
Scene::Scene() {
m_enviroment = Ref<Environment>(new Environment());
}
void Scene::beginExecution() {
DEER_CORE_ASSERT(!m_isExecuting, "Deer scene is already executing");
m_isExecuting = true;
DEER_CORE_INFO("Executing Scene...");
ScriptEngine::beginExecutionContext(this);
// Instantiate all the scripts
auto view = m_enviroment->m_registry.view<ScriptComponent, TagComponent>();
for (auto& entID : view) {
auto& tagComponent = view.get<TagComponent>(entID);
auto& componentScript = view.get<ScriptComponent>(entID);
Entity& entity = m_enviroment->getEntity(tagComponent.entityUID);
componentScript.roeInstance = ScriptEngine::createComponentScriptInstance(componentScript.scriptID, entity);
componentScript.roeInstance->start();
}
}
void Scene::updateInternalVars() {
// Update all scripts
auto view = m_enviroment->m_registry.view<ScriptComponent>();
for (auto& entID : view) {
auto& componentScript = view.get<ScriptComponent>(entID);
componentScript.roeInstance->updateInternalVars();
}
}
void Scene::endExecution() {
DEER_CORE_ASSERT(m_isExecuting, "Deer scene is not executing");
m_isExecuting = false;
// Deatach all scripts
auto view = m_enviroment->m_registry.view<ScriptComponent>();
for (auto& entID : view) {
auto& componentScript = view.get<ScriptComponent>(entID);
componentScript.roeInstance.reset();
}
DEER_CORE_INFO("Stoping Scene...");
}
void Scene::createVoxelWorld(const VoxelWorldProps& props) {
m_voxelWorld = Ref<VoxelWorld>(new VoxelWorld(props));
}
void Scene::deleteVoxelWorld() {
m_voxelWorld.reset();
}
void Scene::clear() {
m_enviroment->clear();
m_voxelWorld.reset();
}
}

View File

@ -0,0 +1,130 @@
#include "Deer/Scene.h"
#include "cereal/archives/json.hpp"
#include "cereal/archives/portable_binary.hpp"
#include "Deer/Scene/Serialization/Serialization.h"
#include <vector>
#include <string>
#include <sstream>
namespace Deer {
Scene loadSceneJson(uint8_t* data, uint32_t size);
Scene loadSceneBin(uint8_t* data, uint32_t size);
Scene SceneDataStore::loadScene(const Path& name) {
Path realName;
realName = Path(DEER_SCENE_PATH) / (name.string() + ".dscn");
uint32_t size;
uint8_t* data = DataStore::readFile(realName, &size);
Scene scene_data;
scene_data = loadSceneJson(data, size);
delete[] data;
return scene_data;
}
Scene loadSceneJson(uint8_t* data, uint32_t size) {
std::string strData((char*)data, size);
std::istringstream stream(strData);
Scene scene;
{
cereal::JSONInputArchive archive(stream);
archive(cereal::make_nvp("scene", scene));
}
return scene;
}
Scene loadSceneBin(uint8_t* data, uint32_t size) {
std::string strData((char*)data, size);
std::istringstream stream(strData);
Scene scene;
{
cereal::PortableBinaryInputArchive archive(stream);
archive(cereal::make_nvp("scene", scene));
}
return scene;
}
void SceneDataStore::deleteSceneJson(const Path& name) {
DataStore::deleteFile((Path(DEER_SCENE_PATH) / (name.generic_string() + ".dscn")));
}
void SceneDataStore::exportSceneJson(Scene& scene, const Path& name) {
is_server_serialization = false;
std::stringstream output;
{
cereal::JSONOutputArchive archive(output);
archive(cereal::make_nvp("scene", scene));
}
Path savePath = Path(DEER_SCENE_PATH) / toLowerCasePath((name.generic_string() + ".dscn"));
std::string_view view = output.view();
DataStore::saveFile(savePath, (uint8_t*)view.data(), view.size());
}
void exportSceneBin(Scene& scene, const Path& name) {
is_server_serialization = false;
std::stringstream output;
{
cereal::PortableBinaryOutputArchive archive(output);
archive(cereal::make_nvp("scene", scene));
}
Path savePath = Path(DEER_BIN_PATH) / Path(DEER_SCENE_PATH) / toLowerCasePath((name.generic_string() + ".dbscn"));
std::string_view view = output.view();
DataStore::saveFile(savePath, (uint8_t*)view.data(), view.size());
}
void SceneDataStore::exportScenesBin() {
std::vector<Path> scenes = DataStore::getFiles(DEER_SCENE_PATH, ".dscn");
for (Path& scene_path : scenes) {
uint32_t size;
uint8_t* data = DataStore::readFile(scene_path, &size);
Scene scene_data = loadSceneJson(data, size);
delete[] data;
Path name = scene_path.lexically_relative(DEER_SCENE_PATH);
name = name.root_path() / name.stem();
exportSceneBin(scene_data, name);
}
}
void SceneDataStore::exportRuntimeScene(Scene& scene) {
std::stringstream output;
{
cereal::PortableBinaryOutputArchive archive(output);
archive(cereal::make_nvp("scene", scene));
}
Path savePath = Path(DEER_TEMP_PATH) / "scene_runtime.dbscn";
std::string_view view = output.view();
DataStore::saveFile(savePath, (uint8_t*)view.data(), view.size());
}
Scene SceneDataStore::importRuntimeScene() {
Path loadPath = Path(DEER_TEMP_PATH) / "scene_runtime.dbscn";
uint32_t size;
uint8_t* data = DataStore::readFile(loadPath, &size);
Scene scene_data = loadSceneBin(data, size);
delete[] data;
return scene_data;
}
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "Deer/Components.h"
namespace Deer {
// RELATIONSHIP COMPONENT
template<class Archive>
void serialize(Archive& archive,
RelationshipComponent& relationship) {
archive(cereal::make_nvp("parentUID", relationship.parent_UID));
archive(cereal::make_nvp("childrensUIDs", relationship.children));
}
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "Deer/Components.h"
namespace Deer {
// SCRIPT COMPONENT
template<class Archive>
void serialize(Archive& archive,
ScriptComponent& scriptComponent) {
archive(cereal::make_nvp("scriptID", scriptComponent.scriptID));
}
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "Deer/Components.h"
namespace Deer {
// TRANSFORM COMPONENT
template<class Archive>
void serialize(Archive& archive,
TransformComponent& transform) {
archive(cereal::make_nvp("position", transform.position));
archive(cereal::make_nvp("scale", transform.scale));
archive(cereal::make_nvp("rotation", transform.rotation));
}
}

View File

@ -0,0 +1,83 @@
#pragma once
#include "Deer/Components.h"
#include "Deer/Scene/Serialization/SerializationGlobalVars.h"
namespace Deer {
template <class Archive, typename T>
void saveComponent(Archive& archive, const std::string& componentName, Entity const& m_entity) {
bool hasComponent = m_entity.hasComponent<T>();
archive(cereal::make_nvp(("has_" + componentName).c_str(), hasComponent));
if (hasComponent) {
T& component = m_entity.getComponent<T>();
archive(cereal::make_nvp(componentName.c_str(), component));
}
}
template <class Archive, typename T>
void loadComponent(Archive& archive, const std::string& componentName, Entity const& m_entity) {
bool hasComponent;
archive(cereal::make_nvp(("has_" + componentName).c_str(), hasComponent));
if (hasComponent) {
T& component = m_entity.addComponent<T>();
archive(cereal::make_nvp(componentName.c_str(), component));
}
}
// ENTITY
template<class Archive>
void save(Archive& archive,
Entity const& m_entity) {
uint32_t id = m_entity.getUID();
TagComponent& name = m_entity.getComponent<TagComponent>();
archive(cereal::make_nvp("id", id));
archive(cereal::make_nvp("name", name.tag));
TransformComponent& transform = m_entity.getComponent<TransformComponent>();
archive(cereal::make_nvp("transform", transform));
RelationshipComponent& relation = m_entity.getComponent<RelationshipComponent>();
archive(cereal::make_nvp("relationship", relation));
#ifdef DEER_RENDER
if (!is_server_serialization) {
saveComponent<Archive, MeshRenderComponent>(archive, "meshRenderComponent", m_entity);
saveComponent<Archive, CameraComponent>(archive, "cameraComponent", m_entity);
saveComponent<Archive, TextureBindingComponent>(archive, "textureBindingComponent", m_entity);
}
#endif
saveComponent<Archive, ScriptComponent>(archive, "scriptComponent", m_entity);
}
template<class Archive>
void load(Archive& archive,
Entity& m_entity) {
uint32_t id;
std::string name;
archive(cereal::make_nvp("id", id));
archive(cereal::make_nvp("name", name));
m_entity.addComponent<TagComponent>() = TagComponent(name, id);
TransformComponent& transform = m_entity.addComponent<TransformComponent>();
archive(cereal::make_nvp("transform", transform));
RelationshipComponent& relationship = m_entity.addComponent<RelationshipComponent>();
archive(cereal::make_nvp("relationship", relationship));
#ifdef DEER_RENDER
if (!is_server_serialization) {
loadComponent<Archive, MeshRenderComponent>(archive, "meshRenderComponent", m_entity);
loadComponent<Archive, CameraComponent>(archive, "cameraComponent", m_entity);
loadComponent<Archive, TextureBindingComponent>(archive, "textureBindingComponent", m_entity);
}
#endif
loadComponent<Archive, ScriptComponent>(archive, "scriptComponent", m_entity);
m_entity.updateInternalVars();
}
}

View File

@ -0,0 +1,76 @@
#pragma once
#include "Deer/Enviroment.h"
#include <vector>
namespace Deer {
struct EntityVector_Environment {
std::vector<Entity> entities;
const Ref<Environment>& environment;
EntityVector_Environment(const Ref<Environment>& _environment)
: environment(_environment) { }
};
template<class Archive>
void save(Archive& archive,
Ref<Environment> const& m_environment) {
EntityVector_Environment entityMap(m_environment);
auto view = m_environment->m_registry.view<TagComponent>();
for (auto entity : view) {
TagComponent& tag = view.get<TagComponent>(entity);
entityMap.entities.push_back(m_environment->getEntity(tag.entityUID));
}
// Sort to avoid conflicts
std::sort(entityMap.entities.begin(), entityMap.entities.end(), [](Entity& a, Entity& b) {
return a.getUID() < b.getUID();
});
uint32_t mainCameraUID = m_environment->tryGetMainCamera();
archive(cereal::make_nvp("entities", entityMap));
archive(cereal::make_nvp("mainCameraUID", mainCameraUID));
}
template<class Archive>
void load(Archive& archive,
Ref<Environment>& m_environment) {
EntityVector_Environment entityMap(m_environment);
uint32_t mainCameraUID;
archive(cereal::make_nvp("entities", entityMap));
archive(cereal::make_nvp("mainCameraUID", mainCameraUID));
if (mainCameraUID != 0)
m_environment->setMainCamera(m_environment->getEntity(mainCameraUID));
}
// ENVIRONMENT
template<class Archive>
void save(Archive& archive,
EntityVector_Environment const& m_entityVector) {
archive(cereal::make_size_tag(static_cast<cereal::size_type>(m_entityVector.entities.size()))); // number of elements
for (auto&& v : m_entityVector.entities)
archive(v);
}
template<class Archive>
void load(Archive& archive,
EntityVector_Environment& m_entityVector) {
cereal::size_type size;
archive(cereal::make_size_tag(size));
m_entityVector.entities.resize(static_cast<std::size_t>(size));
for (auto& v : m_entityVector.entities) {
v = m_entityVector.environment->createEmptyEntity();
archive(v);
}
}
}

View File

@ -0,0 +1,16 @@
#pragma once
#define GLM_ENABLE_EXPERIMENTAL
#include "glm/gtc/quaternion.hpp"
namespace cereal {
template<class Archive>
void serialize(Archive& archive,
glm::quat& rot) {
archive(cereal::make_nvp("x", rot.x));
archive(cereal::make_nvp("y", rot.y));
archive(cereal::make_nvp("z", rot.z));
archive(cereal::make_nvp("w", rot.w));
}
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "Deer/Scene.h"
namespace Deer {
template<class Archive>
void serialize(Archive& archive,
Scene& m_scene) {
archive(cereal::make_nvp("main_environment", m_scene.getMainEnviroment()));
}
}

View File

@ -0,0 +1,4 @@
namespace Deer {
bool is_server_serialization = false;
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "Deer/Asset.h"
#include "cereal/cereal.hpp"
#include "cereal/types/vector.hpp"
#include "cereal/types/string.hpp"
// Serialization Vars
#include "Deer/Scene/Serialization/SerializationGlobalVars.h"
// GENERICS
#include "Deer/Scene/Serialization/Vec3Serialization.h"
#include "Deer/Scene/Serialization/QuatSerialization.h"
// SCENE SPECIFIC
#include "Deer/Scene/Serialization/SceneSerialization.h"
#include "Deer/Scene/Serialization/EnvironmentSerialization.h"
#include "Deer/Scene/Serialization/EntitySerialization.h"
// COMPONENTS SPECIFIC
#include "Deer/Scene/Serialization/Components/TransformComponentSerialization.h"
#include "Deer/Scene/Serialization/Components/RelationshipComponentSerialization.h"
#include "Deer/Scene/Serialization/Components/ScriptComponentSerialization.h"
// RENDER SPECIFIC
#ifdef DEER_RENDER
#include "DeerRender/Scene/Serialization/Components/CameraSerializationComponent.h"
#include "DeerRender/Scene/Serialization/Components/MeshRenderComponentSerialization.h"
#include "DeerRender/Scene/Serialization/Components/TextureBindingSerializationComponent.h"
#endif

View File

@ -0,0 +1,7 @@
#include "SerializationGlobalVars.h"
namespace Deer {
bool is_server_serialization = false;
}

View File

@ -0,0 +1,5 @@
#pragma once
namespace Deer {
extern bool is_server_serialization;
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "glm/glm.hpp"
namespace cereal {
template<class Archive>
void serialize(Archive& archive,
glm::vec3& vec3) {
archive(
cereal::make_nvp("x", vec3.x),
cereal::make_nvp("y", vec3.y),
cereal::make_nvp("z", vec3.z));
}
}

View File

@ -0,0 +1,41 @@
#include "Deer/ComponentScript.h"
#include "angelscript.h"
#include "Deer/Log.h"
#include "Deer/ScriptEngine.h"
#include "angelscript.h"
namespace Deer {
ComponentScript::ComponentScript(asITypeInfo* typeInfo)
: m_typeInfo(typeInfo) {
m_scriptID = m_typeInfo->GetName();
m_attributes = extractAttributes(typeInfo);
}
ComponentScriptInstance::~ComponentScriptInstance() {
m_object->Release();
}
void ComponentScriptInstance::updateInternalVars() {
if (!m_updateFunction)
return;
asIScriptContext* context = ScriptEngine::getExecutionContext();
context->Prepare(m_updateFunction);
context->SetObject(m_object);
context->Execute();
}
void ComponentScriptInstance::start() {
if (!m_startFuction)
return;
asIScriptContext* context = ScriptEngine::getExecutionContext();
context->Prepare(m_startFuction);
context->SetObject(m_object);
context->Execute();
}
}

View File

@ -0,0 +1,24 @@
#include "Deer/ComponentScript.h"
#include "Deer/Log.h"
#include "angelscript.h"
namespace Deer {
ScriptAttributeMap extractAttributes(asITypeInfo* typeInfo) {
ScriptAttributeMap m_attributes;
int atributes = typeInfo->GetPropertyCount();
for (int x = 0; x < atributes; x++) {
const char* name;
bool isPrivate;
int typeID;
int offset;
typeInfo->GetProperty(x, &name, &typeID, &isPrivate, nullptr, &offset);
ScriptAttribute attribute(name, typeID, isPrivate, offset, x);
m_attributes.insert({ std::string(name), attribute});
}
return m_attributes;
}
}

View File

@ -0,0 +1,157 @@
#include "Deer/ScriptEngine.h"
#include "Deer/Log.h"
#include "Deer/Enviroment.h"
#include "angelscript.h"
#include "scriptbuilder.h"
#include "scriptstdstring.h"
#include "scriptmath.h"
#include "ScriptEngineFunctions.h"
#include "Deer/ComponentScript.h"
#include "Deer/Scene.h"
#include <filesystem>
namespace fs = std::filesystem;
namespace Deer {
namespace ScriptEngine {
asIScriptEngine* m_scriptEngine;
asIScriptModule* m_scriptModule;
Scene* m_scene;
bool m_isCompilationValid = false;
asIScriptContext* m_context;
ComponentScriptMap m_componentScripts;
void loadModuleFolder(const std::filesystem::path& modulePath, const char* moduleName);
void registerBaseComponents();
}
void ScriptEngine::shutdownScriptEngine() {
m_componentScripts.clear();
}
void ScriptEngine::beginExecutionContext(Scene* executingScene) {
m_scene = executingScene;
m_context = m_scriptEngine->CreateContext();
}
void ScriptEngine::endExecutionContext() {
m_context->Release();
}
void ScriptEngine::compileScriptEngine(const std::filesystem::path& modulePath) {
m_scriptEngine = asCreateScriptEngine();
m_isCompilationValid = true;
registerBaseComponents();
loadModuleFolder(modulePath, "Deer");
m_scriptModule = m_scriptEngine->GetModule("Deer");
asITypeInfo* m_deerScript = m_scriptModule->GetTypeInfoByName("ComponentScript");
int classCount = m_scriptModule->GetObjectTypeCount();
for (int i = 0; i < classCount; i++) {
asITypeInfo* type = m_scriptModule->GetObjectTypeByIndex(i);
asITypeInfo* parent = type->GetBaseType();
std::string scriptID = type->GetName();
if (parent == m_deerScript) {
ComponentScript componentScript(type);
m_componentScripts.insert({ scriptID, componentScript });
}
}
}
Ref<ComponentScriptInstance> ScriptEngine::createComponentScriptInstance(const std::string& scriptID, Entity& scriptEntity) {
ComponentScript& script = getComponentScript(scriptID);
asITypeInfo* type = script.getTypeInfo();
ComponentScriptInstance* instance = new ComponentScriptInstance();
std::string factoryString(script.getName());
factoryString = factoryString + " @" + script.getName() + "()";
asIScriptFunction* function = type->GetFactoryByDecl(factoryString.c_str());
if (!function) {
DEER_SCRIPT_ERROR("Function constructor not found for class {0}", script.getName());
return nullptr;
}
int r = m_context->Prepare(function);
if (r < 0) {
DEER_SCRIPT_ERROR("Failed to prepare constructor context for class {0}", script.getName());
return nullptr;
}
r = m_context->Execute();
if (r < 0) {
DEER_SCRIPT_ERROR("Failed to execute constructor for class {0}", script.getName());
return nullptr;
}
asIScriptObject* obj = *(asIScriptObject**)m_context->GetAddressOfReturnValue();
obj->AddRef();
int entityPosition = script.getAttribute("entity").internalID;
unsigned int* entityValue = (unsigned int*)obj->GetAddressOfProperty(entityPosition);
*entityValue = scriptEntity.getUID();
asIScriptFunction* updateFunction = type->GetMethodByDecl("void update()");
asIScriptFunction* startFunction = type->GetMethodByDecl("void start()");
instance->m_updateFunction = updateFunction;
instance->m_startFuction = startFunction;
instance->m_object = obj;
return Ref<ComponentScriptInstance>(instance);
}
void ScriptEngine::loadModuleFolder(const std::filesystem::path& modulePath, const char* moduleName) {
CScriptBuilder builder;
int r = builder.StartNewModule(m_scriptEngine, moduleName);
DEER_SCRIPT_ASSERT(r >= 0, "Unrecoverable error while starting a new module. {0}", moduleName);
try {
DEER_CORE_INFO("=== Loading Scripts ===");
for (const auto& entry : fs::recursive_directory_iterator(modulePath)) {
if (fs::is_regular_file(entry) && entry.path().extension() == ".as") {
r = builder.AddSectionFromFile(entry.path().generic_string().c_str());
DEER_SCRIPT_ASSERT(r >= 0, "Please correct the errors in the script and try again. {0}", entry.path().generic_string().c_str());
DEER_CORE_TRACE(" {0}", entry.path().filename().string().c_str());
}
}
} catch (const fs::filesystem_error& e) {
DEER_CORE_ERROR("Error while loading scripts, error: {0}", e.what());
}
r = builder.BuildModule();
if (r < 0) {
DEER_SCRIPT_INFO("Please correct the errors in the script and try again.");
m_isCompilationValid = false;
}
}
void ScriptEngine::registerBaseComponents() {
RegisterStdString(m_scriptEngine);
RegisterScriptMath(m_scriptEngine);
// Regist data types
registerEntity(m_scriptEngine);
registerVec3(m_scriptEngine);
// Regist functions
registerDeerFunctions(m_scriptEngine);
registerInputFunctions(m_scriptEngine);
registerEntityTransformFunctions(m_scriptEngine);
}
}

View File

@ -0,0 +1,201 @@
#include "ScriptEngineFunctions.h"
#include "Deer/Scene.h"
#include "Deer/Enviroment.h"
#include "Deer/ScriptEngine.h"
#include "angelscript.h"
#include "Deer/Log.h"
#include "DeerRender/Input.h"
#include "glm/glm.hpp"
namespace Deer {
void messageCallback(const asSMessageInfo* msg, void* param) {
if (msg->type == asMSGTYPE_WARNING) {
DEER_SCRIPT_WARN("({0} {1}) : {2} {3}", msg->row, msg->col, msg->message, msg->section);
}
else if (msg->type == asMSGTYPE_ERROR) {
DEER_SCRIPT_ERROR("({0} {1}) : {2} {3}", msg->row, msg->col, msg->message, msg->section);
}
else if (msg->type == asMSGTYPE_INFORMATION) {
DEER_SCRIPT_INFO("({0} {1}) : {2} {3}", msg->row, msg->col, msg->message, msg->section);
}
else {
DEER_SCRIPT_WARN("({0} {1}) : {2} {3}", msg->row, msg->col, msg->message, msg->section);
}
}
void print(std::string& msg) {
DEER_SCRIPT_INFO(msg.c_str());
}
glm::vec3 getEntityPosition(uint32_t& entityUID) {
if (entityUID == 0 || entityUID == 1) {
DEER_SCRIPT_ERROR("Entity is not invalid");
return glm::vec3();
}
Ref<Environment>& m_environment = ScriptEngine::m_scene->getMainEnviroment();
Entity& entt = m_environment->getEntity(entityUID);
return entt.getComponent<TransformComponent>().position;
}
void setEntityPosition(glm::vec3 position, uint32_t& entityUID) {
if (entityUID == 0 || entityUID == 1) {
DEER_SCRIPT_ERROR("Entity is not invalid");
return;
}
Ref<Environment>& m_environment = ScriptEngine::m_scene->getMainEnviroment();
Entity& entt = m_environment->getEntity(entityUID);
entt.getComponent<TransformComponent>().position = position;
}
glm::vec3 getEntityScale(uint32_t& entityUID) {
if (entityUID == 0 || entityUID == 1) {
DEER_SCRIPT_ERROR("Entity is not invalid");
return glm::vec3();
}
Ref<Environment>& m_environment = ScriptEngine::m_scene->getMainEnviroment();
Entity& entt = m_environment->getEntity(entityUID);
return entt.getComponent<TransformComponent>().scale;
}
void setEntityScale(glm::vec3 scale, uint32_t& entityUID) {
if (entityUID == 0 || entityUID == 1) {
DEER_SCRIPT_ERROR("Entity is not invalid");
return;
}
Ref<Environment>& m_environment = ScriptEngine::m_scene->getMainEnviroment();
Entity& entt = m_environment->getEntity(entityUID);
entt.getComponent<TransformComponent>().scale = scale;
}
uint32_t getEntityParent(uint32_t& entityUID) {
if (entityUID == 0 || entityUID == 1) {
DEER_SCRIPT_ERROR("Entity is not invalid");
return 0;
}
Ref<Environment>& m_environment = ScriptEngine::m_scene->getMainEnviroment();
Entity& entt = m_environment->getEntity(entityUID);
return entt.getParentUID();
}
bool isEntityValid(uint32_t& entityUID) {
if (entityUID == 0 || entityUID == 1)
return false;
Ref<Environment>& m_environment = ScriptEngine::m_scene->getMainEnviroment();
Entity& entt = m_environment->getEntity(entityUID);
return entt.isValid();
}
void registerVec3(asIScriptEngine* engine) {
engine->RegisterObjectType("Vec3", sizeof(glm::vec3), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits<glm::vec3>() | asOBJ_APP_CLASS_ALLFLOATS);
engine->RegisterObjectBehaviour("Vec3", asBEHAVE_CONSTRUCT, "void f()", asFUNCTIONPR([](void* memory) {
new (memory) glm::vec3();
}, (void*), void), asCALL_CDECL_OBJLAST);
engine->RegisterObjectBehaviour("Vec3", asBEHAVE_CONSTRUCT, "void f(float, float = 0, float = 0)", asFUNCTIONPR([](float x, float y, float z, void* memory) {
new (memory) glm::vec3(x, y, z);
}, (float, float, float, void*), void), asCALL_CDECL_OBJLAST);
engine->RegisterObjectProperty("Vec3", "float x", asOFFSET(glm::vec3, x));
engine->RegisterObjectProperty("Vec3", "float y", asOFFSET(glm::vec3, y));
engine->RegisterObjectProperty("Vec3", "float z", asOFFSET(glm::vec3, z));
engine->RegisterObjectMethod("Vec3", "Vec3 opAdd(const Vec3 &in) const",
asFUNCTIONPR([](const glm::vec3& a, const glm::vec3& b) -> glm::vec3 {
return a + b;
}, (const glm::vec3&, const glm::vec3&), glm::vec3), asCALL_CDECL_OBJFIRST);
engine->RegisterObjectMethod("Vec3", "Vec3 opSub(const Vec3 &in) const",
asFUNCTIONPR([](const glm::vec3& a, const glm::vec3& b) {
return a - b;
}, (const glm::vec3&, const glm::vec3&), glm::vec3), asCALL_CDECL_OBJFIRST);
engine->RegisterObjectMethod("Vec3", "Vec3 opMul(float) const",
asFUNCTIONPR([](const glm::vec3& a, float scalar) {
return a * scalar;
}, (const glm::vec3&, float), glm::vec3), asCALL_CDECL_OBJFIRST);
engine->RegisterObjectMethod("Vec3", "Vec3 opDiv(float) const",
asFUNCTIONPR([](const glm::vec3& a, float scalar) {
return a / scalar;
}, (const glm::vec3&, float), glm::vec3), asCALL_CDECL_OBJFIRST);
engine->RegisterObjectMethod("Vec3", "Vec3 normalize() const",
asFUNCTIONPR(glm::normalize, (const glm::vec3&), glm::vec3), asCALL_CDECL_OBJFIRST);
engine->RegisterObjectMethod("Vec3", "float getMagnitude() const",
asFUNCTIONPR(glm::length, (const glm::vec3&), float), asCALL_CDECL_OBJFIRST);
engine->RegisterObjectMethod("Vec3", "Vec3 opNeg() const",
asFUNCTIONPR([](const glm::vec3& a) {
return -a;
}, (const glm::vec3&), glm::vec3), asCALL_CDECL_OBJFIRST);
engine->RegisterObjectMethod("Vec3", "bool opEquals(const Vec3 &in) const",
asFUNCTIONPR([](const glm::vec3& a, const glm::vec3& b) {
return a == b;
}, (const glm::vec3&, const glm::vec3&), bool), asCALL_CDECL_OBJFIRST);
engine->RegisterObjectMethod("Vec3", "string opImplConv() const",
asFUNCTIONPR([](const glm::vec3& a) {
// Example string conversion using glm (you may need to format it)
char buffer[64];
snprintf(buffer, sizeof(buffer), "(%.2f, %.2f, %.2f)", a.x, a.y, a.z);
return std::string(buffer);
}, (const glm::vec3&), std::string), asCALL_CDECL_OBJFIRST);
}
void registerEntity(asIScriptEngine* engine) {
engine->RegisterObjectType("Entity", sizeof(unsigned int), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_PRIMITIVE);
engine->RegisterObjectProperty("Entity", "uint uint32_t", 0);
engine->RegisterObjectMethod("Entity", "Entity getParent()", asFUNCTION(Deer::getEntityParent), asCALL_CDECL_OBJLAST);
engine->RegisterObjectMethod("Entity", "bool isValid()", asFUNCTION(Deer::isEntityValid), asCALL_CDECL_OBJLAST);
engine->RegisterGlobalFunction("Entity getEntity(uint)", asFUNCTIONPR([](uint32_t id) {
return id;
}, (uint32_t), uint32_t), asCALL_CDECL);
}
void registerDeerFunctions(asIScriptEngine* scriptEngine) {
int r = scriptEngine->SetMessageCallback(asFUNCTION(Deer::messageCallback), 0, asCALL_CDECL);
DEER_SCRIPT_ASSERT(r >= 0, "Error in seting up angel script");
r = scriptEngine->RegisterGlobalFunction("void print(const string &in)", asFUNCTION(Deer::print), asCALL_CDECL);
DEER_SCRIPT_ASSERT(r >= 0, "Error in seting up void print(const string &in)");
}
void registerInputFunctions(asIScriptEngine* scriptEngine) {
int r = scriptEngine->RegisterGlobalFunction("bool isKeyPressed(int)", asFUNCTION(Deer::Input::isKeyPressed), asCALL_CDECL);
DEER_SCRIPT_ASSERT(r >= 0, "Error in seting up bool isKeyPressed(int)");
}
void registerEntityTransformFunctions(asIScriptEngine* scriptEngine) {
int r = scriptEngine->RegisterObjectMethod("Entity", "Vec3 getPosition()", asFUNCTION(Deer::getEntityPosition), asCALL_CDECL_OBJLAST);
DEER_SCRIPT_ASSERT(r >= 0, "Error in seting up Vec3 getPosition()");
scriptEngine->RegisterObjectMethod("Entity", "void setPosition(Vec3)", asFUNCTION(Deer::setEntityPosition), asCALL_CDECL_OBJLAST);
DEER_SCRIPT_ASSERT(r >= 0, "Error in seting up void setPosition(Vec3)");
r = scriptEngine->RegisterObjectMethod("Entity", "Vec3 getScale()", asFUNCTION(Deer::getEntityScale), asCALL_CDECL_OBJLAST);
DEER_SCRIPT_ASSERT(r >= 0, "Error in seting up Vec3 getScale()");
scriptEngine->RegisterObjectMethod("Entity", "void setScale(Vec3)", asFUNCTION(Deer::setEntityScale), asCALL_CDECL_OBJLAST);
DEER_SCRIPT_ASSERT(r >= 0, "Error in seting up void setScale(Vec3)");
}
}

View File

@ -0,0 +1,32 @@
#pragma once
#include <string>
#include "glm/glm.hpp"
struct asSMessageInfo;
class asIScriptEngine;
namespace Deer {
void messageCallback(const asSMessageInfo* msg, void* param);
void print(std::string& msg);
// Entity Transformations -------------
glm::vec3 getEntityPosition(uint32_t& entityUID);
void setEntityPosition(glm::vec3 position, uint32_t& entityUID);
glm::vec3 getEntityScale(uint32_t& entityUID);
void setEntityScale(glm::vec3 scale, uint32_t& entityUID);
// Entity Transformations -------------
// Entity Relationship ------------
uint32_t getEntityParent(uint32_t& entityUID);
bool isEntityValid(uint32_t& entityUID);
// Entity Relationship ------------
void registerVec3(asIScriptEngine* engine);
void registerEntity(asIScriptEngine* engine);
void registerDeerFunctions(asIScriptEngine* scriptEngine);
void registerInputFunctions(asIScriptEngine* scriptEngine);
void registerEntityTransformFunctions(asIScriptEngine* scriptEngine);
}

21
Deer/src/Deer/Voxels/Chunk.cpp Executable file
View File

@ -0,0 +1,21 @@
#include "Chunk.h"
namespace Deer {
Chunk::~Chunk() {
if (m_voxels) {
delete[] m_voxels;
#ifdef DEER_RENDER
delete[] m_lightInfo;
#endif
}
}
void Chunk::loadVoxels() {
if (!m_voxels) {
m_voxels = new Voxel[CHUNK_VOXELS]();
#ifdef DEER_RENDER
m_lightInfo = new VoxelLight[CHUNK_VOXELS]();
#endif
}
}
}

136
Deer/src/Deer/Voxels/Chunk.h Executable file
View File

@ -0,0 +1,136 @@
#pragma once
#include "Deer/Voxel.h"
#ifdef DEER_RENDER
#include "DeerRender/LightVoxel.h"
#include "DeerRender/VoxelAspect.h"
#include <vector>
#endif
#include <array>
namespace Deer {
class Chunk {
public:
Chunk() = default;
~Chunk();
inline Voxel readVoxel(ChunkVoxelID id) {
if (m_voxels)
return m_voxels[VOXEL_POSITION(id)];
return emptyVoxel;
}
inline Voxel& modVoxel(ChunkVoxelID id) {
if (!m_voxels)
loadVoxels();
return m_voxels[VOXEL_POSITION(id)];
}
inline void fillVoxels(ChunkVoxelID min, ChunkVoxelID max, Voxel info) {
if (!m_voxels)
loadVoxels();
ChunkVoxelID voxelID;
for (voxelID.x = min.x; voxelID.x <= max.x; voxelID.x++) {
for (voxelID.y = min.y; voxelID.y <= max.y; voxelID.y++) {
for (voxelID.z = min.z; voxelID.z <= max.x; voxelID.z++) {
m_voxels[VOXEL_POSITION(voxelID)] = info;
}
}
}
}
inline void remplaceVoxels(ChunkVoxelID min, ChunkVoxelID max, Voxel ref, Voxel value) {
if (!m_voxels)
loadVoxels();
ChunkVoxelID voxelID;
for (voxelID.x = min.x; voxelID.x <= max.x; voxelID.x++) {
for (voxelID.y = min.y; voxelID.y <= max.y; voxelID.y++) {
for (voxelID.z = min.z; voxelID.z <= max.z; voxelID.z++) {
Voxel& currentVoxel = m_voxels[VOXEL_POSITION(voxelID)];
if (currentVoxel.id == ref.id)
currentVoxel = value;
}
}
}
}
inline uint8_t calculateLayerVoxelHeight(LayerVoxelID layerVoxelID) {
if (!m_voxels)
return 0;
ChunkVoxelID voxelID(layerVoxelID.x, CHUNK_SIZE_Y - 1, layerVoxelID.z);
for (int y = CHUNK_SIZE_Y - 1; y >= 0; y--) {
voxelID.y = y;
if (m_voxels[VOXEL_POSITION(voxelID)].id != 0)
return voxelID.y + 1;
}
return 0;
}
private:
Voxel* m_voxels = nullptr;
void loadVoxels();
#ifdef DEER_RENDER
public:
inline VoxelLight readLight(ChunkVoxelID id) {
if (m_voxels)
return m_lightInfo[VOXEL_POSITION(id)];
return VoxelLight();
}
inline VoxelLight& modLight(ChunkVoxelID id) {
if (!m_voxels)
loadVoxels();
return m_lightInfo[VOXEL_POSITION(id)];
}
inline void clearVoxelLight(ChunkVoxelID min, ChunkVoxelID max) {
ChunkVoxelID voxelID;
for (voxelID.x = min.x; voxelID.x <= max.x; voxelID.x++){
for (voxelID.y = min.y; voxelID.y <= max.y; voxelID.y++){
for (voxelID.z = min.z; voxelID.z <= max.z; voxelID.z++){
m_lightInfo[VOXEL_POSITION(voxelID)].b_light = 0;
m_lightInfo[VOXEL_POSITION(voxelID)].r_light = 0;
m_lightInfo[VOXEL_POSITION(voxelID)].g_light = 0;
}
}
}
}
// This function is the same as clear Voxel Light but it also checks if there is a source of light
inline void clearVoxelLightAndSaveSources(ChunkVoxelID min, ChunkVoxelID max, ChunkID chunkID, std::vector<VoxelCordinates>& sources) {
if (!m_voxels)
return;
ChunkVoxelID voxelID;
for (voxelID.x = min.x; voxelID.x <= max.x; voxelID.x++){
for (voxelID.y = min.y; voxelID.y <= max.y; voxelID.y++){
for (voxelID.z = min.z; voxelID.z <= max.z; voxelID.z++){
Voxel voxel = m_voxels[VOXEL_POSITION(voxelID)];
VoxelAspect& voxelAspect = VoxelData::voxelsAspect[voxel.id];
if (voxelAspect.isLightSource()) {
sources.push_back(VoxelCordinates(
voxelID.x + chunkID.x * CHUNK_SIZE_X,
voxelID.y + chunkID.y * CHUNK_SIZE_Y,
voxelID.z + chunkID.z * CHUNK_SIZE_Z
));
}
m_lightInfo[VOXEL_POSITION(voxelID)].b_light = 0;
m_lightInfo[VOXEL_POSITION(voxelID)].r_light = 0;
m_lightInfo[VOXEL_POSITION(voxelID)].g_light = 0;
}
}
}
}
private:
VoxelLight* m_lightInfo = nullptr;
#endif
};
}

12
Deer/src/Deer/Voxels/Layer.cpp Executable file
View File

@ -0,0 +1,12 @@
#include "Layer.h"
namespace Deer {
Layer::~Layer() {
if (m_layerInfo)
delete[] m_layerInfo;
}
void Layer::loadData() {
m_layerInfo = new LayerVoxel[LAYER_VOXELS]();
}
}

40
Deer/src/Deer/Voxels/Layer.h Executable file
View File

@ -0,0 +1,40 @@
#pragma once
#include "Deer/Voxel.h"
namespace Deer {
class Layer {
public:
Layer() = default;
~Layer();
inline LayerVoxel readLayerVoxel(LayerVoxelID id) {
if (!m_layerInfo)
return LayerVoxel();
return m_layerInfo[LAYER_VOXEL_POSITION(id)];
}
inline LayerVoxel& modLayerVoxel(LayerVoxelID id) {
if (!m_layerInfo)
loadData();
return m_layerInfo[LAYER_VOXEL_POSITION(id)];
}
inline void fillVoxelLayerMaxHeight(LayerVoxelID min, LayerVoxelID max, uint8_t maxHeight) {
if (!m_layerInfo)
loadData();
LayerVoxelID layerVoxelID;
for (layerVoxelID.x = min.x; layerVoxelID.x <= max.x; layerVoxelID.x++) {
for (layerVoxelID.z = min.x; layerVoxelID.z <= max.z; layerVoxelID.z++) {
int id = LAYER_VOXEL_POSITION(layerVoxelID);
if (m_layerInfo[id].height <= maxHeight)
m_layerInfo[id].height = maxHeight + 1;
}
}
}
private:
void loadData();
LayerVoxel* m_layerInfo = nullptr;
};
}

View File

@ -0,0 +1,56 @@
#pragma once
#include "Deer/Voxel.h"
#include "Deer/Log.h"
#include "cereal/cereal.hpp"
#include "cereal/types/string.hpp"
namespace Deer{
template<class Archive>
void save(Archive & archive, VoxelInfo const & block) {
archive(cereal::make_nvp("name", block.name));
// To avoid breaking things we set it up to Air
const char* blockTypeChar = VOXEL_INFO_TYPE_AIR;
switch (block.type)
{
case VoxelInfoType::Air :
blockTypeChar = VOXEL_INFO_TYPE_AIR;
break;
case VoxelInfoType::Voxel :
blockTypeChar = VOXEL_INFO_TYPE_VOXEL;
break;
case VoxelInfoType::TransparentVoxel :
blockTypeChar = VOXEL_INFO_TYPE_TRANSPARENT_VOXEL;
break;
case VoxelInfoType::Custom :
blockTypeChar = VOXEL_INFO_TYPE_CUSTOM;
break;
}
std::string blockTypeString(blockTypeChar);
archive(cereal::make_nvp("type", blockTypeString));
}
template<class Archive>
void load(Archive & archive, VoxelInfo & block) {archive(cereal::make_nvp("name", block.name));
std::string blockTypeString;
archive(cereal::make_nvp("name", block.name));
archive(cereal::make_nvp("type", blockTypeString));
if (blockTypeString == VOXEL_INFO_TYPE_AIR)
block.type = VoxelInfoType::Air;
else if (blockTypeString == VOXEL_INFO_TYPE_VOXEL)
block.type = VoxelInfoType::Voxel;
else if (blockTypeString == VOXEL_INFO_TYPE_TRANSPARENT_VOXEL)
block.type = VoxelInfoType::TransparentVoxel;
else if (blockTypeString == VOXEL_INFO_TYPE_CUSTOM)
block.type = VoxelInfoType::Custom;
else {
block.type = VoxelInfoType::Air;
DEER_CORE_ERROR("Failed to resolve voxel type for {0}, unknown type : {1}",
block.name.c_str(), blockTypeString.c_str());
}
}
}

19
Deer/src/Deer/Voxels/Voxel.cpp Executable file
View File

@ -0,0 +1,19 @@
#include "Deer/Voxel.h"
namespace Deer {
// This means the voxel is null
Voxel nullVoxel(65535);
Voxel emptyVoxel;
LayerVoxel nullLayerVoxel(65535);
int normalDirs[3 * 6] = {
-1, 0, 0,
1, 0, 0,
0, -1, 0,
0, 1, 0,
0, 0, -1,
0, 0, 1
};
}

View File

@ -0,0 +1,89 @@
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
#include "Deer/DataStore.h"
#include "Deer/Log.h"
#include "Deer/Voxel.h"
#include "Deer/Voxels/Serialization/VoxelInfoSerialization.h"
#include "cereal/archives/json.hpp"
namespace Deer {
namespace VoxelData {
std::vector<VoxelInfo> voxelsInfo;
std::unordered_map<std::string, uint32_t> blockIDMap;
} // namespace VoxelData
int32_t VoxelData::getVoxelID(const std::string& name) {
if (blockIDMap.contains(name)) return blockIDMap[name];
DEER_CORE_WARN("Voxel Info {0} Not Found!", name.c_str());
return -1;
}
void VoxelData::loadVoxelsData() {
voxelsInfo.clear();
blockIDMap.clear();
VoxelInfo airVoxelInfo;
airVoxelInfo.name = VOXEL_INFO_TYPE_AIR;
voxelsInfo.push_back(airVoxelInfo);
blockIDMap[VOXEL_INFO_TYPE_AIR] = 0;
std::vector<Path> voxelsData;
voxelsData = DataStore::getFiles(DEER_VOXEL_DATA_PATH, ".voxel");
DEER_CORE_INFO("=== Loading voxels ===");
DEER_CORE_TRACE(" default - air");
for (Path& voxel : voxelsData) {
VoxelInfo voxelData;
uint32_t dataSize;
uint8_t* data = DataStore::readFile(voxel, &dataSize);
std::string dataString((char*)data, dataSize);
std::istringstream dataInputStream(dataString);
{
cereal::JSONInputArchive archive(dataInputStream);
archive(cereal::make_nvp("voxel", voxelData));
}
if (voxelData.name.empty()) {
DEER_CORE_ERROR("{0} has an empty name",
voxel.generic_string().c_str());
continue;
}
if (blockIDMap.contains(voxelData.name)) {
DEER_CORE_ERROR("{0} with name {1} has dupplicated name id",
voxel.generic_string().c_str(),
voxelData.name.c_str());
continue;
}
DEER_CORE_TRACE(" {0} - {1}",
voxel.filename().generic_string().c_str(),
voxelData.name);
uint32_t id = voxelsInfo.size();
voxelsInfo.push_back(voxelData);
blockIDMap[voxelData.name] = id;
delete data;
}
}
void VoxelData::createExampleVoxelData() {
VoxelInfo block;
std::stringstream data;
{
cereal::JSONOutputArchive archive(data);
archive(cereal::make_nvp("voxel", block));
}
DataStore::saveFile(Path(DEER_VOXEL_PATH) / "voxel.example",
(uint8_t*)(data.str().c_str()), data.str().size());
}
} // namespace Deer

View File

@ -0,0 +1,43 @@
#include "Deer/VoxelWorld.h"
#include "Deer/Log.h"
#include "Deer/Voxels/Chunk.h"
#include "Deer/Voxels/Layer.h"
#ifdef DEER_RENDER
#include "DeerRender/Voxels/VoxelWorldRenderData.h"
#endif
#include <math.h>
#include <cmath>
#include <vector>
namespace Deer {
VoxelWorld::VoxelWorld(const VoxelWorldProps& props)
: m_worldProps(props) {
m_chunks = MakeScope<Chunk[]>(m_worldProps.getChunkCount());
m_layers = MakeScope<Layer[]>(m_worldProps.getLayerCount());
#ifdef DEER_RENDER
m_renderData = MakeScope<VoxelWorldRenderData>(m_worldProps.getChunkCount());
#endif
}
uint16_t VoxelWorld::calculateLayerVoxelHeight(int x, int z) {
LayerVoxelID layerVoxelID;
LayerID layerID;
extractLayerCordinates(x, z, layerID, layerVoxelID);
ChunkID chunkID(layerID.x, 0, layerID.z);
for (int y = m_worldProps.chunkSizeY - 1; y >= 0; y--) {
chunkID.y = y;
Chunk& chunk = m_chunks[m_worldProps.getWorldChunkID(chunkID)];
uint8_t chunkVoxelHeight = chunk.calculateLayerVoxelHeight(layerVoxelID);
if (chunkVoxelHeight != 0) {
return chunkVoxelHeight + chunkID.y * CHUNK_SIZE_Y;
}
}
return 0;
}
}

View File

@ -0,0 +1,154 @@
#include "Deer/VoxelWorld.h"
#include "Deer/Log.h"
#include "Deer/Voxels/Chunk.h"
#include "Deer/Voxels/Layer.h"
#include <math.h>
#include <cmath>
#include <vector>
namespace Deer {
VoxelRayResult VoxelWorld::rayCast(glm::vec3 position, glm::vec3 dir, float maxDistance) {
VoxelRayResult result;
result.hitPos.x = (int32_t)std::floor(position.x);
result.hitPos.y = (int32_t)std::floor(position.y);
result.hitPos.z = (int32_t)std::floor(position.z);
result.distance = 0;
if (dir.x == 0 && dir.y == 0 && dir.z == 0) {
return result;
}
dir = glm::normalize(dir);
glm::vec3 stepAxis = glm::vec3(maxDistance, maxDistance, maxDistance);
glm::vec3 distanceAxis = glm::vec3(maxDistance, maxDistance, maxDistance);
int8_t directionAxis[3] = { 1, 1, 1 };
for (int i = 0; i < 3; i++) {
if (dir[i] < 0) {
stepAxis[i] = -1.0f / dir[i];
directionAxis[i] = -1;
distanceAxis[i] = stepAxis[i] * ((float)position[i] - (float)(&result.hitPos.x)[i]);
}
else if (dir[i] > 0) {
stepAxis[i] = 1.0f / dir[i];
distanceAxis[i] = stepAxis[i] * (1 - (float)position[i] + (float)(&result.hitPos.x)[i]);
}
}
while (result.distance < maxDistance) {
float minDistance = distanceAxis[0];
for (int i = 1; i < 3; i++) {
if (distanceAxis[i] < minDistance)
minDistance = distanceAxis[i];
}
result.distance = minDistance;
if (result.distance > maxDistance)
break;
for (int i = 0; i < 3; i++) {
if (minDistance == distanceAxis[i]) {
result.hitPos[i] += directionAxis[i];
distanceAxis[i] = minDistance + stepAxis[i];
Voxel hitVoxel = readVoxel(result.hitPos);
if (hitVoxel == nullVoxel)
continue;
if (hitVoxel != 0) {
result.face = i * 2;
if (directionAxis[i] == -1)
result.face++;
return result;
}
}
}
}
result.distance = maxDistance;
return result;
}
VoxelRayResult VoxelWorld::rayCast_editor(glm::vec3 position, glm::vec3 dir, float maxDistance) {
VoxelRayResult result;
result.hitPos.x = (int32_t)std::floor(position.x);
result.hitPos.y = (int32_t)std::floor(position.y);
result.hitPos.z = (int32_t)std::floor(position.z);
result.distance = 0;
if (dir.x == 0 && dir.y == 0 && dir.z == 0) {
return result;
}
dir = glm::normalize(dir);
glm::vec3 stepAxis = glm::vec3(maxDistance, maxDistance, maxDistance);
glm::vec3 distanceAxis = glm::vec3(maxDistance, maxDistance, maxDistance);
int8_t directionAxis[3] = { 1, 1, 1 };
for (int i = 0; i < 3; i++) {
if (dir[i] < 0) {
stepAxis[i] = -1.0f / dir[i];
directionAxis[i] = -1;
distanceAxis[i] = stepAxis[i] * ((float)position[i] - (float)result.hitPos[i]);
}
else if (dir[i] > 0) {
stepAxis[i] = 1.0f / dir[i];
distanceAxis[i] = stepAxis[i] * (1 - (float)position[i] + (float)result.hitPos[i]);
}
}
Voxel hitVoxel = readVoxel(result.hitPos);
bool has_exit_inner_walls = hitVoxel.id == 0;
while (result.distance < maxDistance) {
float minDistance = distanceAxis[0];
for (int i = 1; i < 3; i++) {
if (distanceAxis[i] < minDistance)
minDistance = distanceAxis[i];
}
result.distance = minDistance;
if (result.distance > maxDistance)
break;
for (int i = 0; i < 3; i++) {
if (minDistance == distanceAxis[i]) {
result.hitPos[i] += directionAxis[i];
distanceAxis[i] = minDistance + stepAxis[i];
Voxel hitVoxel = readVoxel(result.hitPos);
if (hitVoxel.id == 0) {
if (has_exit_inner_walls && result.hitPos.y == -1 && directionAxis[1] == -1 && i == 1) {
result.face = NORMAL_UP;
return result;
}
has_exit_inner_walls = true;
} else if (hitVoxel.id != 0 && has_exit_inner_walls) {
result.face = i * 2;
if (directionAxis[i] == -1)
result.face++;
return result;
}
}
}
}
result.distance = maxDistance;
return result;
}
}

View File

@ -0,0 +1,246 @@
#include "Deer/Log.h"
#include "Deer/VoxelWorld.h"
#include "Deer/Voxels/Chunk.h"
#include "Deer/Voxels/Layer.h"
#ifdef DEER_RENDER
#include "DeerRender/Voxels/VoxelWorldRenderData.h"
#endif
#include <math.h>
#include <cmath>
#include <vector>
namespace Deer {
Voxel VoxelWorld::readVoxel(VoxelCordinates coords) {
ChunkID chunkID;
ChunkVoxelID chunkVoxelID;
extractChunkCordinates(coords, chunkID, chunkVoxelID);
if (!m_worldProps.isValid(chunkID)) return emptyVoxel;
Chunk& chunk = m_chunks[m_worldProps.getWorldChunkID(chunkID)];
return chunk.readVoxel(chunkVoxelID);
}
void VoxelWorld::setVoxel(VoxelCordinates coords, Voxel info) {
ChunkID chunkID;
ChunkVoxelID chunkVoxelID;
extractChunkCordinates(coords, chunkID, chunkVoxelID);
if (!m_worldProps.isValid(chunkID)) return;
Chunk& chunk = m_chunks[m_worldProps.getWorldChunkID(chunkID)];
chunk.modVoxel(chunkVoxelID) = info;
LayerID layerID;
LayerVoxelID layerVoxelID;
extractLayerCordinates(coords.x, coords.z, layerID, layerVoxelID);
Layer& layer = m_layers[m_worldProps.getWorldLayerID(layerID)];
LayerVoxel& layerVoxel = layer.modLayerVoxel(layerVoxelID);
if (!info.isVoxelType())
layerVoxel.height = calculateLayerVoxelHeight(coords.x, coords.z);
else if (coords.y >= layerVoxel.height)
layerVoxel.height = coords.y + 1;
#ifdef DEER_RENDER
m_renderData->chunkQueue.addChunk(chunkID);
// For every axis, X & Y & Z
for (int i = 0; i < 3; i++) {
if (chunkVoxelID[i] == 0 && chunkID[i] != 0) {
ChunkID nextChunk = chunkID;
nextChunk[i]--;
m_renderData->chunkQueue.addChunk(nextChunk);
}
if (chunkVoxelID[i] == CHUNK_SIZE(i) &&
chunkID[i] != m_worldProps[i] - 1) {
ChunkID nextChunk = chunkID;
nextChunk[i]++;
m_renderData->chunkQueue.addChunk(nextChunk);
}
}
// Check if we should update the lighting
bakeAmbientLightFromPoint(coords.x, coords.z);
bakeVoxelLightFromPoint(coords);
#endif
}
void VoxelWorld::fillVoxels(VoxelCordinates min, VoxelCordinates max,
Voxel info) {
ChunkID minChunkID;
ChunkID maxChunkID;
ChunkVoxelID minChunkVoxelID;
ChunkVoxelID maxChunkVoxelID;
m_worldProps.clampAndSetMinMax(min, max);
extractChunkCordinates(min, minChunkID, minChunkVoxelID);
extractChunkCordinates(max, maxChunkID, maxChunkVoxelID);
for (int chunkX = minChunkID.x; chunkX <= maxChunkID.x; chunkX++) {
for (int chunkY = minChunkID.y; chunkY <= maxChunkID.y; chunkY++) {
for (int chunkZ = minChunkID.z; chunkZ <= maxChunkID.z;
chunkZ++) {
ChunkID workingChunkID(chunkX, chunkY, chunkZ);
LayerID workingLayerID(chunkX, chunkZ);
Chunk& workingChunk =
m_chunks[m_worldProps.getWorldChunkID(workingChunkID)];
Layer& workingLayer =
m_layers[m_worldProps.getWorldLayerID(workingLayerID)];
ChunkVoxelID workingMin(0, 0, 0);
ChunkVoxelID workingMax(CHUNK_SIZE_X - 1, CHUNK_SIZE_Y - 1,
CHUNK_SIZE_Z - 1);
if (chunkX == minChunkID.x)
workingMin.x = minChunkVoxelID.x;
if (chunkY == minChunkID.y)
workingMin.y = minChunkVoxelID.y;
if (chunkZ == minChunkID.z)
workingMin.z = minChunkVoxelID.z;
if (chunkX == maxChunkID.x)
workingMax.x = maxChunkVoxelID.x;
if (chunkY == maxChunkID.y)
workingMax.y = maxChunkVoxelID.y;
if (chunkZ == maxChunkID.z)
workingMax.z = maxChunkVoxelID.z;
LayerVoxelID workingMinLayer(workingMin.x, workingMin.z);
LayerVoxelID workingMaxLayer(workingMax.x, workingMax.z);
workingChunk.fillVoxels(workingMin, workingMax, info);
workingLayer.fillVoxelLayerMaxHeight(
workingMinLayer, workingMaxLayer, max.y);
#ifdef DEER_RENDER
m_renderData->chunkQueue.addChunk(workingChunkID);
#endif
}
}
}
#ifdef DEER_RENDER
VoxelCordinates minLightModification = min;
VoxelCordinates maxLightModification = max;
// We want to add a 16 layer border
for (int i = 0; i < 3; i++) {
minLightModification[i] -= 16;
maxLightModification[i] += 16;
}
m_worldProps.clampCordinates(minLightModification);
m_worldProps.clampCordinates(maxLightModification);
bakeAmbientLight(minLightModification.x, maxLightModification.x,
minLightModification.z, maxLightModification.z);
bakeVoxelLight(minLightModification, maxLightModification);
#endif
}
void VoxelWorld::remplaceVoxels(VoxelCordinates min, VoxelCordinates max,
Voxel ref, Voxel value) {
ChunkID minChunkID;
ChunkID maxChunkID;
ChunkVoxelID minChunkVoxelID;
ChunkVoxelID maxChunkVoxelID;
m_worldProps.clampAndSetMinMax(min, max);
extractChunkCordinates(min, minChunkID, minChunkVoxelID);
extractChunkCordinates(max, maxChunkID, maxChunkVoxelID);
for (int chunkX = minChunkID.x; chunkX <= maxChunkID.x; chunkX++) {
for (int chunkY = minChunkID.y; chunkY <= maxChunkID.y; chunkY++) {
for (int chunkZ = minChunkID.z; chunkZ <= maxChunkID.z;
chunkZ++) {
ChunkID workingChunkID(chunkX, chunkY, chunkZ);
Chunk& workingChunk =
m_chunks[m_worldProps.getWorldChunkID(workingChunkID)];
ChunkVoxelID workingMin(0, 0, 0);
ChunkVoxelID workingMax(CHUNK_SIZE_X - 1, CHUNK_SIZE_Y - 1,
CHUNK_SIZE_Z - 1);
if (chunkX == minChunkID.x)
workingMin.x = minChunkVoxelID.x;
if (chunkY == minChunkID.y)
workingMin.y = minChunkVoxelID.y;
if (chunkZ == minChunkID.z)
workingMin.z = minChunkVoxelID.z;
if (chunkX == maxChunkID.x)
workingMax.x = maxChunkVoxelID.x;
if (chunkY == maxChunkID.y)
workingMax.y = maxChunkVoxelID.y;
if (chunkZ == maxChunkID.z)
workingMax.z = maxChunkVoxelID.z;
workingChunk.remplaceVoxels(workingMin, workingMax, ref,
value);
#ifdef DEER_RENDER
m_renderData->chunkQueue.addChunk(workingChunkID);
#endif
}
}
}
for (int xPos = min.x; xPos <= max.x; xPos++) {
for (int zPos = min.z; zPos <= max.z; zPos++) {
LayerID layerID;
LayerVoxelID layerVoxelID;
extractLayerCordinates(xPos, zPos, layerID, layerVoxelID);
int worldLayerID = m_worldProps.getWorldLayerID(layerID);
m_layers[worldLayerID].modLayerVoxel(layerVoxelID).height =
calculateLayerVoxelHeight(xPos, zPos);
}
}
#ifdef DEER_RENDER
VoxelCordinates minLightModification = min;
VoxelCordinates maxLightModification = max;
// We want to add a 16 layer border
for (int i = 0; i < 3; i++) {
minLightModification[i] -= 16;
maxLightModification[i] += 16;
}
m_worldProps.clampCordinates(minLightModification);
m_worldProps.clampCordinates(maxLightModification);
bakeAmbientLight(minLightModification.x, maxLightModification.x,
minLightModification.z, maxLightModification.z);
bakeVoxelLight(minLightModification, maxLightModification);
#endif
}
LayerVoxel VoxelWorld::readLayerVoxel(int x, int z) {
LayerID layerID;
LayerVoxelID layerVoxelID;
extractLayerCordinates(x, z, layerID, layerVoxelID);
if (!m_worldProps.isValid(layerID)) return LayerVoxel();
Layer& layer = m_layers[m_worldProps.getWorldLayerID(layerID)];
return layer.readLayerVoxel(layerVoxelID);
}
LayerVoxel& VoxelWorld::modLayerVoxel(int x, int z) {
LayerID layerID;
LayerVoxelID layerVoxelID;
extractLayerCordinates(x, z, layerID, layerVoxelID);
if (!m_worldProps.isValid(layerID)) return nullLayerVoxel;
Layer& layer = m_layers[m_worldProps.getWorldLayerID(layerID)];
return layer.modLayerVoxel(layerVoxelID);
}
} // namespace Deer

View File

@ -0,0 +1,29 @@
#include "DeerRender/Input.h"
namespace Deer {
#ifdef DEER_RENDER
bool Input::isKeyPressed(unsigned int key) {
return Application::s_application->m_window->getKeyPressed(key);
}
bool Input::isMouseButtonPressed(int button) {
return Application::s_application->m_window->getMouseButton(button);
}
void Input::getMousePos(float& x, float& y) {
return Application::s_application->m_window->getMousePos(x, y);
}
#else
bool Input::isKeyPressed(unsigned int key) {
return false;
}
bool Input::isMouseButtonPressed(int button) {
return false;
}
void Input::getMousePos(float& x, float& y) {
return;
}
#endif
}

View File

@ -0,0 +1,29 @@
#pragma once
#include "DeerRender/Events/ApplicationEvent.h"
#include "DeerRender/Events/KeyEvent.h"
#include "DeerRender/Events/MouseEvent.h"
namespace Deer {
class ImGuiLayer {
public:
~ImGuiLayer() = default;
void onAttach();
void onDetach();
void begin();
void end();
void onEvent(Event& event);
private:
bool onMouseButtonPressedEvent(MouseButtonPressedEvent& e);
bool onMouseButtonReleasedEvent(MouseButtonReleasedEvent& e);
bool onMouseMovedEvent(MouseMovedEvent& e);
bool onMouseScrollEvent(MouseScrolledEvent& e);
bool onKeyPressedEvent(KeyPressedEvent& e);
bool onKeyReleasedEvent(KeyReleasedEvent& e);
bool onKeyTypedEvent(KeyTypedEvent& e);
bool onWindowResizeEvent(WindowResizeEvent& e);
};
}

View File

@ -0,0 +1,129 @@
#include "DeerRender/Render/Buffer.h"
#include "Deer/Log.h"
#include "DeerRender/Render/Render.h"
namespace Deer {
unsigned int dataTypeSize(DataType type) {
switch (type) {
case DataType::None: DEER_CORE_ERROR("Unkown shader data type"); return 0;
// Halfs : 2
case DataType::Half: return 2;
case DataType::Half2: return 2 * 2;
case DataType::Half3: return 2 * 3;
case DataType::Half4: return 2 * 4;;
// Floats : 4
case DataType::Float: return 4;
case DataType::Float2: return 4 * 2;
case DataType::Float3: return 4 * 3;
case DataType::Float4: return 4 * 4;
// Byte : 1
case DataType::Byte: return 1;
case DataType::Byte2: return 1 * 2;
case DataType::Byte3: return 1 * 3;
case DataType::Byte4: return 1 * 4;
// Short : 2
case DataType::Short: return 2;
case DataType::Short2: return 2 * 2;
case DataType::Short3: return 2 * 3;
case DataType::Short4: return 2 * 4;
// Ints : 4
case DataType::Int: return 4;
case DataType::Int2: return 4 * 2;
case DataType::Int3: return 4 * 3;
case DataType::Int4: return 4 * 4;
// Byte : 1
case DataType::Unsigned_Byte: return 1;
case DataType::Unsigned_Byte2: return 1 * 2;
case DataType::Unsigned_Byte3: return 1 * 3;
case DataType::Unsigned_Byte4: return 1 * 4;
// Short : 2
case DataType::Unsigned_Short: return 2;
case DataType::Unsigned_Short2: return 2 * 2;
case DataType::Unsigned_Short3: return 2 * 3;
case DataType::Unsigned_Short4: return 2 * 4;
// Ints : 4
case DataType::Unsigned_Int: return 4;
case DataType::Unsigned_Int2: return 4 * 2;
case DataType::Unsigned_Int3: return 4 * 3;
case DataType::Unsigned_Int4: return 4 * 4;
}
DEER_CORE_ERROR("Unkown shader data type");
}
unsigned int dataTypeCount(DataType type) {
switch (type) {
case DataType::None: DEER_CORE_ERROR("Unkown shader data type"); return 0;
// Halfs : 2
case DataType::Half: return 1;
case DataType::Half2: return 2;
case DataType::Half3: return 3;
case DataType::Half4: return 4;
// Floats : 4
case DataType::Float: return 1;
case DataType::Float2: return 2;
case DataType::Float3: return 3;
case DataType::Float4: return 4;
// Byte : 1
case DataType::Byte: return 1;
case DataType::Byte2: return 2;
case DataType::Byte3: return 3;
case DataType::Byte4: return 4;
// Short : 2
case DataType::Short: return 1;
case DataType::Short2: return 2;
case DataType::Short3: return 3;
case DataType::Short4: return 4;
// Ints : 4
case DataType::Int: return 1;
case DataType::Int2: return 2;
case DataType::Int3: return 3;
case DataType::Int4: return 4;
// Byte : 1
case DataType::Unsigned_Byte: return 1;
case DataType::Unsigned_Byte2: return 2;
case DataType::Unsigned_Byte3: return 3;
case DataType::Unsigned_Byte4: return 4;
// Short : 2
case DataType::Unsigned_Short: return 1;
case DataType::Unsigned_Short2: return 2;
case DataType::Unsigned_Short3: return 3;
case DataType::Unsigned_Short4: return 4;
// Ints : 4
case DataType::Unsigned_Int: return 1;
case DataType::Unsigned_Int2: return 2;
case DataType::Unsigned_Int3: return 3;
case DataType::Unsigned_Int4: return 4;
}
DEER_CORE_ERROR("Unkown shader data type");
}
unsigned int indexDataTypeSize(IndexDataType type)
{
switch (type) {
case IndexDataType::None: DEER_CORE_ERROR("Unkown shader data type"); return 0;
case IndexDataType::Unsigned_Byte: return 1;
case IndexDataType::Unsigned_Short: return 2;
case IndexDataType::Unsigned_Int: return 4;
}
DEER_CORE_ERROR("Unkown shader data type");
}
void BufferLayout::calculateOffsetAndStride() {
unsigned int offset = 0;
int calc_stride = 0;
for (auto& element : m_bufferElements) {
if (element.offset == -1)
element.offset = offset;
offset += dataTypeSize(element.type);
calc_stride += dataTypeSize(element.type);
}
if (m_stride == -1)
m_stride = calc_stride;
}
}

View File

@ -0,0 +1,22 @@
#include "DeerRender/Render/Camera.h"
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/quaternion.hpp>
#include "glm/gtc/matrix_transform.hpp"
namespace Deer {
Camera::Camera(float aspect, float fov, float nearZ, float farZ)
: m_aspect(aspect), m_fov(fov), m_nearZ(nearZ), m_farZ(farZ), m_position(glm::vec3(0)), m_rotation(glm::quat()){
recalculateMatrices();
}
void Camera::recalculateMatrices() {
m_viewMatrix = glm::perspective(glm::radians(m_fov), m_aspect, m_nearZ, m_farZ);
//m_viewMatrix = glm::inverse(m_viewMatrix);
glm::mat4 zInvertedMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(1.0f, 1.0f, -1.0f));
m_projectionMatrix = glm::translate(glm::mat4(1.0f), m_position);
m_projectionMatrix = m_projectionMatrix * glm::toMat4(m_rotation);
m_projectionMatrix = zInvertedMatrix * glm::inverse(m_projectionMatrix);
}
}

View File

@ -0,0 +1,21 @@
#include "MeshUtils.h"
#include "DeerRender/Render/Buffer.h"
#include "DeerRender/Render/VertexArray.h"
namespace Deer {
Ref<VertexArray> MeshUtils::createMesh(int vertexCount, float* vertices, int indexCount, unsigned int* m_indices) {
Ref<VertexBuffer> vBuffer = VertexBuffer::create(vertices, vertexCount * sizeof(float));
Ref<IndexBuffer> iBuffer = IndexBuffer::create(m_indices, indexCount * sizeof(unsigned int), IndexDataType::Unsigned_Int);
BufferLayout layout({
{"a_Position", DataType::Float3, ShaderDataType::FloatingPoint}
});
vBuffer->setLayout(layout);
Ref<VertexArray> vertexArray = VertexArray::create();
vertexArray->addVertexBuffer(vBuffer);
vertexArray->setIndexBuffer(iBuffer);
return vertexArray;
}
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "Deer/Memory.h"
namespace Deer {
class VertexArray;
class MeshUtils {
public:
static Ref<VertexArray> createMesh(int vertexCount, float* vertices, int indexCount, unsigned int* m_indices);
};
}

View File

@ -0,0 +1,25 @@
#include "Render.h"
#include "DeerRender/Render/RenderCommand.h"
#include "DeerRender/Render/VertexArray.h"
#include "DeerRender/Render/RenderAPI.h"
namespace Deer {
void Render::beginExecution() {
}
void Render::endExecution() {
}
void Render::submit(const Ref<VertexArray>& vertexArray) {
vertexArray->bind();
RenderCommand::drawIndex(vertexArray);
}
void Render::submitLine(const Ref<VertexArray>& vertexArray) {
vertexArray->bind();
RenderCommand::drawLines(vertexArray);
}
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "DeerRender/Render/RenderAPI.h"
namespace Deer {
class VertexArray;
class Render {
public:
static void beginExecution();
static void endExecution();
static void submit(const Ref<VertexArray>& vertexArray);
static void submitLine(const Ref<VertexArray>& vertexArray);
static RenderAPI::API getAPI() { return RenderAPI::getAPI(); }
};
}

View File

@ -0,0 +1,10 @@
#include "RenderAPI.h"
#include "DeerRender/Render/VertexArray.h"
namespace Deer {
#ifdef DEER_RENDER
RenderAPI::API RenderAPI::s_API = RenderAPI::API::OpenGL;
#else // !DEER_SERVICE
RenderAPI::API RenderAPI::s_API = RenderAPI::API::None;
#endif
}

View File

@ -0,0 +1,30 @@
#pragma once
#include "Deer/Memory.h"
#include "glm/glm.hpp"
namespace Deer {
class VertexArray;
class RenderAPI {
public:
enum class API {
None = 0,
OpenGL = 1
};
public:
virtual ~RenderAPI() = default;
virtual void init() = 0;
virtual void clearColor(const glm::vec4& color) = 0;
virtual void setDepthBuffer(bool enable) = 0;
virtual void setBackfaceCulling(bool enable) = 0;
virtual void clear() = 0;
virtual void drawIndex(const Ref<VertexArray>& vertexArray) = 0;
virtual void drawLines(const Ref<VertexArray>& vertexArray) = 0;
inline static API getAPI() { return s_API; }
private:
static API s_API;
};
}

View File

@ -0,0 +1,44 @@
#pragma once
#include "Deer/Memory.h"
#include "DeerRender/Render/RenderAPI.h"
#include "glm/glm.hpp"
namespace Deer {
class VertexArray;
class RenderAPI;
class RenderCommand {
public:
static inline void setClearColor(const glm::vec4& color) {
s_renderAPI->clearColor(color);
}
static inline void clear() {
s_renderAPI->clear();
}
static inline void drawIndex(const Ref<VertexArray>& vertexArray) {
s_renderAPI->drawIndex(vertexArray);
}
static inline void drawLines(const Ref<VertexArray>& vertexArray) {
s_renderAPI->drawLines(vertexArray);
}
static inline void setDepthBuffer(bool state) {
s_renderAPI->setDepthBuffer(state);
}
static inline void setBackfaceCulling(bool state) {
s_renderAPI->setBackfaceCulling(state);
}
static inline void init() {
s_renderAPI->init();
}
private:
static Scope<RenderAPI> s_renderAPI;
};
}

View File

@ -0,0 +1,9 @@
#pragma once
namespace Deer {
class RenderContext {
public:
virtual void init() = 0;
virtual void swapBuffers() = 0;
};
}

View File

@ -0,0 +1,182 @@
#include "RenderUtils.h"
#include "DeerRender/Render/VertexArray.h"
#include "DeerRender/Render/Buffer.h"
#include "DeerRender/Render/Shader.h"
#ifdef DEER_RENDER
namespace Deer {
namespace RenderUtils {
Ref<VertexArray> m_lineVertexArray;
Ref<VertexArray> m_faceVertexArray;
Ref<Shader> m_lineShader;
Ref<Shader> m_faceShader;
Ref<VertexArray> genLineVertexArray();
Ref<VertexArray> genFaceVertexArray();
Ref<Shader> getLineShader();
Ref<Shader> getFaceShader();
}
void RenderUtils::initializeRenderUtils() {
m_lineVertexArray = genLineVertexArray();
m_lineShader = getLineShader();
m_faceVertexArray = genFaceVertexArray();
m_faceShader = getFaceShader();
}
Ref<VertexArray> RenderUtils::genLineVertexArray() {
unsigned int vertices[2] = { 0, 1 };
Ref<VertexArray> vertexArray = VertexArray::create();
vertexArray->bind();
Ref<VertexBuffer> vertexBuffer = VertexBuffer::create(vertices, sizeof(vertices));
Ref<IndexBuffer> indexBuffer = IndexBuffer::create(vertices, sizeof(vertices), IndexDataType::Unsigned_Int);
BufferLayout layout({
{"a_vertexID", DataType::Unsigned_Int, ShaderDataType::Integer}
});
vertexBuffer->setLayout(layout);
vertexArray->addVertexBuffer(vertexBuffer);
vertexArray->setIndexBuffer(indexBuffer);
return vertexArray;
}
Ref<Shader> RenderUtils::getLineShader() {
std::string vertexSrc = R"(
#version 410 core
layout(location = 0) in int a_vertexID;
uniform mat4 u_viewMatrix;
uniform vec3 u_posA;
uniform vec3 u_posB;
void main()
{
vec3 pos;
if (a_vertexID == 0) {
pos = u_posA;
} else {
pos = u_posB;
}
gl_Position = u_viewMatrix * vec4(pos,1.0);
})";
std::string fragmentSrc = R"(
#version 410 core
layout(location = 0) out vec4 fragColor;
layout(location = 1) out int objectID;
uniform vec3 u_color;
void main()
{
objectID = -1;
fragColor = vec4(u_color, 1.0);
})";
return Shader::create(vertexSrc, fragmentSrc);
}
Ref<VertexArray> RenderUtils::genFaceVertexArray() {
unsigned int vertices[4] = {
0, 1, 2, 3
};
unsigned int indices[6] = {
0, 2, 1, 1, 2, 3
};
Ref<VertexArray> vertexArray = VertexArray::create();
vertexArray->bind();
Ref<VertexBuffer> vertexBuffer = VertexBuffer::create(vertices, sizeof(vertices));
Ref<IndexBuffer> indexBuffer = IndexBuffer::create(indices, sizeof(indices), IndexDataType::Unsigned_Int);
BufferLayout layout({
{"a_vertexID", DataType::Unsigned_Int, ShaderDataType::Integer}
});
vertexBuffer->setLayout(layout);
vertexArray->addVertexBuffer(vertexBuffer);
vertexArray->setIndexBuffer(indexBuffer);
return vertexArray;
}
Ref<Shader> RenderUtils::getFaceShader() {
std::string vertexSrc = R"(
#version 410 core
layout(location = 0) in int a_vertexID;
uniform mat4 u_viewMatrix;
uniform vec3 u_posA;
uniform vec3 u_posB;
uniform vec3 u_posC;
uniform vec3 u_posD;
uniform int u_textureSize;
uniform int u_textureID;
out vec2 uv;
void main()
{
vec3 pos;
vec2 a_uv;
if (a_vertexID == 0) {
pos = u_posA;
a_uv = vec2(0, 0);
} else if (a_vertexID == 1) {
pos = u_posB;
a_uv = vec2(1, 0);
} else if (a_vertexID == 2) {
pos = u_posC;
a_uv = vec2(0, 1);
} else {
pos = u_posD;
a_uv = vec2(1, 1);
}
int posY = u_textureID / u_textureSize;
int posX = u_textureID - posY * u_textureSize;
uv = vec2((a_uv.x + float(posX)) / float(u_textureSize), (a_uv.y + float(posY)) / float(u_textureSize));
gl_Position = u_viewMatrix * vec4(pos,1.0);
})";
std::string fragmentSrc = R"(
#version 410 core
layout(location = 0) out vec4 fragColor;
layout(location = 1) out int objectID;
in vec2 uv;
uniform sampler2D u_texture;
void main()
{
vec4 color = texture(u_texture, uv);
if (color.a == 0){
discard;
return;
}
objectID = -1;
fragColor = color;
})";
return Shader::create(vertexSrc, fragmentSrc);
}
}
#endif

View File

@ -0,0 +1,17 @@
#pragma once
#include "Deer/Memory.h"
namespace Deer {
class VertexArray;
class Shader;
namespace RenderUtils {
extern Ref<VertexArray> m_lineVertexArray;
extern Ref<VertexArray> m_faceVertexArray;
extern Ref<Shader> m_lineShader;
extern Ref<Shader> m_faceShader;
void initializeRenderUtils();
}
}

View File

@ -0,0 +1,156 @@
#include "Deer/Enviroment.h"
#include "Deer/Application.h"
#include "Deer/Asset.h"
#include "Deer/Components.h"
#include "Deer/Voxel.h"
#include "DeerRender/Render/Render.h"
#include "DeerRender/Render/RenderUtils.h"
#include "DeerRender/Render/Texture.h"
#include "Deer/Log.h"
namespace Deer {
void Environment::render(SceneCamera& camera) {
glm::mat4 camMatrix = glm::inverse(camera.transform.getMatrix());
glm::mat4 projectionMatrix = camera.camera.getMatrix();
glm::mat4 invertZ = glm::scale(glm::mat4(1.0f), glm::vec3(1, 1, -1));
// Lets invert the z axis for engine convenience
glm::mat4 cameraProjectionMatrix = projectionMatrix * invertZ * camMatrix;
{
auto view = m_registry.view<MeshRenderComponent, TagComponent>();
for (auto entityId : view) {
auto& meshRender = view.get<MeshRenderComponent>(entityId);
if (meshRender.shaderAssetID == 0)
continue;
if (meshRender.meshAssetID == 0)
continue;
auto& tag = view.get<TagComponent>(entityId);
Entity& entity = getEntity(tag.entityUID);
if (entity.hasComponent<TextureBindingComponent>()) {
TextureBindingComponent& textureBinding = entity.getComponent<TextureBindingComponent>();
for (int x = 0; x < MAX_TEXTURE_BINDINGS; x++) {
if (textureBinding.textureAssetID[x] == 0)
continue;
Asset<Texture2D>& textureAsset = AssetManager::getAsset<Texture2D>(textureBinding.textureAssetID[x]);
textureAsset.value->bind(textureBinding.textureBindID[x]);
}
}
glm::mat4 matrix = entity.getWorldMatrix();
Asset<Shader>& shaderAsset = AssetManager::getAsset<Shader>(meshRender.shaderAssetID);
shaderAsset.value->bind();
shaderAsset.value->uploadUniformMat4("u_viewMatrix", cameraProjectionMatrix);
shaderAsset.value->uploadUniformMat4("u_worldMatrix", matrix);
shaderAsset.value->uploadUniformInt("u_objectID", tag.entityUID);
Asset<Mesh>& meshAsset = AssetManager::getAsset<Mesh>(meshRender.meshAssetID);
meshAsset.value->bind();
Render::submit(meshAsset.value);
}
}
// Draw Grid Gizmo
{
RenderUtils::m_lineShader->bind();
RenderUtils::m_lineShader->uploadUniformMat4("u_viewMatrix", cameraProjectionMatrix);
RenderUtils::m_lineShader->uploadUniformFloat3("u_color", glm::vec3(.5f, .5f, .5f));
for (int x = 0; x < CHUNK_SIZE_X + 1; x++) {
RenderUtils::m_lineShader->uploadUniformFloat3("u_posA", glm::vec3(x, 0, 0));
RenderUtils::m_lineShader->uploadUniformFloat3("u_posB", glm::vec3(x, 0, CHUNK_SIZE_Z));
Render::submitLine(RenderUtils::m_lineVertexArray);
}
for (int z = 0; z < CHUNK_SIZE_Z + 1; z++) {
RenderUtils::m_lineShader->uploadUniformFloat3("u_posA", glm::vec3(0, 0, z));
RenderUtils::m_lineShader->uploadUniformFloat3("u_posB", glm::vec3(CHUNK_SIZE_X, 0, z));
Render::submitLine(RenderUtils::m_lineVertexArray);
}
}
// Rendering Camera Gizmo
{
RenderUtils::m_lineShader->bind();
RenderUtils::m_lineShader->uploadUniformMat4("u_viewMatrix", cameraProjectionMatrix);
RenderUtils::m_lineShader->uploadUniformFloat3("u_color", glm::vec3(.7f, .85f, 1));
auto view = m_registry.view<CameraComponent, TagComponent>();
for (auto entityId : view) {
CameraComponent cameraComponent = view.get<CameraComponent>(entityId);
cameraComponent.nearZ = .5f;
cameraComponent.farZ = 2;
TagComponent& tag = view.get<TagComponent>(entityId);
Entity& entity = getEntity(tag.entityUID);
glm::mat4 matrix = entity.getWorldMatrix();
glm::mat4 camPrespective = glm::inverse(cameraComponent.getMatrix());
glm::mat4 cameraMatrix = camPrespective;
glm::vec3 cameraAxisPoints[2 * 2 * 2];
// Generate 8 Points
for (int x = 0; x < 2; x++) {
for (int y = 0; y < 2; y++) {
for (int z = 0; z < 2; z++) {
glm::vec4 endPos = invertZ * camPrespective * glm::vec4(x * 2 - 1, y * 2 - 1, z, 1);
endPos = endPos * endPos.w;
endPos.w = 1;
cameraAxisPoints[z * 4 + y * 2 + x] = matrix * endPos;
}
}
}
// Draw the lines
for (int x = 0; x < 2; x++) {
for (int y = 0; y < 2; y++) {
int posA = 0 * 4 + y * 2 + x;
int posB = 1 * 4 + y * 2 + x;
RenderUtils::m_lineShader->uploadUniformFloat3("u_posA", cameraAxisPoints[posA]);
RenderUtils::m_lineShader->uploadUniformFloat3("u_posB", cameraAxisPoints[posB]);
Render::submitLine(RenderUtils::m_lineVertexArray);
}
}
for (int x = 0; x < 2; x++) {
for (int z = 0; z < 2; z++) {
int posA = z * 4 + 0 * 2 + x;
int posB = z * 4 + 1 * 2 + x;
RenderUtils::m_lineShader->uploadUniformFloat3("u_posA", cameraAxisPoints[posA]);
RenderUtils::m_lineShader->uploadUniformFloat3("u_posB", cameraAxisPoints[posB]);
Render::submitLine(RenderUtils::m_lineVertexArray);
}
}
for (int y = 0; y < 2; y++) {
for (int z = 0; z < 2; z++) {
int posA = z * 4 + y * 2 + 0;
int posB = z * 4 + y * 2 + 1;
RenderUtils::m_lineShader->uploadUniformFloat3("u_posA", cameraAxisPoints[posA]);
RenderUtils::m_lineShader->uploadUniformFloat3("u_posB", cameraAxisPoints[posB]);
Render::submitLine(RenderUtils::m_lineVertexArray);
}
}
}
}
}
}

View File

@ -0,0 +1,166 @@
#include "DeerRender/GizmoRenderer.h"
#include "DeerRender/Render/Render.h"
#include "Deer/Voxel.h"
#include "DeerRender/LightVoxel.h"
#include "DeerRender/Render/RenderUtils.h"
#include "DeerRender/SceneCamera.h"
#include "Deer/Components.h"
#include "DeerRender/Render/Texture.h"
#include "DeerRender/Render/RenderCommand.h"
namespace Deer {
void GizmoRenderer::drawLine(glm::vec3 a, glm::vec3 b, glm::vec3 color) {
m_lines.push_back({ a, b, color });
}
void GizmoRenderer::drawVoxelLine(int _x, int _y, int _z, glm::vec3 color) {
for (int x = 0; x < 2; x++) {
for (int y = 0; y < 2; y++) {
glm::vec3 a = glm::vec3(x + _x, y + _y, 0 + _z);
glm::vec3 b = glm::vec3(x + _x, y + _y, 1 + _z);
drawLine(a, b, color);
}
}
for (int z = 0; z < 2; z++) {
for (int y = 0; y < 2; y++) {
glm::vec3 a = glm::vec3(0 + _x, y + _y, z + _z);
glm::vec3 b = glm::vec3(1 + _x, y + _y, z + _z);
drawLine(a, b, color);
}
}
for (int x = 0; x < 2; x++) {
for (int z = 0; z < 2; z++) {
glm::vec3 a = glm::vec3(x + _x, 0 + _y, z + _z);
glm::vec3 b = glm::vec3(x + _x, 1 + _y, z + _z);
drawLine(a, b, color);
}
}
}
void GizmoRenderer::drawVoxelLineFace(int x, int y, int z, uint8_t face, glm::vec3 color) {
glm::vec3 points[4];
for (int i = 0; i < 4; i++) {
points[i] = {
x + NORMAL_VERTEX_POS(X_AXIS, i, face),
y + NORMAL_VERTEX_POS(Y_AXIS, i, face),
z + NORMAL_VERTEX_POS(Z_AXIS, i, face)
};
}
drawLine(points[0], points[1], color);
drawLine(points[2], points[3], color);
drawLine(points[0], points[2], color);
drawLine(points[1], points[3], color);
}
void GizmoRenderer::drawVoxelFace(int x, int y, int z, uint16_t voxelID, uint8_t faceID, uint8_t priority) {
glm::vec3 points[4];
VoxelAspect& aspect = VoxelData::voxelsAspect[voxelID];
GizmoFace face;
face.textureID = aspect.textureFacesIDs[faceID];
face.face = faceID;
for (int i = 0; i < 4; i++) {
face.positions[i] = {
x + NORMAL_VERTEX_POS(X_AXIS, i, faceID),
y + NORMAL_VERTEX_POS(Y_AXIS, i, faceID),
z + NORMAL_VERTEX_POS(Z_AXIS, i, faceID)
};
}
m_faces[priority].push_back(face);
}
void GizmoRenderer::drawVoxelFaceInverted(int x, int y, int z, uint16_t voxelID, uint8_t faceID, uint8_t priority) {
glm::vec3 points[4];
VoxelAspect& aspect = VoxelData::voxelsAspect[voxelID];
GizmoFace face;
face.textureID = aspect.textureFacesIDs[faceID];
face.face = faceID;
face.positions[0] = {
x + NORMAL_VERTEX_POS(X_AXIS, 0, faceID),
y + NORMAL_VERTEX_POS(Y_AXIS, 0, faceID),
z + NORMAL_VERTEX_POS(Z_AXIS, 0, faceID)
};
face.positions[2] = {
x + NORMAL_VERTEX_POS(X_AXIS, 1, faceID),
y + NORMAL_VERTEX_POS(Y_AXIS, 1, faceID),
z + NORMAL_VERTEX_POS(Z_AXIS, 1, faceID)
};
face.positions[1] = {
x + NORMAL_VERTEX_POS(X_AXIS, 2, faceID),
y + NORMAL_VERTEX_POS(Y_AXIS, 2, faceID),
z + NORMAL_VERTEX_POS(Z_AXIS, 2, faceID)
};
face.positions[3] = {
x + NORMAL_VERTEX_POS(X_AXIS, 3, faceID),
y + NORMAL_VERTEX_POS(Y_AXIS, 3, faceID),
z + NORMAL_VERTEX_POS(Z_AXIS, 3, faceID)
};
m_faces[priority].push_back(face);
}
void GizmoRenderer::render(const SceneCamera& camera) {
RenderCommand::setDepthBuffer(true);
// We make a exact camera but with less far clip to give priority in the depht test
CameraComponent camera_lessDistance = camera.camera;
camera_lessDistance.farZ *= 1.1f;
glm::mat4 camMatrix = glm::inverse(camera.transform.getMatrix());
glm::mat4 projectionMatrix = camera_lessDistance.getMatrix();
glm::mat4 invertZ = glm::scale(glm::mat4(1.0f), glm::vec3(1, 1, -1));
glm::mat4 cameraProjectionMatrix = projectionMatrix * invertZ * camMatrix;
VoxelData::getVoxelColorTextureAtlas()->bind(0);
RenderUtils::m_faceShader->bind();
RenderUtils::m_faceShader->uploadUniformMat4("u_viewMatrix", cameraProjectionMatrix);
RenderUtils::m_faceShader->uploadUniformInt("u_texture", 0);
RenderUtils::m_faceShader->uploadUniformInt("u_textureSize", VoxelData::getVoxelTextureAtlasSize());
for (int i = 0; i < GIZMO_DEPTH; i++) {
for (GizmoFace& face : m_faces[i]) {
RenderUtils::m_faceShader->uploadUniformInt("u_textureID", face.textureID);
RenderUtils::m_faceShader->uploadUniformFloat3("u_posA", face.positions[0]);
RenderUtils::m_faceShader->uploadUniformFloat3("u_posB", face.positions[1]);
RenderUtils::m_faceShader->uploadUniformFloat3("u_posC", face.positions[2]);
RenderUtils::m_faceShader->uploadUniformFloat3("u_posD", face.positions[3]);
Render::submit(RenderUtils::m_faceVertexArray);
}
}
RenderCommand::setDepthBuffer(false);
for (std::array<glm::vec3, 3>&line : m_lines) {
RenderUtils::m_lineShader->bind();
RenderUtils::m_lineShader->uploadUniformMat4("u_viewMatrix", cameraProjectionMatrix);
RenderUtils::m_lineShader->uploadUniformFloat3("u_color", line[2]);
RenderUtils::m_lineShader->uploadUniformFloat3("u_posA", line[0]);
RenderUtils::m_lineShader->uploadUniformFloat3("u_posB", line[1]);
Render::submitLine(RenderUtils::m_lineVertexArray);
}
}
void GizmoRenderer::refresh() {
m_lines.clear();
for (int i = 0; i < GIZMO_DEPTH; i++)
m_faces[i].clear();
}
}

View File

@ -0,0 +1,31 @@
#include "Deer/Scene.h"
#include "Deer/Enviroment.h"
#include "Deer/Components.h"
#include "Deer/VoxelWorld.h"
#include "DeerRender/Render/RenderCommand.h"
#include "Deer/Enviroment.h"
namespace Deer {
void Scene::render() {
uint32_t mainCamera = m_enviroment->tryGetMainCamera();
if (mainCamera == 0)
return;
Entity& m_cameraEntity = m_enviroment->getEntity(mainCamera);
SceneCamera sceneCamera;
sceneCamera.camera = m_cameraEntity.getComponent<CameraComponent>();
sceneCamera.transform = m_cameraEntity.getComponent<TransformComponent>();
Scene::render(sceneCamera);
}
void Scene::render(SceneCamera sceneCamera) {
RenderCommand::setDepthBuffer(true);
m_enviroment->render(sceneCamera);
if (m_voxelWorld)
m_voxelWorld->render(sceneCamera);
RenderCommand::setDepthBuffer(false);
m_gizmoRenderer.render(sceneCamera);
}
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "DeerRender/Components.h"
namespace Deer {
// CAMERA COMPONENT
template<class Archive>
void serialize(Archive& archive,
CameraComponent& camera) {
archive(cereal::make_nvp("aspect", camera.aspect));
archive(cereal::make_nvp("fov", camera.fov));
archive(cereal::make_nvp("farZ", camera.farZ));
archive(cereal::make_nvp("nearZ", camera.nearZ));
}
}

View File

@ -0,0 +1,28 @@
#pragma once
#include "Deer/Components.h"
#include "Deer/Asset.h"
namespace Deer{
template<class Archive>
void save(Archive& archive,
MeshRenderComponent const& meshRender) {
std::string meshLocation = AssetManager::getAssetLocation(meshRender.meshAssetID).generic_string();
archive(cereal::make_nvp("mesh", meshLocation));
std::string shaderLocation = AssetManager::getAssetLocation(meshRender.shaderAssetID).generic_string();
archive(cereal::make_nvp("shader", shaderLocation));
}
template<class Archive>
void load(Archive& archive,
MeshRenderComponent& meshRender) {
std::string meshLocation;
archive(cereal::make_nvp("mesh", meshLocation));
std::string shaderLocation;
archive(cereal::make_nvp("shader", shaderLocation));
meshRender.meshAssetID = AssetManager::loadAsset<Mesh>(std::filesystem::path(meshLocation));
meshRender.shaderAssetID = AssetManager::loadAsset<Shader>(std::filesystem::path(shaderLocation));
}
}

View File

@ -0,0 +1,61 @@
#pragma once
#include "Deer/Components.h"
#include "DeerRender/Render/Texture.h"
#include <string>
#include <vector>
namespace Deer {
struct TextureBinding {
std::string texturePath;
unsigned char bindingID;
TextureBinding() = default;
TextureBinding(std::string _texturePath, unsigned char _bindingID) : texturePath(_texturePath), bindingID(_bindingID) { }
};
template<class Archive>
void serialize(Archive& archive,
TextureBinding& textureBinding) {
archive(cereal::make_nvp("texturePath", textureBinding.texturePath));
archive(cereal::make_nvp("bindingID", textureBinding.bindingID));
}
template<class Archive>
void load(Archive& archive,
TextureBindingComponent& textureBinding) {
std::vector<TextureBinding> bindings;
archive(cereal::make_nvp("bindings", bindings));
int id = 0;
for (auto& binding : bindings) {
if (id >= MAX_TEXTURE_BINDINGS)
break;
textureBinding.textureAssetID[id] = AssetManager::loadAsset<Texture2D>(
std::filesystem::path(binding.texturePath));
textureBinding.textureBindID[id] = binding.bindingID;
}
}
template<class Archive>
void save(Archive& archive,
TextureBindingComponent const& textureBinding) {
std::vector<TextureBinding> bindings;
for (int x = 0; x < MAX_TEXTURE_BINDINGS; x++) {
if (textureBinding.textureAssetID[x] != 0) {
bindings.push_back(TextureBinding(
AssetManager::getAssetLocation(textureBinding.textureAssetID[x]).generic_string(),
textureBinding.textureBindID[x]
));
}
}
std::sort(bindings.begin(), bindings.end(), [](TextureBinding& a, TextureBinding& b) {
return a.bindingID < b.bindingID; });
archive(cereal::make_nvp("bindings", bindings));
}
}

View File

@ -0,0 +1,22 @@
#include "DeerRender/Voxels/VoxelWorldRenderData.h"
namespace Deer {
void ChunkUpdateQueue::addChunk(ChunkID chunkID) {
if (m_containingChunks.contains(chunkID))
return;
m_containingChunks.insert(chunkID);
m_updateOrder.push(chunkID);
}
ChunkID ChunkUpdateQueue::pullChunk() {
ChunkID chunkID = m_updateOrder.front();
m_updateOrder.pop();
m_containingChunks.erase(chunkID);
return chunkID;
}
bool ChunkUpdateQueue::hasChunk() {
return m_updateOrder.size() > 0;
}
}

View File

@ -0,0 +1,33 @@
#pragma once
#include "DeerRender/VoxelAspect.h"
#include "Deer/Log.h"
#include "Deer/Voxel.h"
#include "cereal/cereal.hpp"
#include "cereal/types/string.hpp"
namespace Deer{
template <class Archive>
void serialize(Archive& archive, VoxelTextureFaceDefinition& textureFaceDefinitions) {
archive(cereal::make_nvp("left", textureFaceDefinitions.textureFaces[NORMAL_LEFT]));
archive(cereal::make_nvp("right", textureFaceDefinitions.textureFaces[NORMAL_RIGHT]));
archive(cereal::make_nvp("down", textureFaceDefinitions.textureFaces[NORMAL_DOWN]));
archive(cereal::make_nvp("up", textureFaceDefinitions.textureFaces[NORMAL_UP]));
archive(cereal::make_nvp("front", textureFaceDefinitions.textureFaces[NORMAL_FRONT]));
archive(cereal::make_nvp("back", textureFaceDefinitions.textureFaces[NORMAL_BACK]));
}
template <class Archive>
void serialize(Archive& archive, VoxelColorEmission& voxelEmission) {
archive(cereal::make_nvp("red", voxelEmission.r_value));
archive(cereal::make_nvp("green", voxelEmission.g_value));
archive(cereal::make_nvp("blue", voxelEmission.b_value));
}
template<class Archive>
void serialize(Archive & archive, VoxelAspectDefinition& voxelAspectDefinition) {
archive(cereal::make_nvp("name", voxelAspectDefinition.voxelName));
archive(cereal::make_nvp("textureFaces", voxelAspectDefinition.textureFaces));
archive(cereal::make_nvp("emission", voxelAspectDefinition.colorEmission));
}
}

View File

@ -0,0 +1,155 @@
#include "DeerRender/LightVoxel.h"
#include <iostream>
#include <string>
namespace Deer {
VoxelLight lightVoxel(255);
int lightPropagationComplexDir[12 * 2] = {
NORMAL_LEFT, NORMAL_DOWN,
NORMAL_RIGHT, NORMAL_DOWN,
NORMAL_LEFT, NORMAL_UP,
NORMAL_RIGHT, NORMAL_UP,
NORMAL_LEFT, NORMAL_BACK,
NORMAL_RIGHT, NORMAL_BACK,
NORMAL_LEFT, NORMAL_FRONT,
NORMAL_RIGHT, NORMAL_FRONT,
NORMAL_DOWN, NORMAL_BACK,
NORMAL_UP, NORMAL_BACK,
NORMAL_DOWN, NORMAL_FRONT,
NORMAL_UP, NORMAL_FRONT
};
int normalFacePositions[3 * 4 * 6] = {
// Left
0, 0, 1,
0, 0, 0,
0, 1, 1,
0, 1, 0,
// Right
1, 0, 0,
1, 0, 1,
1, 1, 0,
1, 1, 1,
// Down
0, 0, 1,
1, 0, 1,
0, 0, 0,
1, 0, 0,
// Up
0, 1, 0,
1, 1, 0,
0, 1, 1,
1, 1, 1,
// Back
0, 0, 0,
1, 0, 0,
0, 1, 0,
1, 1, 0,
// Front
1, 0, 1,
0, 0, 1,
1, 1, 1,
0, 1, 1
};
int uvFace[2 * 4] = {
0, 0,
1, 0,
0, 1,
1, 1
};
int ambientOcclusionVertex[6 * 2 * 4 * 3]{
0, -1, 0, 0, 0, 1,
0, -1, 0, 0, 0, -1,
0, 1, 0, 0, 0, 1,
0, 1, 0, 0, 0, -1,
0, -1, 0, 0, 0, -1,
0, -1, 0, 0, 0, 1,
0, 1, 0, 0, 0, -1,
0, 1, 0, 0, 0, 1,
-1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1,
-1, 0, 0, 0, 0, -1,
1, 0, 0, 0, 0, -1,
-1, 0, 0, 0, 0, -1,
1, 0, 0, 0, 0, -1,
-1, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1,
-1, 0, 0, 0, -1, 0,
1, 0, 0, 0, -1, 0,
-1, 0, 0, 0, 1, 0,
1, 0, 0, 0, 1, 0,
1, 0, 0, 0, -1, 0,
-1, 0, 0, 0, -1, 0,
1, 0, 0, 0, 1, 0,
-1, 0, 0, 0, 1, 0
};
int layerCheckDirections[2 * 8]{
-1, 0,
-1, 1,
0, 1,
1, 1,
1, 0,
1, -1,
0, -1,
-1, -1
};
void calcFaces() {
std::string out;
for (int f = 0; f < 6; f++) {
int normalDirX = NORMAL_DIR(X_AXIS, f);
int normalDirY = NORMAL_DIR(Y_AXIS, f);
int normalDirZ = NORMAL_DIR(Z_AXIS, f);
for (int vertex = 0; vertex < 4; vertex++) {
int xPos = NORMAL_VERTEX_POS(X_AXIS, vertex, f);
int yPos = NORMAL_VERTEX_POS(Y_AXIS, vertex, f);
int zPos = NORMAL_VERTEX_POS(Z_AXIS, vertex, f);
int sideX = (xPos == 1) ? 1 : -1;
int sideY = (yPos == 1) ? 1 : -1;
int sideZ = (zPos == 1) ? 1 : -1;
if (normalDirX == 1 || normalDirX == -1) {
out += std::to_string(0) + ", ";
out += std::to_string(sideY) + ", ";
out += std::to_string(0) + ",\t";
out += std::to_string(0) + ", ";
out += std::to_string(0) + ", ";
out += std::to_string(sideZ) + ",\n";
}
if (normalDirY == 1 || normalDirY == -1) {
out += std::to_string(sideX) + ", ";
out += std::to_string(0) + ", ";
out += std::to_string(0) + ",\t";
out += std::to_string(0) + ", ";
out += std::to_string(0) + ", ";
out += std::to_string(sideZ) + ",\n";
}
if (normalDirZ == 1 || normalDirZ == -1) {
out += std::to_string(sideX) + ", ";
out += std::to_string(0) + ", ";
out += std::to_string(0) + ",\t";
out += std::to_string(0) + ", ";
out += std::to_string(sideY) + ", ";
out += std::to_string(0) + ",\n";
}
}
}
std::cout << out;
}/**/
}

View File

@ -0,0 +1,120 @@
#include "Deer/Voxel.h"
#include "Deer/Log.h"
#include "Deer/DataStore.h"
#include "DeerRender/Render/Shader.h"
#include "DeerRender/VoxelAspect.h"
#include "cereal/archives/json.hpp"
#include "DeerRender/Voxels/Serialization/VoxelAspect_Serialization.h"
#include <unordered_map>
#include <string>
#include <sstream>
namespace Deer{
namespace VoxelData {
std::vector<VoxelAspect> voxelsAspect;
std::unordered_map<std::string, uint16_t> texturesIDs;
Ref<Shader> solidVoxelShader;
}
void VoxelData::loadVoxelsAspect() {
std::vector<Path> voxelsAspectPath = DataStore::getFiles(DEER_VOXEL_ASPECT_PATH, ".vaspect");
voxelsAspect.clear();
voxelsAspect.resize(voxelsInfo.size());
voxelsAspect[0].definition.voxelName = VOXEL_INFO_TYPE_AIR;
DEER_CORE_INFO("=== Loading voxel aspect ===");
DEER_CORE_TRACE(" default - air");
for (Path& voxelAspectPath: voxelsAspectPath) {
uint32_t size;
uint8_t* data = DataStore::readFile(voxelAspectPath, &size);
std::string dataString((char*)data, size);
std::istringstream inputStream(dataString);
VoxelAspectDefinition aspectDefinition;
{
cereal::JSONInputArchive archive(inputStream);
archive(cereal::make_nvp("voxelAspect", aspectDefinition));
}
if (aspectDefinition.voxelName.empty()) {
DEER_CORE_ERROR("{0} has an empty name", voxelAspectPath.generic_string().c_str());
continue;
}
int16_t voxelID = getVoxelID(aspectDefinition.voxelName);
if (voxelID == -1) {
DEER_CORE_ERROR("Voxel aspect {0} references {1} but it does not exist",
voxelAspectPath.generic_string().c_str(), aspectDefinition.voxelName.c_str());
continue;
}
DEER_CORE_TRACE(" {0} - {1}",
voxelAspectPath.filename().generic_string().c_str(),
aspectDefinition.voxelName.c_str());
voxelsAspect[voxelID].definition = aspectDefinition;
}
DEER_CORE_INFO("=== Extracting textures ===");
for (VoxelAspect& voxelAspect : voxelsAspect) {
if (voxelsInfo[VoxelData::getVoxelID(voxelAspect.definition.voxelName)].type != VoxelInfoType::Voxel)
continue;
for (int i = 0; i < 6; i++) {
std::string& faceTextureString = voxelAspect.definition.textureFaces[i];
if (faceTextureString.empty()) {
DEER_CORE_WARN("{0} has an empty texture at position {1} this could cause unwanted behaviour!",
voxelAspect.definition.voxelName.c_str(), i);
voxelAspect.textureFacesIDs[i] = 0;
continue;
}
if (texturesIDs.contains(faceTextureString))
voxelAspect.textureFacesIDs[i] = texturesIDs[faceTextureString];
else {
uint16_t textureID = texturesIDs.size();
texturesIDs[faceTextureString] = textureID;
voxelAspect.textureFacesIDs[i] = textureID;
DEER_CORE_TRACE(" texture {0} - id: {1}",
faceTextureString.c_str(), textureID);
}
}
}
}
void VoxelData::createExampleVoxelAspect() {
VoxelAspectDefinition voxelAspectDefinition;
std::ostringstream outputStream;
{
cereal::JSONOutputArchive archive(outputStream);
archive(cereal::make_nvp("voxelAspect", voxelAspectDefinition));
}
std::string restultString = outputStream.str();
DataStore::saveFile(Path(DEER_VOXEL_PATH) / "vaspect.example", (uint8_t*)restultString.c_str(), restultString.size());
}
void VoxelData::loadVoxelsShaders() {
uint32_t size;
uint8_t* data = DataStore::readFile(Path(DEER_VOXEL_SHADER_PATH) / "solid_voxel.glsl", &size);
solidVoxelShader = Shader::create(data, size);
delete[] data;
}
Ref<Shader>& VoxelData::getSolidVoxelShader() {
return solidVoxelShader;
}
}

View File

@ -0,0 +1,106 @@
#include "Deer/Voxel.h"
#include "Deer/Log.h"
#include "DeerRender/VoxelAspect.h"
#include "stb_image.h"
#include "stb_image_write.h"
#include "Deer/DataStore.h"
#include "DeerRender/Render/Texture.h"
#include <unordered_map>
#include <string>
namespace Deer {
namespace VoxelData {
extern std::unordered_map<std::string, uint16_t> texturesIDs;
int squareTextureSize = 0;
uint8_t* voxelColorTextureAtlasData = nullptr;
Ref<Texture2D> voxelColorTextureAtlas = nullptr;
}
void VoxelData::generateTextureAtlas() {
if (voxelColorTextureAtlasData != nullptr) {
delete[] voxelColorTextureAtlasData;
voxelColorTextureAtlasData = nullptr;
}
squareTextureSize = 1;
int textureCount = texturesIDs.size();
while (squareTextureSize * squareTextureSize < textureCount)
squareTextureSize++;
int textureAtlasSize = squareTextureSize * VOXEL_TEXTURE_SIZE_X * squareTextureSize * VOXEL_TEXTURE_SIZE_Y * 4;
voxelColorTextureAtlasData = new uint8_t[textureAtlasSize]{};
stbi_set_flip_vertically_on_load(true);
stbi_flip_vertically_on_write(true);
DEER_CORE_INFO("=== Creating Texture Atlas ===");
for (auto& texture : texturesIDs) {
uint32_t size;
uint8_t* fileData = DataStore::readFile(Path(DEER_VOXEL_TEXTURE_PATH) / (texture.first + ".png"), &size);
DEER_CORE_TRACE(" {0}.png - {1}", texture.first.c_str(), texture.second);
if (fileData == nullptr) {
DEER_CORE_ERROR("{0}.png does not exists",
texture.first.c_str());
continue;
}
int width, height, channels;
uint8_t* textureData = stbi_load_from_memory(fileData, size, &width, &height, &channels, 4);
if (channels < 3) {
DEER_CORE_ERROR("{0}.png has {1} channels and it must be bigger than {2}",
texture.first.c_str(), channels, 3);
} else if (width != VOXEL_TEXTURE_SIZE_X) {
DEER_CORE_ERROR("{0}.png has a width of {1} and it must be {2}",
texture.first.c_str(), width, VOXEL_TEXTURE_SIZE_X);
} else if (height != VOXEL_TEXTURE_SIZE_Y) {
DEER_CORE_ERROR("{0}.png has a height of {1} and it must be {2}",
texture.first.c_str(), height, VOXEL_TEXTURE_SIZE_Y);
} else {
int yOffset = (int)(texture.second / squareTextureSize);
int xOffset = texture.second - yOffset * squareTextureSize;
int yOffsetPixels = yOffset * VOXEL_TEXTURE_SIZE_Y;
int xOffsetPixels = xOffset * VOXEL_TEXTURE_SIZE_X;
for (int y = 0; y < VOXEL_TEXTURE_SIZE_Y; y++) {
for (int x = 0; x < VOXEL_TEXTURE_SIZE_X; x++) {
int inputTextureIndex = (x + y * width) * 4;
int outputTextureIndex = (x + xOffsetPixels + (y + yOffsetPixels) * VOXEL_TEXTURE_SIZE_X * squareTextureSize) * 4;
voxelColorTextureAtlasData[outputTextureIndex + 0] = textureData[inputTextureIndex + 0];
voxelColorTextureAtlasData[outputTextureIndex + 1] = textureData[inputTextureIndex + 1];
voxelColorTextureAtlasData[outputTextureIndex + 2] = textureData[inputTextureIndex + 2];
voxelColorTextureAtlasData[outputTextureIndex + 3] = textureData[inputTextureIndex + 3];
}
}
}
stbi_image_free(textureData);
delete[] fileData;
}
voxelColorTextureAtlas = Texture2D::create(voxelColorTextureAtlasData, squareTextureSize * VOXEL_TEXTURE_SIZE_X, squareTextureSize * VOXEL_TEXTURE_SIZE_Y, 4);
// temp
Path savePath = DataStore::rootPath / DEER_TEMP_PATH / "voxel_texture_atlas.png";
DataStore::createFolder(DataStore::rootPath / DEER_TEMP_PATH);
stbi_write_png(savePath.generic_string().c_str(), squareTextureSize * VOXEL_TEXTURE_SIZE_X, squareTextureSize * VOXEL_TEXTURE_SIZE_Y, 4, voxelColorTextureAtlasData, squareTextureSize * VOXEL_TEXTURE_SIZE_X * 4);
}
int VoxelData::getVoxelTextureAtlasSize() {
return squareTextureSize;
}
Ref<Texture2D>& VoxelData::getVoxelColorTextureAtlas() {
return voxelColorTextureAtlas;
}
}

Some files were not shown because too many files have changed in this diff Show More