diff --git a/Deer/Include/DeerRender/Mesh.h b/Deer/Include/DeerRender/Mesh.h index 623a056..aaf1674 100644 --- a/Deer/Include/DeerRender/Mesh.h +++ b/Deer/Include/DeerRender/Mesh.h @@ -77,21 +77,8 @@ namespace Deer { static Scope buildResource(const BaseDataType& baseData); }; - namespace MeshManager { - uint16_t loadModel(const Path&); - uint16_t loadModel(const MeshData&, const Path&); - VertexArray& getModel(uint16_t model_id); - const Path& getModelName(uint16_t model_id); - - void unloadAllModels(); - } // namespace MeshManager - - namespace DataStore { - void saveModel(const MeshData&, const Path& name); - void loadModel(MeshData&, const Path& name); - - void saveBinModel(const MeshData&, const Path& name); - - void createExampleMeshData(); - } // namespace DataStore + namespace Builtin { + Resource cube(); + Resource sphere(); + } // namespace Builtin } // namespace Deer \ No newline at end of file diff --git a/Deer/Include/DeerRender/Shader.h b/Deer/Include/DeerRender/Shader.h index ed8e63e..8196b15 100644 --- a/Deer/Include/DeerRender/Shader.h +++ b/Deer/Include/DeerRender/Shader.h @@ -37,4 +37,9 @@ namespace Deer { namespace DataStore { void loadShader(ShaderData& data, const Path& name); } // namespace DataStore + + namespace Builtin { + Resource simpleShader(); + } + } // namespace Deer \ No newline at end of file diff --git a/Deer/src/DeerRender/Mesh/BuiltinCube.cpp b/Deer/src/DeerRender/Mesh/BuiltinCube.cpp new file mode 100644 index 0000000..76d8c0a --- /dev/null +++ b/Deer/src/DeerRender/Mesh/BuiltinCube.cpp @@ -0,0 +1,125 @@ +#include "DeerRender/Mesh.h" + +namespace Deer { + namespace Builtin { + + // Vertex positions (duplicated per face for correct normals) + VertexPosition cubeVertexPositions[] = { + // Front (+Z) + VertexPosition(-0.5f, -0.5f, 0.5f), + VertexPosition(0.5f, -0.5f, 0.5f), + VertexPosition(0.5f, 0.5f, 0.5f), + VertexPosition(-0.5f, 0.5f, 0.5f), + + // Back (-Z) + VertexPosition(0.5f, -0.5f, -0.5f), + VertexPosition(-0.5f, -0.5f, -0.5f), + VertexPosition(-0.5f, 0.5f, -0.5f), + VertexPosition(0.5f, 0.5f, -0.5f), + + // Left (-X) + VertexPosition(-0.5f, -0.5f, -0.5f), + VertexPosition(-0.5f, -0.5f, 0.5f), + VertexPosition(-0.5f, 0.5f, 0.5f), + VertexPosition(-0.5f, 0.5f, -0.5f), + + // Right (+X) + VertexPosition(0.5f, -0.5f, 0.5f), + VertexPosition(0.5f, -0.5f, -0.5f), + VertexPosition(0.5f, 0.5f, -0.5f), + VertexPosition(0.5f, 0.5f, 0.5f), + + // Top (+Y) + VertexPosition(-0.5f, 0.5f, 0.5f), + VertexPosition(0.5f, 0.5f, 0.5f), + VertexPosition(0.5f, 0.5f, -0.5f), + VertexPosition(-0.5f, 0.5f, -0.5f), + + // Bottom (-Y) + VertexPosition(-0.5f, -0.5f, -0.5f), + VertexPosition(0.5f, -0.5f, -0.5f), + VertexPosition(0.5f, -0.5f, 0.5f), + VertexPosition(-0.5f, -0.5f, 0.5f), + }; + + // Normals per vertex + VertexNormal cubeVertexNormals[] = { + // Front (+Z) + VertexNormal(0, 0, 64), + VertexNormal(0, 0, 64), + VertexNormal(0, 0, 64), + VertexNormal(0, 0, 64), + + // Back (-Z) + VertexNormal(0, 0, -64), + VertexNormal(0, 0, -64), + VertexNormal(0, 0, -64), + VertexNormal(0, 0, -64), + + // Left (-X) + VertexNormal(-64, 0, 0), + VertexNormal(-64, 0, 0), + VertexNormal(-64, 0, 0), + VertexNormal(-64, 0, 0), + + // Right (+X) + VertexNormal(64, 0, 0), + VertexNormal(64, 0, 0), + VertexNormal(64, 0, 0), + VertexNormal(64, 0, 0), + + // Top (+Y) + VertexNormal(0, 64, 0), + VertexNormal(0, 64, 0), + VertexNormal(0, 64, 0), + VertexNormal(0, 64, 0), + + // Bottom (-Y) + VertexNormal(0, -64, 0), + VertexNormal(0, -64, 0), + VertexNormal(0, -64, 0), + VertexNormal(0, -64, 0), + }; + + uint32_t cubeIndices[] = { + // Front + 0, 1, 2, 0, 2, 3, + // Back + 4, 5, 6, 4, 6, 7, + // Left + 8, 9, 10, 8, 10, 11, + // Right + 12, 13, 14, 12, 14, 15, + // Top + 16, 17, 18, 16, 18, 19, + // Bottom + 20, 21, 22, 20, 22, 23}; + + } // namespace Builtin + Resource Builtin::cube() { + Resource cubeMesh = ResourceManager::getResource("Builtin:Cube"); + if (cubeMesh.isValid()) + return cubeMesh; + + MeshData meshData; + + meshData.createVertices(24); // 4 vertices per face * 6 faces + meshData.createIndices(36); // 6 indices per face * 6 faces + + // Fill positions + for (size_t i = 0; i < 24; i++) + meshData.getVertexPosition()[i] = cubeVertexPositions[i]; + + // Fill normals + for (size_t i = 0; i < 24; i++) { + meshData.getVertexNormal()[i] = cubeVertexNormals[i]; + } + + // Fill indices + for (size_t i = 0; i < 36; i++) + meshData.getIndexData()[i] = cubeIndices[i]; + + return ResourceManager::loadResourceFromData(meshData, "Builtin:Cube"); + } + +} // namespace Deer diff --git a/Deer/src/DeerRender/Mesh/BuiltinSphere.cpp b/Deer/src/DeerRender/Mesh/BuiltinSphere.cpp new file mode 100644 index 0000000..5cf34c4 --- /dev/null +++ b/Deer/src/DeerRender/Mesh/BuiltinSphere.cpp @@ -0,0 +1,76 @@ +#include "DeerRender/Mesh.h" +#include + +namespace Deer { + namespace Builtin { + // Sphere parameters + constexpr int SPHERE_SEGMENTS = 32; // longitude + constexpr int SPHERE_RINGS = 16; // latitude + } // namespace Builtin + + Resource Builtin::sphere() { + Resource sphereMesh = ResourceManager::getResource("Builtin:Sphere"); + if (sphereMesh.isValid()) + return sphereMesh; + + MeshData meshData; + + const int vertexCount = (SPHERE_RINGS + 1) * (SPHERE_SEGMENTS + 1); + const int indexCount = SPHERE_RINGS * SPHERE_SEGMENTS * 6; + + meshData.createVertices(vertexCount); + meshData.createIndices(indexCount); + + VertexPosition* positions = meshData.getVertexPosition(); + VertexNormal* normals = meshData.getVertexNormal(); + uint32_t* indices = meshData.getIndexData(); + + // Generate vertices + int v = 0; + for (int y = 0; y <= SPHERE_RINGS; y++) { + float theta = y * M_PI / SPHERE_RINGS; // 0..pi + float sinTheta = sinf(theta); + float cosTheta = cosf(theta); + + for (int x = 0; x <= SPHERE_SEGMENTS; x++) { + float phi = x * 2.0f * M_PI / SPHERE_SEGMENTS; // 0..2pi + float sinPhi = sinf(phi); + float cosPhi = cosf(phi); + + float px = cosPhi * sinTheta * 0.5f; // scale to radius 0.5 + float py = cosTheta * 0.5f; + float pz = sinPhi * sinTheta * 0.5f; + + positions[v] = VertexPosition(px, py, pz); + + // Normal is just position normalized and scaled to int8_t range + normals[v] = VertexNormal( + static_cast(px * 127.0f), + static_cast(py * 127.0f), + static_cast(pz * 127.0f)); + + v++; + } + } + + // Generate indices + int idx = 0; + for (int y = 0; y < SPHERE_RINGS; y++) { + for (int x = 0; x < SPHERE_SEGMENTS; x++) { + int a = y * (SPHERE_SEGMENTS + 1) + x; + int b = a + SPHERE_SEGMENTS + 1; + + indices[idx++] = a; + indices[idx++] = a + 1; + indices[idx++] = b; + + indices[idx++] = b; + indices[idx++] = a + 1; + indices[idx++] = b + 1; + } + } + + return ResourceManager::loadResourceFromData(meshData, "Builtin:Sphere"); + } + +} // namespace Deer diff --git a/Deer/src/DeerRender/Scripting/InternalAPI/Resources.h b/Deer/src/DeerRender/Scripting/InternalAPI/Resources.h index 69b48e7..b5b361e 100644 --- a/Deer/src/DeerRender/Scripting/InternalAPI/Resources.h +++ b/Deer/src/DeerRender/Scripting/InternalAPI/Resources.h @@ -10,6 +10,7 @@ namespace Deer { class FrameBuffer; + class GPUMesh; namespace Scripting { template @@ -47,6 +48,5 @@ namespace Deer { bool frameBuffer_isValid(Resource&); void frameBuffer_resize(int, int, Resource&); Resource createFrameBuffer(std::string& name, int sixeX, int sizeY); - } // namespace Scripting } // namespace Deer \ No newline at end of file diff --git a/Deer/src/DeerRender/Scripting/ResourcesRegistry.cpp b/Deer/src/DeerRender/Scripting/ResourcesRegistry.cpp index bb91b2b..7afa8e1 100644 --- a/Deer/src/DeerRender/Scripting/ResourcesRegistry.cpp +++ b/Deer/src/DeerRender/Scripting/ResourcesRegistry.cpp @@ -49,6 +49,10 @@ namespace Deer { scriptEngine->SetDefaultNamespace("Resource"); REGISTER_GLOBAL_FUNC(scriptEngine, "FrameBuffer createFrameBuffer(string&in name, int sizeX, int sizeY)", createFrameBuffer); + scriptEngine->SetDefaultNamespace("Builtin"); + REGISTER_GLOBAL_FUNC(scriptEngine, "GPUMesh cube()", Builtin::cube); + REGISTER_GLOBAL_FUNC(scriptEngine, "GPUMesh sphere()", Builtin::sphere); + REGISTER_GLOBAL_FUNC(scriptEngine, "Shader simpleShader()", Builtin::simpleShader); scriptEngine->SetDefaultNamespace(""); } /* diff --git a/Deer/src/DeerRender/Shader/BuiltinSimpleShader.cpp b/Deer/src/DeerRender/Shader/BuiltinSimpleShader.cpp new file mode 100644 index 0000000..aa78e95 --- /dev/null +++ b/Deer/src/DeerRender/Shader/BuiltinSimpleShader.cpp @@ -0,0 +1,105 @@ +#include "DeerRender/Shader.h" + +namespace Deer { + namespace Builtin { + const char* vertexSimpleShader = R"( +#version 410 core + +// Vertex attributes +layout(location = 0) in vec3 v_position; +layout(location = 1) in vec3 v_normal; +layout(location = 2) in vec2 v_UV; + +// Outputs to fragment shader +out vec3 normalWS; +out vec3 normalVS; +out vec3 positionVS; + +// Uniforms +uniform mat4 u_worldMatrix; +uniform mat4 u_viewMatrix; + +void main() +{ + gl_Position = u_viewMatrix * u_worldMatrix * vec4(v_position, 1.0); + positionVS = gl_Position.xyz; + + mat3 normalMatrix = transpose(inverse(mat3(u_worldMatrix))); + normalWS = normalize(normalMatrix * v_normal); + + mat3 normalMatrixView = transpose(inverse(mat3(u_viewMatrix * u_worldMatrix))); + normalVS = normalize(normalMatrixView * v_normal); + +} + +)"; + + const char* fragmentSimpleShader = R"( +#version 410 core + +// Fragment outputs +layout(location = 0) out vec4 fragColor; +layout(location = 1) out int objectID; + +// Inputs from vertex shader +in vec3 normalWS; +in vec3 normalVS; +in vec3 positionVS; + +// Uniforms +uniform int u_objectID; + +void main() +{ + // Directional light + vec3 lightDir = normalize(vec3(1.0, 7.0, 3.0)); + + // Base light intensity + float light = clamp(dot(normalWS, lightDir) * 0.8 + 0.2, 0.0, 1.0); + + // Gradient from purple (dark) to cyan (lit) + vec3 gradientColor = mix( + vec3(0.9412, 0.4471, 0.7137), + vec3(1.0, 0.9725, 0.5255), + light + ); + + // Rim/fresnel effect + float rim = 0.5 - dot(normalWS, normalize(lightDir)) * 0.5; + float rimValue = rim * rim * rim * rim; + vec3 rimColor = vec3(1.0, 0.8, 0.5) * (rim * rim * rim * rim); + + vec3 mixedColor = mix( + gradientColor, + vec3(1, 1, 1) , + rimValue + ); + + //float fresnel = dot(, vec3(0, 0, -1)); + //fresnel = fresnel * fresnel; + + float fresnel = 1 + dot(normalize(positionVS), normalize(normalVS)); + fresnel = fresnel * fresnel * fresnel * fresnel; + + // Combine everything + fragColor = vec4(mixedColor + vec3(fresnel, fresnel, fresnel), 1.0); + + // Object ID output + objectID = u_objectID; +} +)"; + } // namespace Builtin + + Resource Builtin::simpleShader() { + Resource shader = ResourceManager::getResource("Builtin:SimpleShader"); + + if (shader.isValid()) + return shader; + + ShaderData shaderData; + shaderData.fragmentShader = fragmentSimpleShader; + shaderData.vertexShader = vertexSimpleShader; + + return ResourceManager::loadResourceFromData(shaderData, "Builtin:SimpleShader"); + } +} // namespace Deer \ No newline at end of file diff --git a/DeerStudio/src/DeerStudio/DeerStudio.cpp b/DeerStudio/src/DeerStudio/DeerStudio.cpp index fbd1e6f..d2054a9 100644 --- a/DeerStudio/src/DeerStudio/DeerStudio.cpp +++ b/DeerStudio/src/DeerStudio/DeerStudio.cpp @@ -105,8 +105,6 @@ namespace Deer { StudioPanel::render(); - ImGui::ShowDemoWindow(); - ImGui::End(); Engine::endRender(); diff --git a/Editor/Scripts/EntityManipulation/CameraProperties.as b/Editor/Scripts/EntityManipulation/CameraProperties.as index a1b342e..f4054f9 100644 --- a/Editor/Scripts/EntityManipulation/CameraProperties.as +++ b/Editor/Scripts/EntityManipulation/CameraProperties.as @@ -16,7 +16,7 @@ class CameraComponentRender { float nearZ = cameraComponent.nearZ; float farZ = cameraComponent.farZ; - fov = ImGui::magicSlider("FOV", fov, 0.1f); + fov = ImGui::magicSlider("Fov", fov, 0.1f); if (fov > 180.0f) fov = 180.0f; if (fov < 1.0f) fov = 1.0f; diff --git a/Editor/Scripts/TreeExplorer/Tree.as b/Editor/Scripts/TreeExplorer/Tree.as index d068d22..9d67029 100644 --- a/Editor/Scripts/TreeExplorer/Tree.as +++ b/Editor/Scripts/TreeExplorer/Tree.as @@ -70,7 +70,6 @@ class EntityNodeUI { } void renderInteraction() { - // Drag and drop ImGui::dragDropSource("ENTITY", any(entity), entity.name); ImGui::dragDropTarget("ENTITY", ReciverFunction(this.entityDrop)); @@ -114,6 +113,9 @@ class EntityNodeUI { entity.createChild("node"); rootPanel.onInit(); } + + ImGui::separator(); + ImGui::subMenu("3d Object", SimpleFunction(this.render3dObjectMenu)); } else { if (ImGui::menuItem("Add child")) { entity.createChild("node"); @@ -129,6 +131,33 @@ class EntityNodeUI { entity.destroy(); rootPanel.onInit(); } + + ImGui::separator(); + ImGui::subMenu("3d Object", SimpleFunction(this.render3dObjectMenu)); + } + } + + void render3dObjectMenu() { + if (ImGui::menuItem("Cube")) { + Entity child = entity.createChild("node"); + MeshComponent childMesh = child.addComponent(); + childMesh.meshResource = Builtin::cube(); + + ShaderComponent childShader = child.addComponent(); + childShader.shader = Builtin::simpleShader(); + + rootPanel.onInit(); + } + + if (ImGui::menuItem("Sphere")) { + Entity child = entity.createChild("node"); + MeshComponent childMesh = child.addComponent(); + childMesh.meshResource = Builtin::sphere(); + + ShaderComponent childShader = child.addComponent(); + childShader.shader = Builtin::simpleShader(); + + rootPanel.onInit(); } } @@ -181,7 +210,6 @@ class TreePanel : Panel { } void onImGui() { - if (infoChanged) { Entity root = World::getRoot(); @rootNode = @EntityNodeUI(root, this); @@ -189,6 +217,10 @@ class TreePanel : Panel { infoChanged = false; } - rootNode.render(); + ImGui::contextMenuPopup("POP_ROOT", SimpleFunction(rootNode.renderContextMenu)); + + for (uint i = 0; i < rootNode.children.length(); i++) { + if (rootNode.children[i].entity.exists) rootNode.children[i].render(); + } } } diff --git a/Resources/shader.glsl b/Resources/shader.glsl index 37806b0..dda8e26 100644 --- a/Resources/shader.glsl +++ b/Resources/shader.glsl @@ -1,43 +1,83 @@ #type vertex #version 410 core +// Vertex attributes layout(location = 0) in vec3 v_position; layout(location = 1) in vec3 v_normal; layout(location = 2) in vec2 v_UV; -out vec3 normal; -out vec2 uv; +// Outputs to fragment shader +out vec3 normalWS; +out vec3 normalVS; +out vec3 positionVS; -uniform mat4 u_viewMatrix; +// Uniforms uniform mat4 u_worldMatrix; +uniform mat4 u_viewMatrix; void main() { - uv = v_UV; - normal = mat3(u_worldMatrix) * v_normal; // transform normals into world space - gl_Position = u_viewMatrix * u_worldMatrix * vec4(v_position, 1.0); + positionVS = gl_Position.xyz; + + mat3 normalMatrix = transpose(inverse(mat3(u_worldMatrix))); + normalWS = normalize(normalMatrix * v_normal); + + mat3 normalMatrixView = transpose(inverse(mat3(u_viewMatrix * u_worldMatrix))); + normalVS = normalize(normalMatrixView * v_normal); + } #type fragment #version 410 core +// Fragment outputs layout(location = 0) out vec4 fragColor; layout(location = 1) out int objectID; -in vec3 normal; -in vec2 uv; +// Inputs from vertex shader +in vec3 normalWS; +in vec3 normalVS; +in vec3 positionVS; -uniform sampler2D u_Texture; +// Uniforms uniform int u_objectID; void main() { - vec3 lightDir = normalize(vec3(1.0, 7.0, 3.0)) * 0.2f + 0.8f; - float light = clamp(dot(normalize(normal), lightDir), 0.1, 1.0); + // Directional light + vec3 lightDir = normalize(vec3(1.0, 7.0, 3.0)); - fragColor = vec4(light, light, light, 1); - // fragColor = vec4(uv.r, uv.g, 0, 1); + // Base light intensity + float light = clamp(dot(normalWS, lightDir) * 0.8 + 0.2, 0.0, 1.0); + + // Gradient from purple (dark) to cyan (lit) + vec3 gradientColor = mix( + vec3(0.9412, 0.4471, 0.7137), + vec3(1.0, 0.9725, 0.5255), + light + ); + + // Rim/fresnel effect + float rim = 0.5 - dot(normalWS, normalize(lightDir)) * 0.5; + float rimValue = rim * rim * rim * rim; + vec3 rimColor = vec3(1.0, 0.8, 0.5) * (rim * rim * rim * rim); + + vec3 mixedColor = mix( + gradientColor, + vec3(1, 1, 1) , + rimValue + ); + + //float fresnel = dot(, vec3(0, 0, -1)); + //fresnel = fresnel * fresnel; + + float fresnel = 1 + dot(normalize(positionVS), normalize(normalVS)); + fresnel = fresnel * fresnel * fresnel * fresnel; + + // Combine everything + fragColor = vec4(mixedColor + vec3(fresnel, fresnel, fresnel), 1.0); + + // Object ID output objectID = u_objectID; -} - +} \ No newline at end of file diff --git a/imgui.ini b/imgui.ini index 9c2d509..aa4b925 100644 --- a/imgui.ini +++ b/imgui.ini @@ -67,7 +67,7 @@ DockSpace ID=0x0AC2E849 Window=0xD0388BC8 Pos=0,26 Size=2560,1345 Split=Y DockNode ID=0x00000003 Parent=0x0AC2E849 SizeRef=2560,926 Split=X DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1230,1336 Split=X Selected=0x16E3C1E7 DockNode ID=0x00000005 Parent=0x00000001 SizeRef=569,572 CentralNode=1 Selected=0x16E3C1E7 - DockNode ID=0x00000006 Parent=0x00000001 SizeRef=1299,572 Selected=0x5E5F7166 + DockNode ID=0x00000006 Parent=0x00000001 SizeRef=1299,572 Selected=0x0F5FFC8C DockNode ID=0x00000002 Parent=0x00000003 SizeRef=688,1336 Selected=0x9876A79B DockNode ID=0x00000004 Parent=0x0AC2E849 SizeRef=2560,418 Selected=0x018A0F9B