diff --git a/.github/labeler.yml b/.github/labeler.yml index 8a85242..fbdcd12 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,7 +1,7 @@ # Labeler Configuration # Labels PRs based on changed files using actions/labeler@v5 syntax -CI/CD: +'CI/CD': - changed-files: - any-glob-to-any-file: '.github/workflows/**/*' @@ -17,7 +17,7 @@ Vendor: - 'Vendor/**/*' - '.gitmodules' -AI Agents: +'AI Agents': - changed-files: - any-glob-to-any-file: - '.agents/**/*' diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 0303c13..cdd7b15 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -14,7 +14,9 @@ jobs: steps: - uses: actions/checkout@v4 with: - ref: ${{ github.head_ref }} + repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} + ref: ${{ github.event.pull_request.head.ref || github.ref_name }} + token: ${{ secrets.GITHUB_TOKEN }} - name: Get changed files id: changed-files @@ -46,7 +48,19 @@ jobs: } - name: Commit changes - if: steps.changed-files.outputs.any_changed == 'true' + if: steps.changed-files.outputs.any_changed == 'true' && github.event_name == 'push' uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: "Fix CI: Automated Clang-Format" + + - name: Verify Clang-Format on Pull Requests + if: steps.changed-files.outputs.any_changed == 'true' && github.event_name == 'pull_request' + shell: pwsh + run: | + $status = git status --porcelain + if ($status) { + Write-Error "Clang-Format check failed! Code formatting issues detected. Please run clang-format locally." + git diff + exit 1 + } + diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index c3471e9..ce8aedc 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -1,15 +1,16 @@ name: Pull Request Labeler on: - - pull_request + - pull_request_target jobs: label: - runs-on: windows-latest + runs-on: ubuntu-latest permissions: contents: read pull-requests: write steps: + - uses: actions/checkout@v4 - uses: actions/labeler@v5 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.gitignore b/.gitignore index 7dce437..8a45a20 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # Binaries Bin/ Bin-Intermediate/ +Build_Release/ +Build_Development/ **/build/ **/bin/ **/obj/ @@ -33,8 +35,14 @@ recent_projects.txt .idea/ .Rider*/ +# Xcode / Mac IDE +*.xcodeproj/ +*.xcworkspace/ +*.xe + # Mac .DS_Store +TimeEditor/Info.plist # Sandbox diff --git a/Engine/Include/Renderer/GraphicsAPI.hpp b/Engine/Include/Renderer/GraphicsAPI.hpp index 224a0df..ed61441 100644 --- a/Engine/Include/Renderer/GraphicsAPI.hpp +++ b/Engine/Include/Renderer/GraphicsAPI.hpp @@ -8,6 +8,7 @@ enum class GraphicsAPI OpenGL, OpenGLES, Vulkan, - DirectX11 + DirectX11, + Metal }; } diff --git a/Engine/Include/Renderer/Metal/MetalFramebuffer.hpp b/Engine/Include/Renderer/Metal/MetalFramebuffer.hpp new file mode 100644 index 0000000..0052611 --- /dev/null +++ b/Engine/Include/Renderer/Metal/MetalFramebuffer.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "Renderer/Framebuffer.hpp" + +namespace TE +{ + +class MetalFramebuffer : public Framebuffer +{ +public: + MetalFramebuffer(const FramebufferSpecification &spec); + virtual ~MetalFramebuffer(); + + void Invalidate(); + + virtual void Bind() override; + virtual void Unbind() override; + virtual void Resize(uint32_t width, uint32_t height) override; + + virtual uint32_t GetColorAttachmentRendererID() const override { return 0; } + virtual const FramebufferSpecification &GetSpecification() const override { return m_Specification; } + + void *GetColorTexture() const { return m_ColorTexture; } + +private: + void *m_ColorTexture = nullptr; + void *m_DepthTexture = nullptr; + FramebufferSpecification m_Specification; +}; + +} // namespace TE diff --git a/Engine/Include/Renderer/Metal/MetalIndexBuffer.hpp b/Engine/Include/Renderer/Metal/MetalIndexBuffer.hpp new file mode 100644 index 0000000..9499536 --- /dev/null +++ b/Engine/Include/Renderer/Metal/MetalIndexBuffer.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "Renderer/IndexBuffer.hpp" + +namespace TE +{ +class MetalIndexBuffer : public IndexBuffer +{ +public: + MetalIndexBuffer(uint32_t *indices, uint32_t count); + virtual ~MetalIndexBuffer(); + + virtual void Bind() const override; + virtual void Unbind() const override; + virtual void SetData(uint32_t *indices, uint32_t count) const override; + + virtual uint32_t GetCount() const override { return m_Count; } + void *GetBuffer() const { return m_Buffer; } + +private: + void *m_Buffer = nullptr; + uint32_t m_Count = 0; +}; +} // namespace TE diff --git a/Engine/Include/Renderer/Metal/MetalRendererAPI.hpp b/Engine/Include/Renderer/Metal/MetalRendererAPI.hpp new file mode 100644 index 0000000..90136cc --- /dev/null +++ b/Engine/Include/Renderer/Metal/MetalRendererAPI.hpp @@ -0,0 +1,32 @@ +#pragma once +#include "Renderer/RendererAPI.hpp" + +namespace TE +{ + +class MetalRendererAPI : public RendererAPI +{ +public: + virtual ~MetalRendererAPI() override = default; + + virtual void Init() override; + virtual void SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) override; + virtual void SetClearColor(const glm::vec4 &color) override; + virtual void Clear() override; + virtual void DrawIndexed(uint32_t vao, uint32_t indexCount) override; + virtual void SetBlendMode(int blendMode) override; + + virtual bool LoadLoader(void *(*loadProc)(const char *)) override; + virtual std::string GetVersionString() override; + virtual std::string GetGPUVendor() override; + virtual std::string GetGPURenderer() override; + + virtual void GetViewport(int *viewport) override; + virtual void GetClearColor(float *color) override; + virtual void ReadPixelsRGBA(int x, int y, int width, int height, void *outPixels) override; + virtual void SetBlendFunc(BlendFactor src, BlendFactor dst) override; + virtual void SetBlendFuncSeparate(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, + BlendFactor dstAlpha) override; +}; + +} // namespace TE diff --git a/Engine/Include/Renderer/Metal/MetalShader.hpp b/Engine/Include/Renderer/Metal/MetalShader.hpp new file mode 100644 index 0000000..1e7e312 --- /dev/null +++ b/Engine/Include/Renderer/Metal/MetalShader.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "Renderer/Shader.hpp" + +namespace TE +{ +class MetalShader : public Shader +{ +public: + MetalShader(const std::string &vertexSrc, const std::string &fragmentSrc); + MetalShader(const std::string &computeSrc); + virtual ~MetalShader(); + + virtual void Bind() const override; + virtual void Unbind() const override; + + virtual void SetUniformMat4(const std::string &name, const glm::mat4 &value) override; + virtual void SetUniform4f(const std::string &name, const glm::vec4 &value) override; + virtual void SetUniform3f(const std::string &name, const glm::vec3 &value) override; + virtual void SetUniform2f(const std::string &name, const glm::vec2 &value) override; + virtual void SetUniform1f(const std::string &name, float value) override; + virtual void SetUniform1i(const std::string &name, int value) override; + + void *GetLibrary() const { return m_Library; } + +private: + void *m_Library = nullptr; + void *m_PipelineState = nullptr; +}; +} // namespace TE diff --git a/Engine/Include/Renderer/Metal/MetalVertexArray.hpp b/Engine/Include/Renderer/Metal/MetalVertexArray.hpp new file mode 100644 index 0000000..f5b094f --- /dev/null +++ b/Engine/Include/Renderer/Metal/MetalVertexArray.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "Renderer/VertexArray.hpp" + +namespace TE +{ +class MetalVertexArray : public VertexArray +{ +public: + MetalVertexArray(); + virtual ~MetalVertexArray(); + + virtual void Bind() const override; + virtual void Unbind() const override; + + virtual void AddVertexBuffer(VertexBuffer *vertexBuffer) override; + virtual void SetIndexBuffer(IndexBuffer *indexBuffer) override; + + virtual uint32_t GetRendererID() const override { return 0; } + + VertexBuffer *GetVertexBuffer() const { return m_VertexBuffer; } + IndexBuffer *GetIndexBuffer() const { return m_IndexBuffer; } + +private: + VertexBuffer *m_VertexBuffer = nullptr; + IndexBuffer *m_IndexBuffer = nullptr; +}; +} // namespace TE diff --git a/Engine/Include/Renderer/Metal/MetalVertexBuffer.hpp b/Engine/Include/Renderer/Metal/MetalVertexBuffer.hpp new file mode 100644 index 0000000..6a15188 --- /dev/null +++ b/Engine/Include/Renderer/Metal/MetalVertexBuffer.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "Renderer/VertexBuffer.hpp" + +namespace TE +{ +class MetalVertexBuffer : public VertexBuffer +{ +public: + MetalVertexBuffer(float *vertices, uint32_t size); + virtual ~MetalVertexBuffer(); + + virtual void Bind() const override; + virtual void Unbind() const override; + virtual void SetData(float *vertices, uint32_t size) const override; + + void *GetBuffer() const { return m_Buffer; } + +private: + void *m_Buffer = nullptr; + uint32_t m_Size = 0; +}; +} // namespace TE diff --git a/Engine/src/Renderer/Framebuffer.cpp b/Engine/src/Renderer/Framebuffer.cpp index 32ff38d..a4a6d4b 100644 --- a/Engine/src/Renderer/Framebuffer.cpp +++ b/Engine/src/Renderer/Framebuffer.cpp @@ -8,6 +8,10 @@ #include "Renderer/OpenGLES/OpenGLESFramebuffer.hpp" #endif +#ifdef TE_SUPPORT_METAL +#include "Renderer/Metal/MetalFramebuffer.hpp" +#endif + namespace TE { @@ -24,6 +28,10 @@ std::shared_ptr Framebuffer::Create(const FramebufferSpecification #if defined(TE_PLATFORM_MOBILE) case GraphicsAPI::OpenGLES: return std::make_shared(spec); +#endif +#ifdef TE_SUPPORT_METAL + case GraphicsAPI::Metal: + return std::make_shared(spec); #endif default: break; diff --git a/Engine/src/Renderer/IndexBuffer.cpp b/Engine/src/Renderer/IndexBuffer.cpp index 6eab081..9427dfd 100644 --- a/Engine/src/Renderer/IndexBuffer.cpp +++ b/Engine/src/Renderer/IndexBuffer.cpp @@ -13,6 +13,9 @@ #ifdef TE_SUPPORT_VULKAN #include "Renderer/Vulkan/VulkanIndexBuffer.hpp" #endif +#ifdef TE_SUPPORT_METAL +#include "Renderer/Metal/MetalIndexBuffer.hpp" +#endif namespace TE { @@ -38,6 +41,10 @@ IndexBuffer *IndexBuffer::Create(uint32_t *indices, uint32_t Count) #ifdef TE_SUPPORT_DIRECTX11 case GraphicsAPI::DirectX11: return new DirectX11IndexBuffer(indices, Count); +#endif +#ifdef TE_SUPPORT_METAL + case GraphicsAPI::Metal: + return new MetalIndexBuffer(indices, Count); #endif default: return nullptr; diff --git a/Engine/src/Renderer/Metal/MetalFramebuffer.mm b/Engine/src/Renderer/Metal/MetalFramebuffer.mm new file mode 100644 index 0000000..81dc61c --- /dev/null +++ b/Engine/src/Renderer/Metal/MetalFramebuffer.mm @@ -0,0 +1,67 @@ +#include "Renderer/Metal/MetalFramebuffer.hpp" +#include "Core/Log.h" + +#ifdef TE_SUPPORT_METAL +#import +#endif + +namespace TE +{ + +MetalFramebuffer::MetalFramebuffer(const FramebufferSpecification &spec) + : m_Specification(spec) +{ + Invalidate(); +} + +MetalFramebuffer::~MetalFramebuffer() +{ +#ifdef TE_SUPPORT_METAL + if (m_ColorTexture) + { + id tex = (__bridge_transfer id)m_ColorTexture; + tex = nil; + m_ColorTexture = nullptr; + } + if (m_DepthTexture) + { + id tex = (__bridge_transfer id)m_DepthTexture; + tex = nil; + m_DepthTexture = nullptr; + } +#endif +} + +void MetalFramebuffer::Invalidate() +{ +#ifdef TE_SUPPORT_METAL + id device = MTLCreateSystemDefaultDevice(); + if (!device || m_Specification.Width == 0 || m_Specification.Height == 0) + return; + + MTLTextureDescriptor *colorDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm + width:m_Specification.Width + height:m_Specification.Height + mipmapped:NO]; + colorDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead; + id colorTex = [device newTextureWithDescriptor:colorDesc]; + m_ColorTexture = (__bridge_retained void *)colorTex; +#endif +} + +void MetalFramebuffer::Bind() +{ +} + +void MetalFramebuffer::Unbind() +{ +} + +void MetalFramebuffer::Resize(uint32_t width, uint32_t height) +{ + m_Specification.Width = width; + m_Specification.Height = height; + Invalidate(); +} + +} // namespace TE diff --git a/Engine/src/Renderer/Metal/MetalIndexBuffer.mm b/Engine/src/Renderer/Metal/MetalIndexBuffer.mm new file mode 100644 index 0000000..555793b --- /dev/null +++ b/Engine/src/Renderer/Metal/MetalIndexBuffer.mm @@ -0,0 +1,58 @@ +#include "Renderer/Metal/MetalIndexBuffer.hpp" +#include "Core/Log.h" + +#ifdef TE_SUPPORT_METAL +#import +#endif + +namespace TE +{ + +MetalIndexBuffer::MetalIndexBuffer(uint32_t *indices, uint32_t count) + : m_Count(count) +{ +#ifdef TE_SUPPORT_METAL + id device = MTLCreateSystemDefaultDevice(); + if (device && indices && count > 0) + { + uint32_t size = count * sizeof(uint32_t); + id buffer = [device newBufferWithBytes:indices + length:size + options:MTLResourceStorageModeShared]; + m_Buffer = (__bridge_retained void *)buffer; + } +#endif +} + +MetalIndexBuffer::~MetalIndexBuffer() +{ +#ifdef TE_SUPPORT_METAL + if (m_Buffer) + { + id buffer = (__bridge_transfer id)m_Buffer; + buffer = nil; + m_Buffer = nullptr; + } +#endif +} + +void MetalIndexBuffer::Bind() const +{ +} + +void MetalIndexBuffer::Unbind() const +{ +} + +void MetalIndexBuffer::SetData(uint32_t *indices, uint32_t count) const +{ +#ifdef TE_SUPPORT_METAL + if (m_Buffer && indices && count > 0) + { + id buffer = (__bridge id)m_Buffer; + memcpy([buffer contents], indices, count * sizeof(uint32_t)); + } +#endif +} + +} // namespace TE diff --git a/Engine/src/Renderer/Metal/MetalRendererAPI.mm b/Engine/src/Renderer/Metal/MetalRendererAPI.mm new file mode 100644 index 0000000..cc911e2 --- /dev/null +++ b/Engine/src/Renderer/Metal/MetalRendererAPI.mm @@ -0,0 +1,93 @@ +#include "Renderer/Metal/MetalRendererAPI.hpp" +#include "Renderer/RendererContext.hpp" +#include "Core/Log.h" + +#ifdef TE_SUPPORT_METAL +#import +#import +#endif + +namespace TE +{ + +void MetalRendererAPI::Init() +{ + TE_CORE_INFO("MetalRendererAPI initialized."); +} + +void MetalRendererAPI::SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) +{ +} + +void MetalRendererAPI::SetClearColor(const glm::vec4 &color) +{ +} + +void MetalRendererAPI::Clear() +{ +} + +void MetalRendererAPI::DrawIndexed(uint32_t vao, uint32_t indexCount) +{ +} + +void MetalRendererAPI::SetBlendMode(int blendMode) +{ +} + +bool MetalRendererAPI::LoadLoader(void *(*loadProc)(const char *)) +{ + return true; +} + +std::string MetalRendererAPI::GetVersionString() +{ +#ifdef TE_SUPPORT_METAL + id device = MTLCreateSystemDefaultDevice(); + if (device) + { + return std::string("Metal ") + [device name].UTF8String; + } +#endif + return "Metal 1.0"; +} + +std::string MetalRendererAPI::GetGPUVendor() +{ + return "Apple"; +} + +std::string MetalRendererAPI::GetGPURenderer() +{ +#ifdef TE_SUPPORT_METAL + id device = MTLCreateSystemDefaultDevice(); + if (device) + { + return [device name].UTF8String; + } +#endif + return "Apple Metal GPU"; +} + +void MetalRendererAPI::GetViewport(int *viewport) +{ +} + +void MetalRendererAPI::GetClearColor(float *color) +{ +} + +void MetalRendererAPI::ReadPixelsRGBA(int x, int y, int width, int height, void *outPixels) +{ +} + +void MetalRendererAPI::SetBlendFunc(BlendFactor src, BlendFactor dst) +{ +} + +void MetalRendererAPI::SetBlendFuncSeparate(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, + BlendFactor dstAlpha) +{ +} + +} // namespace TE diff --git a/Engine/src/Renderer/Metal/MetalShader.mm b/Engine/src/Renderer/Metal/MetalShader.mm new file mode 100644 index 0000000..b74fbff --- /dev/null +++ b/Engine/src/Renderer/Metal/MetalShader.mm @@ -0,0 +1,93 @@ +#include "Renderer/Metal/MetalShader.hpp" +#include "Core/Log.h" + +#ifdef TE_SUPPORT_METAL +#import +#endif + +namespace TE +{ + +MetalShader::MetalShader(const std::string &vertexSrc, const std::string &fragmentSrc) +{ +#ifdef TE_SUPPORT_METAL + id device = MTLCreateSystemDefaultDevice(); + if (device) + { + NSString *src = [NSString stringWithUTF8String:(vertexSrc + "\n" + fragmentSrc).c_str()]; + NSError *error = nil; + id lib = [device newLibraryWithSource:src options:nil error:&error]; + if (lib) + { + m_Library = (__bridge_retained void *)lib; + } + else if (error) + { + TE_CORE_WARN("MetalShader compilation warning: {0}", [error.localizedDescription UTF8String]); + } + } +#endif +} + +MetalShader::MetalShader(const std::string &computeSrc) +{ +#ifdef TE_SUPPORT_METAL + id device = MTLCreateSystemDefaultDevice(); + if (device) + { + NSString *src = [NSString stringWithUTF8String:computeSrc.c_str()]; + NSError *error = nil; + id lib = [device newLibraryWithSource:src options:nil error:&error]; + if (lib) + { + m_Library = (__bridge_retained void *)lib; + } + } +#endif +} + +MetalShader::~MetalShader() +{ +#ifdef TE_SUPPORT_METAL + if (m_Library) + { + id lib = (__bridge_transfer id)m_Library; + lib = nil; + m_Library = nullptr; + } +#endif +} + +void MetalShader::Bind() const +{ +} + +void MetalShader::Unbind() const +{ +} + +void MetalShader::SetUniformMat4(const std::string &name, const glm::mat4 &value) +{ +} + +void MetalShader::SetUniform4f(const std::string &name, const glm::vec4 &value) +{ +} + +void MetalShader::SetUniform3f(const std::string &name, const glm::vec3 &value) +{ +} + +void MetalShader::SetUniform2f(const std::string &name, const glm::vec2 &value) +{ +} + +void MetalShader::SetUniform1f(const std::string &name, float value) +{ +} + +void MetalShader::SetUniform1i(const std::string &name, int value) +{ +} + +} // namespace TE diff --git a/Engine/src/Renderer/Metal/MetalVertexArray.mm b/Engine/src/Renderer/Metal/MetalVertexArray.mm new file mode 100644 index 0000000..ee8f994 --- /dev/null +++ b/Engine/src/Renderer/Metal/MetalVertexArray.mm @@ -0,0 +1,32 @@ +#include "Renderer/Metal/MetalVertexArray.hpp" + +namespace TE +{ + +MetalVertexArray::MetalVertexArray() +{ +} + +MetalVertexArray::~MetalVertexArray() +{ +} + +void MetalVertexArray::Bind() const +{ +} + +void MetalVertexArray::Unbind() const +{ +} + +void MetalVertexArray::AddVertexBuffer(VertexBuffer *vertexBuffer) +{ + m_VertexBuffer = vertexBuffer; +} + +void MetalVertexArray::SetIndexBuffer(IndexBuffer *indexBuffer) +{ + m_IndexBuffer = indexBuffer; +} + +} // namespace TE diff --git a/Engine/src/Renderer/Metal/MetalVertexBuffer.mm b/Engine/src/Renderer/Metal/MetalVertexBuffer.mm new file mode 100644 index 0000000..57ee9c1 --- /dev/null +++ b/Engine/src/Renderer/Metal/MetalVertexBuffer.mm @@ -0,0 +1,57 @@ +#include "Renderer/Metal/MetalVertexBuffer.hpp" +#include "Core/Log.h" + +#ifdef TE_SUPPORT_METAL +#import +#endif + +namespace TE +{ + +MetalVertexBuffer::MetalVertexBuffer(float *vertices, uint32_t size) + : m_Size(size) +{ +#ifdef TE_SUPPORT_METAL + id device = MTLCreateSystemDefaultDevice(); + if (device && vertices && size > 0) + { + id buffer = [device newBufferWithBytes:vertices + length:size + options:MTLResourceStorageModeShared]; + m_Buffer = (__bridge_retained void *)buffer; + } +#endif +} + +MetalVertexBuffer::~MetalVertexBuffer() +{ +#ifdef TE_SUPPORT_METAL + if (m_Buffer) + { + id buffer = (__bridge_transfer id)m_Buffer; + buffer = nil; + m_Buffer = nullptr; + } +#endif +} + +void MetalVertexBuffer::Bind() const +{ +} + +void MetalVertexBuffer::Unbind() const +{ +} + +void MetalVertexBuffer::SetData(float *vertices, uint32_t size) const +{ +#ifdef TE_SUPPORT_METAL + if (m_Buffer && vertices && size > 0) + { + id buffer = (__bridge id)m_Buffer; + memcpy([buffer contents], vertices, size); + } +#endif +} + +} // namespace TE diff --git a/Engine/src/Renderer/RendererAPI.cpp b/Engine/src/Renderer/RendererAPI.cpp index db6a9ce..a88ec06 100644 --- a/Engine/src/Renderer/RendererAPI.cpp +++ b/Engine/src/Renderer/RendererAPI.cpp @@ -17,6 +17,9 @@ #include "Renderer/DirectX11/DirectX11RendererAPI.hpp" #endif +#ifdef TE_SUPPORT_METAL +#include "Renderer/Metal/MetalRendererAPI.hpp" +#endif namespace TE { GraphicsAPI RendererAPI::GetAPI() { return RendererContext::GetAPI(); } @@ -50,6 +53,12 @@ std::unique_ptr RendererAPI::Create() return std::make_unique(); #else return nullptr; +#endif + case GraphicsAPI::Metal: +#ifdef TE_SUPPORT_METAL + return std::make_unique(); +#else + return nullptr; #endif } return nullptr; diff --git a/Engine/src/Renderer/Shader.cpp b/Engine/src/Renderer/Shader.cpp index 8995a4a..dbadfb3 100644 --- a/Engine/src/Renderer/Shader.cpp +++ b/Engine/src/Renderer/Shader.cpp @@ -13,6 +13,9 @@ #ifdef TE_SUPPORT_VULKAN #include "Renderer/Vulkan/VulkanShader.hpp" #endif +#ifdef TE_SUPPORT_METAL +#include "Renderer/Metal/MetalShader.hpp" +#endif namespace TE { @@ -38,6 +41,10 @@ Shader *Shader::Create(const std::string &vertexSrc, const std::string &fragment #ifdef TE_SUPPORT_DIRECTX11 case GraphicsAPI::DirectX11: return new DirectX11Shader(vertexSrc, fragmentSrc); +#endif +#ifdef TE_SUPPORT_METAL + case GraphicsAPI::Metal: + return new MetalShader(vertexSrc, fragmentSrc); #endif default: return nullptr; diff --git a/Engine/src/Renderer/VertexArray.cpp b/Engine/src/Renderer/VertexArray.cpp index d01c996..abc6bc2 100644 --- a/Engine/src/Renderer/VertexArray.cpp +++ b/Engine/src/Renderer/VertexArray.cpp @@ -13,6 +13,9 @@ #ifdef TE_SUPPORT_VULKAN #include "Renderer/Vulkan/VulkanVertexArray.hpp" #endif +#ifdef TE_SUPPORT_METAL +#include "Renderer/Metal/MetalVertexArray.hpp" +#endif namespace TE { @@ -38,6 +41,10 @@ VertexArray *VertexArray::Create() #ifdef TE_SUPPORT_DIRECTX11 case GraphicsAPI::DirectX11: return new DirectX11VertexArray(); +#endif +#ifdef TE_SUPPORT_METAL + case GraphicsAPI::Metal: + return new MetalVertexArray(); #endif default: return nullptr; diff --git a/Engine/src/Renderer/VertexBuffer.cpp b/Engine/src/Renderer/VertexBuffer.cpp index d66ec2a..badb29c 100644 --- a/Engine/src/Renderer/VertexBuffer.cpp +++ b/Engine/src/Renderer/VertexBuffer.cpp @@ -13,6 +13,9 @@ #ifdef TE_SUPPORT_VULKAN #include "Renderer/Vulkan/VulkanVertexBuffer.hpp" #endif +#ifdef TE_SUPPORT_METAL +#include "Renderer/Metal/MetalVertexBuffer.hpp" +#endif namespace TE { @@ -40,6 +43,10 @@ VertexBuffer *VertexBuffer::Create(float *vertices, uint32_t size) #ifdef TE_SUPPORT_DIRECTX11 case GraphicsAPI::DirectX11: return new DirectX11VertexBuffer(vertices, size); +#endif +#ifdef TE_SUPPORT_METAL + case GraphicsAPI::Metal: + return new MetalVertexBuffer(vertices, size); #endif } return nullptr; diff --git a/Engine/src/Utils/Platform/Unix/UnixPlatformUtils.cpp b/Engine/src/Utils/Platform/Unix/UnixPlatformUtils.cpp index 94728f5..03d9ead 100644 --- a/Engine/src/Utils/Platform/Unix/UnixPlatformUtils.cpp +++ b/Engine/src/Utils/Platform/Unix/UnixPlatformUtils.cpp @@ -9,21 +9,153 @@ namespace TE { -std::string PlatformUtils::OpenFolder(const char *initialPath) { return ""; } +std::string PlatformUtils::OpenFolder(const char *initialPath) +{ +#ifdef __APPLE__ + std::string result = ""; + char buffer[128]; + // osascript prompt will bring up the native finder folder selector + FILE *pipe = popen("osascript -e 'POSIX path of (choose folder with prompt \"Select Folder\")' 2>/dev/null", "r"); + if (pipe) + { + while (fgets(buffer, sizeof(buffer), pipe) != nullptr) + { + result += buffer; + } + pclose(pipe); + } -std::string PlatformUtils::OpenFile(const char *filter) { return ""; } + // Strip trailing newline + if (!result.empty() && result.back() == '\n') + { + result.pop_back(); + } + return result; +#else + return ""; +#endif +} + +std::string PlatformUtils::OpenFile(const char *filter) +{ +#ifdef __APPLE__ + std::string appleScriptCmd = "osascript -e 'POSIX path of (choose file with prompt \"Select File\""; -std::string PlatformUtils::SaveFile(const char *filter) { return ""; } + // Parse extensions from the filter string (e.g. "*.png;*.jpg") + if (filter != nullptr) + { + std::string filterStr(filter); + std::vector extensions; + size_t pos = 0; + + // Simple scan to find extensions starting with '.' + while ((pos = filterStr.find('.', pos)) != std::string::npos) + { + pos++; // Move past the '.' + std::string ext = ""; + while (pos < filterStr.size() && std::isalnum(filterStr[pos])) + { + ext += filterStr[pos]; + pos++; + } + if (!ext.empty()) + { + extensions.push_back(ext); + } + } + + if (!extensions.empty()) + { + appleScriptCmd += " of type {"; + for (size_t i = 0; i < extensions.size(); i++) + { + appleScriptCmd += "\"" + extensions[i] + "\""; + if (i < extensions.size() - 1) + appleScriptCmd += ", "; + } + appleScriptCmd += "}"; + } + } + + appleScriptCmd += ")' 2>/dev/null"; + + std::string result = ""; + char buffer[128]; + FILE *pipe = popen(appleScriptCmd.c_str(), "r"); + if (pipe) + { + while (fgets(buffer, sizeof(buffer), pipe) != nullptr) + { + result += buffer; + } + pclose(pipe); + } + + if (!result.empty() && result.back() == '\n') + { + result.pop_back(); + } + return result; +#else + return ""; +#endif +} + +std::string PlatformUtils::SaveFile(const char *filter) +{ +#ifdef __APPLE__ + std::string result = ""; + char buffer[128]; + FILE *pipe = popen("osascript -e 'POSIX path of (choose file name with prompt \"Save File\" default name " + "\"untitled\")' 2>/dev/null", + "r"); + if (pipe) + { + while (fgets(buffer, sizeof(buffer), pipe) != nullptr) + { + result += buffer; + } + pclose(pipe); + } + + if (!result.empty() && result.back() == '\n') + { + result.pop_back(); + } + return result; +#else + return ""; +#endif +} bool PlatformUtils::RegisterFileAssociation(const std::string &extension, const std::string &appName, const std::string &appPath, const std::string &description) { +#ifdef __APPLE__ + size_t lastSlash = appPath.find_last_of("/\\"); + std::string appDir = (lastSlash != std::string::npos) ? appPath.substr(0, lastSlash) : "."; + std::string bundlePath = appDir + "/TimeEditor.app"; + + std::string command = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/" + "Support/lsregister -f \"" + + bundlePath + "\" 2>/dev/null"; + int ret = system(command.c_str()); + return (ret == 0); +#else return false; +#endif } bool PlatformUtils::IsFileAssociationRegistered(const std::string &extension, const std::string &appPath) { +#ifdef __APPLE__ + size_t lastSlash = appPath.find_last_of("/\\"); + std::string appDir = (lastSlash != std::string::npos) ? appPath.substr(0, lastSlash) : "."; + std::string bundlePath = appDir + "/TimeEditor.app"; + return (access(bundlePath.c_str(), F_OK) == 0); +#else return false; +#endif } std::string PlatformUtils::GetExecutablePath() @@ -36,7 +168,33 @@ std::string PlatformUtils::GetExecutablePath() char path[1024]; uint32_t size = sizeof(path); if (_NSGetExecutablePath(path, &size) == 0) - return std::string(path); + { + std::string execPath(path); + char cwd[1024]; + if (getcwd(cwd, sizeof(cwd)) != nullptr && (std::string(cwd) == "/" || std::string(cwd).empty())) + { + size_t lastSlash = execPath.find_last_of("/\\"); + if (lastSlash != std::string::npos) + { + std::string exeDir = execPath.substr(0, lastSlash); + size_t bundlePos = exeDir.find(".app/Contents/MacOS"); + if (bundlePos != std::string::npos) + { + std::string appDir = exeDir.substr(0, bundlePos); + size_t appParentPos = appDir.find_last_of("/\\"); + if (appParentPos != std::string::npos) + { + chdir(appDir.substr(0, appParentPos).c_str()); + } + } + else + { + chdir(exeDir.c_str()); + } + } + } + return execPath; + } return ""; #else return ""; diff --git a/Engine/src/Window/WindowsWindow.cpp b/Engine/src/Window/WindowsWindow.cpp index 034bef2..3dbdfa5 100644 --- a/Engine/src/Window/WindowsWindow.cpp +++ b/Engine/src/Window/WindowsWindow.cpp @@ -69,6 +69,12 @@ void WindowsWindow::Init(const WindowProps &props) switch (TE::RendererContext::GetAPI()) { case TE::GraphicsAPI::OpenGL: +#ifdef __APPLE__ + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); +#endif break; case TE::GraphicsAPI::Vulkan: case TE::GraphicsAPI::DirectX11: diff --git a/Premake5.lua b/Premake5.lua index e1bd44c..3172fe9 100644 --- a/Premake5.lua +++ b/Premake5.lua @@ -40,6 +40,12 @@ group "Vendor" targetdir ("Bin/" .. outputdir .. "/%{prj.name}") objdir ("Bin-Intermediate/" .. outputdir .. "/%{prj.name}") + filter "system:macosx" + xcodebuildsettings { + ["LD_DYLIB_INSTALL_NAME"] = "@rpath/libVelox.dylib" + } + filter {} + files { "Vendor/Velox/include/velox/**.h", "Vendor/Velox/src/core/**.cpp", @@ -52,7 +58,7 @@ group "Vendor" "Vendor/Velox/src/api/**.h" } - includedirs { + externalincludedirs { "Vendor/Velox/include", "Vendor/Velox/src", "Vendor/Velox/src/core", @@ -155,6 +161,13 @@ project "Engine" targetdir ("Bin/" .. outputdir .. "/%{prj.name}") objdir ("Bin-Intermediate/" .. outputdir .. "/%{prj.name}") + filter "system:macosx" + xcodebuildsettings { + ["LD_DYLIB_INSTALL_NAME"] = "@rpath/libEngine.dylib" + } + files { "Engine/src/**.mm" } + filter {} + files { -- Core Engine "Engine/src/**.h", @@ -196,6 +209,14 @@ project "Engine" } filter {} + -- Exclude Metal specific source files on non-macOS platforms + filter "system:not macosx" + removefiles { + "Engine/src/Renderer/Metal/**", + "Engine/Include/Renderer/Metal/**", + "Engine/src/**.mm" + } + filter {} -- Exclude non-Metal renderers on macOS (OpenGL, OpenGLES, Vulkan) since Metal is not yet present filter "system:macosx" removefiles { @@ -241,6 +262,21 @@ project "Engine" "%{IncludeDir.OpenGLES}" } + externalincludedirs { + "%{IncludeDir.ImGui}", + "%{IncludeDir.Engine}", + "%{IncludeDir.Engine_Include}", + "%{IncludeDir.Logger}", + "%{IncludeDir.GLFW}", + "%{IncludeDir.GLAD}", + "%{IncludeDir.GLM}", + "%{IncludeDir.stb_image}", + "%{IncludeDir.Velox}", + "%{IncludeDir.Vulkan}", + "%{IncludeDir.volk}", + "%{IncludeDir.OpenGLES}" + } + filter "action:vs*" libdirs { "Vendor/Customizable_Logger/build/lib/%{cfg.buildcfg}", @@ -269,7 +305,8 @@ project "Engine" "IOKit.framework", "CoreFoundation.framework", "CoreVideo.framework", - "QuartzCore.framework" + "QuartzCore.framework", + "Metal.framework" } filter {} @@ -325,7 +362,55 @@ project "Engine" filter "system:windows" icon "Resources/Branding/Icon.ico" --- ========== TimeEditor Project ========== +-- Auto-generate TimeEditor/Info.plist on macOS if missing +if os.target() == "macosx" and not os.isfile("TimeEditor/Info.plist") then + local plistFile = io.open("TimeEditor/Info.plist", "w") + if plistFile then + plistFile:write([[ + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + TimeEditor + CFBundleIdentifier + com.timeengine.timeeditor + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + TimeEditor + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + 10.14 + NSHighResolutionCapable + + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + teproj + + CFBundleTypeName + TimeEngine Project + CFBundleTypeRole + Editor + LSHandlerRank + Owner + + + + +]]) + plistFile:close() + end +end project "TimeEditor" location "TimeEditor" @@ -334,6 +419,21 @@ project "TimeEditor" cppdialect "C++17" staticruntime "off" + filter "system:macosx" + kind "WindowedApp" + files { "Info.plist" } + xcodebuildsettings { + ["INFOPLIST_FILE"] = "Info.plist", + ["LD_RUNPATH_SEARCH_PATHS"] = "@executable_path" + } + postbuildcommands { + 'mkdir -p "%{cfg.targetdir}/TimeEditor.app/Contents/MacOS/"', + 'cp -f "../Bin/' .. outputdir .. '/Engine/libEngine.dylib" "%{cfg.targetdir}/TimeEditor.app/Contents/MacOS/"', + 'cp -f "../Bin/' .. outputdir .. '/Velox/libVelox.dylib" "%{cfg.targetdir}/TimeEditor.app/Contents/MacOS/"', + 'codesign --force --deep --sign - "%{cfg.targetdir}/TimeEditor.app" 2>/dev/null || true' + } + filter {} + targetdir ("Bin/" .. outputdir .. "/%{prj.name}") objdir ("Bin-Intermediate/" .. outputdir .. "/%{prj.name}") @@ -366,6 +466,18 @@ project "TimeEditor" "%{IncludeDir.volk}" } + externalincludedirs { + "%{IncludeDir.ImGui}", + "%{IncludeDir.Engine}", + "%{IncludeDir.Engine_Include}", + "%{IncludeDir.Logger}", + "%{IncludeDir.GLM}", + "%{IncludeDir.GLFW}", + "%{IncludeDir.Velox}", + "%{IncludeDir.Vulkan}", + "%{IncludeDir.volk}" + } + filter "action:vs*" libdirs { "Vendor/Customizable_Logger/build/lib/%{cfg.buildcfg}", @@ -378,25 +490,32 @@ project "TimeEditor" } filter {} - links { - "Engine", - "Customizable_Logger", - "Velox", - "glfw3" - } + filter "system:windows or linux" + links { + "Engine", + "Customizable_Logger", + "Velox", + "glfw3" + } - filter "system:windows" - links { "opengl32" } - filter "system:linux" - links { "GL" } filter "system:macosx" links { + "Engine", + "Customizable_Logger", + "Velox", + "glfw3", "Cocoa.framework", "IOKit.framework", "CoreFoundation.framework", "CoreVideo.framework", - "QuartzCore.framework" + "QuartzCore.framework", + "Metal.framework" } + + filter "system:windows" + links { "opengl32" } + filter "system:linux" + links { "GL" } filter {} dependson { "Engine", "Logger", "Velox" } @@ -480,6 +599,18 @@ for _, pluginPath in ipairs(enginePlugins) do "Vendor/volk" } + externalincludedirs { + "Engine/src", + "Engine/Include", + "Vendor/IMGUI/ImGui", + "Vendor/Customizable_Logger/Include", + "Vendor/GLM", + "Vendor/GLFW/glfw/include", + "Vendor/Velox/include", + "Vendor/Vulkan/include", + "Vendor/volk" + } + filter "action:vs*" libdirs { "Vendor/Customizable_Logger/build/lib/%{cfg.buildcfg}", @@ -563,6 +694,18 @@ for _, pluginPath in ipairs(projectPlugins) do "Vendor/volk" } + externalincludedirs { + "Engine/src", + "Engine/Include", + "Vendor/IMGUI/ImGui", + "Vendor/Customizable_Logger/Include", + "Vendor/GLM", + "Vendor/GLFW/glfw/include", + "Vendor/Velox/include", + "Vendor/Vulkan/include", + "Vendor/volk" + } + filter "action:vs*" libdirs { "Vendor/Customizable_Logger/build/lib/%{cfg.buildcfg}", diff --git a/Scripts/Mac/BuildCommercialRelease.sh b/Scripts/Mac/BuildCommercialRelease.sh deleted file mode 100644 index 3dfb3e0..0000000 --- a/Scripts/Mac/BuildCommercialRelease.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/bash -# BuildCommercialRelease.sh (Mac) - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -ROOT_DIR="$( cd "$SCRIPT_DIR/../.." && pwd )" - -echo "[≡ Building TimeEngine Commercial Release (Dist)]" - -cd "$ROOT_DIR" - -if [ -d "TimeEngine.xcworkspace" ]; then - echo "[≡ Building project using xcodebuild (workspace)...]" - xcodebuild -workspace TimeEngine.xcworkspace -scheme TimeEditor -configuration Dist -elif [ -d "TimeEngine.xcodeproj" ]; then - echo "[≡ Building project using xcodebuild (project)...]" - xcodebuild -project TimeEngine.xcodeproj -scheme TimeEditor -configuration Dist -else - echo "[!] Xcode project or workspace not found. Please run GenerateProjectFiles.sh first." - read -p "Press Enter to exit..." - exit 1 -fi - -if [ $? -ne 0 ]; then - echo "[✖ Build Failed!]" - read -p "Press Enter to exit..." - exit 1 -fi - -echo "" -echo "[✅ Build Successful!]" - -DIST_DIR="$ROOT_DIR/Build_Release" -echo "[≡ Packaging to: $DIST_DIR]" - -rm -rf "$DIST_DIR" -mkdir -p "$DIST_DIR" - -# Find build output -BUILD_OUTPUT="$ROOT_DIR/Bin/Dist-macosx-x86_64/TimeEditor" -if [ ! -d "$BUILD_OUTPUT" ]; then - BUILD_OUTPUT=$(find "$ROOT_DIR/Bin" -type d -path "*/Dist-*/TimeEditor" | head -n 1) -fi - -if [ -z "$BUILD_OUTPUT" ] || [ ! -f "$BUILD_OUTPUT/TimeEditor" ]; then - echo "[!] Error: Build output not found at $BUILD_OUTPUT/TimeEditor" - read -p "Press Enter to exit..." - exit 1 -fi - -echo "[ - Copying Binaries...]" -cp -R "$BUILD_OUTPUT/"* "$DIST_DIR/" - -echo "[ - Copying Assets & Resources...]" -if [ -d "$ROOT_DIR/Resources" ]; then - cp -R "$ROOT_DIR/Resources" "$DIST_DIR/" -else - echo "[!] Warning: Resources folder not found at $ROOT_DIR/Resources" -fi - -# Create a Run script for convenience -echo "#!/bin/bash" > "$DIST_DIR/RunEngine.sh" -echo "./TimeEditor" >> "$DIST_DIR/RunEngine.sh" -chmod +x "$DIST_DIR/RunEngine.sh" - -echo "" -echo "[✅ Packaging Complete!]" -echo "[Executable located at: $DIST_DIR/TimeEditor]" -echo "[You can now package the '$DIST_DIR' folder and distribute it.]" -echo "" -read -p "Press Enter to continue..." diff --git a/Scripts/Mac/Makefiles/BuildCommercialRelease.sh b/Scripts/Mac/Makefiles/BuildCommercialRelease.sh new file mode 100755 index 0000000..c42d2f4 --- /dev/null +++ b/Scripts/Mac/Makefiles/BuildCommercialRelease.sh @@ -0,0 +1,93 @@ +#!/bin/bash +# BuildCommercialRelease.sh (Mac Makefiles) + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" + +echo "[=== Building TimeEngine Commercial Release (Dist via make) ===]" + +cd "$ROOT_DIR" + +if [ ! -f "Makefile" ]; then + echo "[ERROR] Makefile not found. Please run GenerateProjectFiles.sh first." + read -p "Press Enter to exit..." + exit 1 +fi + +echo "[INFO] Running make config=dist..." +make config=dist -j$(sysctl -n hw.ncpu) CC=clang CXX=clang++ +if [ $? -ne 0 ]; then + echo "[✖ Build Failed!]" + read -p "Press Enter to exit..." + exit 1 +fi + +echo "" +echo "[✅ Build Successful!]" + +DIST_DIR="$ROOT_DIR/Build_Release" +echo "[≡ Packaging to: $DIST_DIR]" + +rm -rf "$DIST_DIR" +mkdir -p "$DIST_DIR" + +# Find build output +BUILD_OUTPUT="$ROOT_DIR/Bin/Dist-macosx-x86_64/TimeEditor" +if [ ! -d "$BUILD_OUTPUT" ]; then + BUILD_OUTPUT=$(find "$ROOT_DIR/Bin" -type d -path "*/Dist-*/TimeEditor" | head -n 1) +fi + +if [ -z "$BUILD_OUTPUT" ] || { [ ! -d "$BUILD_OUTPUT/TimeEditor.app" ] && [ ! -f "$BUILD_OUTPUT/TimeEditor" ]; }; then + echo "[!] Error: Build output not found in $BUILD_OUTPUT" + read -p "Press Enter to exit..." + exit 1 +fi + +echo "[ - Packaging TimeEditor.app Bundle...]" +if [ -d "$BUILD_OUTPUT/TimeEditor.app" ]; then + cp -R "$BUILD_OUTPUT/TimeEditor.app" "$DIST_DIR/" +else + cp -R "$BUILD_OUTPUT/"* "$DIST_DIR/" +fi + +echo "[ - Copying Assets & Resources...]" +if [ -d "$ROOT_DIR/Resources" ]; then + cp -R "$ROOT_DIR/Resources" "$DIST_DIR/" + if [ -d "$DIST_DIR/TimeEditor.app" ]; then + cp -R "$ROOT_DIR/Resources" "$DIST_DIR/TimeEditor.app/Contents/" + fi +fi + +# Ensure dylibs are inside TimeEditor.app/Contents/MacOS/ and rpath is correct +ENGINE_DIR=$(find "$ROOT_DIR/Bin" -type d -path "*/Dist-*/Engine" | head -n 1) +VELOX_DIR=$(find "$ROOT_DIR/Bin" -type d -path "*/Dist-*/Velox" | head -n 1) + +if [ -d "$DIST_DIR/TimeEditor.app" ]; then + if [ -n "$ENGINE_DIR" ] && [ -f "$ENGINE_DIR/libEngine.dylib" ]; then + cp -f "$ENGINE_DIR/libEngine.dylib" "$DIST_DIR/TimeEditor.app/Contents/MacOS/" + fi + if [ -n "$VELOX_DIR" ] && [ -f "$VELOX_DIR/libVelox.dylib" ]; then + cp -f "$VELOX_DIR/libVelox.dylib" "$DIST_DIR/TimeEditor.app/Contents/MacOS/" + fi + + # Fix install_name and rpath for standalone bundle + install_name_tool -id "@rpath/libEngine.dylib" "$DIST_DIR/TimeEditor.app/Contents/MacOS/libEngine.dylib" 2>/dev/null || true + install_name_tool -id "@rpath/libVelox.dylib" "$DIST_DIR/TimeEditor.app/Contents/MacOS/libVelox.dylib" 2>/dev/null || true + install_name_tool -change "/usr/local/lib/libEngine.dylib" "@rpath/libEngine.dylib" "$DIST_DIR/TimeEditor.app/Contents/MacOS/TimeEditor" 2>/dev/null || true + install_name_tool -change "/usr/local/lib/libVelox.dylib" "@rpath/libVelox.dylib" "$DIST_DIR/TimeEditor.app/Contents/MacOS/TimeEditor" 2>/dev/null || true + install_name_tool -add_rpath "@executable_path/" "$DIST_DIR/TimeEditor.app/Contents/MacOS/TimeEditor" 2>/dev/null || true + codesign --force --deep --sign - "$DIST_DIR/TimeEditor.app" 2>/dev/null || true +fi + +# Create a Run script for convenience +echo "#!/bin/bash" > "$DIST_DIR/RunEngine.sh" +echo 'SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"' >> "$DIST_DIR/RunEngine.sh" +echo 'open "$SCRIPT_DIR/TimeEditor.app"' >> "$DIST_DIR/RunEngine.sh" +chmod +x "$DIST_DIR/RunEngine.sh" + +echo "" +echo "[✅ Packaging Complete!]" +echo "[Executable App Bundle located at: $DIST_DIR/TimeEditor.app]" +echo "[You can now package the '$DIST_DIR' folder and distribute it.]" +echo "" +read -p "Press Enter to continue..." diff --git a/Scripts/Mac/Makefiles/BuildDebug.sh b/Scripts/Mac/Makefiles/BuildDebug.sh new file mode 100755 index 0000000..ef31b46 --- /dev/null +++ b/Scripts/Mac/Makefiles/BuildDebug.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# BuildDebug.sh (Mac Makefiles) + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" + +echo "[=== Building TimeEngine (Mac Debug via make) ===]" +cd "$ROOT_DIR" + +if [ ! -f "Makefile" ]; then + echo "[ERROR] Makefile not found. Please run GenerateProjectFiles.sh first." + read -p "Press Enter to exit..." + exit 1 +fi + +echo "[INFO] Running make..." +make config=debug -j$(sysctl -n hw.ncpu) CC=clang CXX=clang++ +if [ $? -ne 0 ]; then + echo "[ERROR] Build failed!" + read -p "Press Enter to exit..." + exit 1 +fi + +echo "" +echo "[SUCCESS] Build completed successfully." +read -p "Press Enter to continue..." diff --git a/Scripts/Mac/Makefiles/CleanProjectFiles.sh b/Scripts/Mac/Makefiles/CleanProjectFiles.sh new file mode 100755 index 0000000..70cd3b6 --- /dev/null +++ b/Scripts/Mac/Makefiles/CleanProjectFiles.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# CleanProjectFiles.sh (Mac Makefiles) + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" + +echo "[🧹 Cleaning TimeEngine build artifacts, CMake, and project files...]" + +# Root build folders +rm -rf "$ROOT_DIR/Bin" +rm -rf "$ROOT_DIR/Bin-Intermediate" + +# Logger CMake cleanup +rm -rf "$ROOT_DIR/Vendor/Customizable_Logger/build" +rm -rf "$ROOT_DIR/Vendor/Customizable_Logger/bin" +rm -rf "$ROOT_DIR/Vendor/Customizable_Logger/lib" +rm -f "$ROOT_DIR/Vendor/Customizable_Logger/CMakeCache.txt" +rm -rf "$ROOT_DIR/Vendor/Customizable_Logger/CMakeFiles" +rm -f "$ROOT_DIR/Vendor/Customizable_Logger/Makefile" +rm -f "$ROOT_DIR/Vendor/Customizable_Logger/cmake_install.cmake" + +# GLFW CMake cleanup +rm -rf "$ROOT_DIR/Vendor/GLFW/build" +rm -rf "$ROOT_DIR/Vendor/bin" +rm -f "$ROOT_DIR/Vendor/GLFW/CMakeCache.txt" +rm -rf "$ROOT_DIR/Vendor/GLFW/CMakeFiles" +rm -f "$ROOT_DIR/Vendor/GLFW/Makefile" +rm -f "$ROOT_DIR/Vendor/GLFW/cmake_install.cmake" + +# VS/Premake/Xcode generated files +find "$ROOT_DIR" -type f \( -name "*.sln" -o -name "*.vcxproj" -o -name "*.vcxproj.filters" -o -name "*.vcxproj.user" -o -name "Makefile" -o -name "*.make" \) -delete +rm -rf "$ROOT_DIR/.vs" +rm -rf "$ROOT_DIR"/*.xcodeproj +rm -rf "$ROOT_DIR"/*.xcworkspace +rm -f "$ROOT_DIR/TimeEditor/Info.plist" + +echo "[✅ Cleanup complete. All build, CMake, and project artifacts removed.]" +read -p "Press Enter to continue..." diff --git a/Scripts/Mac/Makefiles/GenerateProjectFiles.sh b/Scripts/Mac/Makefiles/GenerateProjectFiles.sh new file mode 100755 index 0000000..de5fb0b --- /dev/null +++ b/Scripts/Mac/Makefiles/GenerateProjectFiles.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# GenerateProjectFiles.sh (Mac Makefiles) + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" + +echo "[≡ Generating TimeEngine Makefiles: Clean + CMake + Premake]" + +# Inline Clean +echo "[🧹 Cleaning previous build artifacts, CMake, and project files...]" +rm -rf "$ROOT_DIR/Bin" +rm -rf "$ROOT_DIR/Bin-Intermediate" +rm -rf "$ROOT_DIR/Vendor/Customizable_Logger/build" +rm -rf "$ROOT_DIR/Vendor/Customizable_Logger/bin" +rm -rf "$ROOT_DIR/Vendor/Customizable_Logger/lib" +rm -f "$ROOT_DIR/Vendor/Customizable_Logger/CMakeCache.txt" +rm -rf "$ROOT_DIR/Vendor/Customizable_Logger/CMakeFiles" +rm -f "$ROOT_DIR/Vendor/Customizable_Logger/Makefile" +rm -f "$ROOT_DIR/Vendor/Customizable_Logger/cmake_install.cmake" +rm -rf "$ROOT_DIR/Vendor/GLFW/build" +rm -rf "$ROOT_DIR/Vendor/bin" +rm -f "$ROOT_DIR/Vendor/GLFW/CMakeCache.txt" +rm -rf "$ROOT_DIR/Vendor/GLFW/CMakeFiles" +rm -f "$ROOT_DIR/Vendor/GLFW/Makefile" +rm -f "$ROOT_DIR/Vendor/GLFW/cmake_install.cmake" +find "$ROOT_DIR" -type f \( -name "*.sln" -o -name "*.vcxproj" -o -name "*.vcxproj.filters" -o -name "*.vcxproj.user" -o -name "Makefile" -o -name "*.make" \) -delete +rm -rf "$ROOT_DIR/.vs" +rm -rf "$ROOT_DIR"/*.xcodeproj +rm -rf "$ROOT_DIR"/*.xcworkspace + +echo "[✅ Cleanup complete.]" + +# Logger +echo "[≡ CMake configure/build: Logger]" +mkdir -p "$ROOT_DIR/Vendor/Customizable_Logger/build" +cd "$ROOT_DIR/Vendor/Customizable_Logger/build" +cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ +if [ $? -ne 0 ]; then + echo "[✖ Logger CMake configuration failed.]" + read -p "Press Enter to exit..." + exit 1 +fi +cmake --build . --config Debug +if [ $? -ne 0 ]; then + echo "[✖ Logger build failed.]" + read -p "Press Enter to exit..." + exit 1 +fi + +# GLFW +echo "[≡ CMake configure/build: GLFW]" +mkdir -p "$ROOT_DIR/Vendor/GLFW/build" +cd "$ROOT_DIR/Vendor/GLFW/build" +cmake ../glfw -DGLFW_BUILD_DOCS=OFF -DGLFW_BUILD_TESTS=OFF -DGLFW_BUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ +if [ $? -ne 0 ]; then + echo "[✖ GLFW CMake configuration failed.]" + read -p "Press Enter to exit..." + exit 1 +fi +cmake --build . --config Debug +if [ $? -ne 0 ]; then + echo "[✖ GLFW build failed.]" + read -p "Press Enter to exit..." + exit 1 +fi + +# Premake +echo "[≡ Generating GNU Makefiles with Premake...]" +cd "$ROOT_DIR" + +if command -v premake5 &> /dev/null; then + premake5 gmake2 --file=Premake5.lua +else + echo "[!] premake5 not found in PATH. Please install premake5 (e.g., via Homebrew: brew install premake) or ensure it is in your PATH." + read -p "Press Enter to exit..." + exit 1 +fi + +if [ $? -ne 0 ]; then + echo "[✖ Premake generation failed.]" + read -p "Press Enter to exit..." + exit 1 +fi + +echo "[✅ Setup complete. Run BuildDebug.sh to compile the project.]" +read -p "Press Enter to continue..." diff --git a/Scripts/Mac/Makefiles/RegisterFileExtension.sh b/Scripts/Mac/Makefiles/RegisterFileExtension.sh new file mode 100755 index 0000000..4e71fb6 --- /dev/null +++ b/Scripts/Mac/Makefiles/RegisterFileExtension.sh @@ -0,0 +1,113 @@ +#!/bin/bash +# RegisterFileExtension.sh (Mac Makefiles) + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" + +# Search for TimeEditor executable +ENGINE_EXE="" +for path in \ + "Bin/Debug-macosx-x86_64/TimeEditor/TimeEditor" \ + "Bin/Release-macosx-x86_64/TimeEditor/TimeEditor" \ + "Bin/Debug-macosx-arm64/TimeEditor/TimeEditor" \ + "Bin/Release-macosx-arm64/TimeEditor/TimeEditor"; do + if [ -f "$ROOT_DIR/$path" ]; then + ENGINE_EXE="$ROOT_DIR/$path" + break + fi +done + +if [ -z "$ENGINE_EXE" ]; then + ENGINE_EXE=$(find "$ROOT_DIR/Bin" -type f -name "TimeEditor" | grep -v "Intermediate" | grep -v "\.dSYM" | grep -v "\.app" | head -n 1) +fi + +if [ -n "$ENGINE_EXE" ] && [ -f "$ENGINE_EXE" ]; then + echo "[TimeEngine] Packaging TimeEditor into macOS Application Bundle (.app)..." + echo "[TimeEngine] Found executable: $ENGINE_EXE" + chmod +x "$ENGINE_EXE" + + APP_DIR="$(dirname "$ENGINE_EXE")" + BUNDLE_DIR="$APP_DIR/TimeEditor.app" + CONTENTS_DIR="$BUNDLE_DIR/Contents" + MACOS_DIR="$CONTENTS_DIR/MacOS" + RESOURCES_DIR="$CONTENTS_DIR/Resources" + + mkdir -p "$MACOS_DIR" + mkdir -p "$RESOURCES_DIR" + + # Create Info.plist inside bundle + cat << 'EOF' > "$CONTENTS_DIR/Info.plist" + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + TimeEditor + CFBundleIdentifier + com.timeengine.timeeditor + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + TimeEditor + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + 10.14 + NSHighResolutionCapable + + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + teproj + + CFBundleTypeName + TimeEngine Project + CFBundleTypeRole + Editor + LSHandlerRank + Owner + + + + +EOF + + # Copy binary into bundle + cp "$ENGINE_EXE" "$MACOS_DIR/TimeEditor" + chmod +x "$MACOS_DIR/TimeEditor" + + # Copy dynamic libraries if present + find "$APP_DIR/.." -name "*.dylib" -exec cp {} "$MACOS_DIR/" \; 2>/dev/null || true + + # Ensure @executable_path/ is in rpath so dyld finds dylibs inside bundle + install_name_tool -add_rpath "@executable_path/" "$MACOS_DIR/TimeEditor" 2>/dev/null || true + + # Ad-hoc code sign bundle so macOS Gatekeeper permits execution + codesign --force --deep --sign - "$BUNDLE_DIR" 2>/dev/null || true + + echo "[TimeEngine] TimeEditor.app bundle created and signed at: $BUNDLE_DIR" + + # Register with macOS Launch Services if lsregister tool exists + LSREGISTER="/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister" + if [ -f "$LSREGISTER" ]; then + echo "[TimeEngine] Registering TimeEditor.app with Launch Services..." + "$LSREGISTER" -f "$BUNDLE_DIR" + fi + + echo "[TimeEngine] Running internal registration..." + DYLD_LIBRARY_PATH="$APP_DIR/../Engine:$APP_DIR/../Velox:$DYLD_LIBRARY_PATH" "$ENGINE_EXE" --register || true + echo "[TimeEngine] Registration process completed successfully." +else + echo "[TimeEngine] ERROR: TimeEditor executable not found!" + echo "[TimeEngine] Please build the TimeEditor project first before running this script." + read -p "Press Enter to continue..." +fi + diff --git a/Scripts/Mac/RegisterFileExtension.sh b/Scripts/Mac/RegisterFileExtension.sh deleted file mode 100644 index 5c0f927..0000000 --- a/Scripts/Mac/RegisterFileExtension.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# RegisterFileExtension.sh (Mac) - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -ROOT_DIR="$( cd "$SCRIPT_DIR/../.." && pwd )" - -# Search for TimeEditor executable -ENGINE_EXE="" -for path in \ - "Bin/Debug-macosx-x86_64/TimeEditor/TimeEditor" \ - "Bin/Release-macosx-x86_64/TimeEditor/TimeEditor" \ - "Bin/Debug-macosx-arm64/TimeEditor/TimeEditor" \ - "Bin/Release-macosx-arm64/TimeEditor/TimeEditor"; do - if [ -f "$ROOT_DIR/$path" ]; then - ENGINE_EXE="$ROOT_DIR/$path" - break - fi -done - -if [ -z "$ENGINE_EXE" ]; then - ENGINE_EXE=$(find "$ROOT_DIR/Bin" -type f -name "TimeEditor" | grep -v "Intermediate" | head -n 1) -fi - -if [ -n "$ENGINE_EXE" ] && [ -f "$ENGINE_EXE" ]; then - echo "[TimeEngine] Registering .teproj file extension..." - echo "[TimeEngine] Found executable: $ENGINE_EXE" - chmod +x "$ENGINE_EXE" - "$ENGINE_EXE" --register - echo "[TimeEngine] Registration process completed." -else - echo "[TimeEngine] ERROR: TimeEditor executable not found!" - echo "[TimeEngine] Please build the TimeEditor project first before running this script." - read -p "Press Enter to continue..." -fi diff --git a/Scripts/Mac/Xcode/BuildCommercialRelease.sh b/Scripts/Mac/Xcode/BuildCommercialRelease.sh new file mode 100755 index 0000000..9ca8403 --- /dev/null +++ b/Scripts/Mac/Xcode/BuildCommercialRelease.sh @@ -0,0 +1,97 @@ +#!/bin/bash +# BuildCommercialRelease.sh (Mac Xcode) + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" + +echo "[=== Building TimeEngine Commercial Release (Dist) ===]" + +cd "$ROOT_DIR" + +if [ -d "TimeEngine.xcworkspace" ]; then + echo "[≡ Building project using xcodebuild (workspace)...]" + DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer" xcodebuild -workspace TimeEngine.xcworkspace -scheme TimeEditor -configuration Dist +elif [ -d "TimeEngine.xcodeproj" ]; then + echo "[≡ Building project using xcodebuild (project)...]" + DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer" xcodebuild -project TimeEngine.xcodeproj -scheme TimeEditor -configuration Dist +else + echo "[!] Xcode project or workspace not found. Please run GenerateProjectFiles.sh first." + read -p "Press Enter to exit..." + exit 1 +fi + +if [ $? -ne 0 ]; then + echo "[✖ Build Failed!]" + read -p "Press Enter to exit..." + exit 1 +fi + +echo "" +echo "[✅ Build Successful!]" + +DIST_DIR="$ROOT_DIR/Build_Release" +echo "[≡ Packaging to: $DIST_DIR]" + +rm -rf "$DIST_DIR" +mkdir -p "$DIST_DIR" + +# Find build output +BUILD_OUTPUT="$ROOT_DIR/Bin/Dist-macosx-x86_64/TimeEditor" +if [ ! -d "$BUILD_OUTPUT" ]; then + BUILD_OUTPUT=$(find "$ROOT_DIR/Bin" -type d -path "*/Dist-*/TimeEditor" | head -n 1) +fi + +if [ -z "$BUILD_OUTPUT" ] || { [ ! -d "$BUILD_OUTPUT/TimeEditor.app" ] && [ ! -f "$BUILD_OUTPUT/TimeEditor" ]; }; then + echo "[!] Error: Build output not found in $BUILD_OUTPUT" + read -p "Press Enter to exit..." + exit 1 +fi + +echo "[ - Packaging TimeEditor.app Bundle...]" +if [ -d "$BUILD_OUTPUT/TimeEditor.app" ]; then + cp -R "$BUILD_OUTPUT/TimeEditor.app" "$DIST_DIR/" +else + cp -R "$BUILD_OUTPUT/"* "$DIST_DIR/" +fi + +echo "[ - Copying Assets & Resources...]" +if [ -d "$ROOT_DIR/Resources" ]; then + cp -R "$ROOT_DIR/Resources" "$DIST_DIR/" + if [ -d "$DIST_DIR/TimeEditor.app" ]; then + cp -R "$ROOT_DIR/Resources" "$DIST_DIR/TimeEditor.app/Contents/" + fi +fi + +# Ensure dylibs are inside TimeEditor.app/Contents/MacOS/ and rpath is correct +ENGINE_DIR=$(find "$ROOT_DIR/Bin" -type d -path "*/Dist-*/Engine" | head -n 1) +VELOX_DIR=$(find "$ROOT_DIR/Bin" -type d -path "*/Dist-*/Velox" | head -n 1) + +if [ -d "$DIST_DIR/TimeEditor.app" ]; then + if [ -n "$ENGINE_DIR" ] && [ -f "$ENGINE_DIR/libEngine.dylib" ]; then + cp -f "$ENGINE_DIR/libEngine.dylib" "$DIST_DIR/TimeEditor.app/Contents/MacOS/" + fi + if [ -n "$VELOX_DIR" ] && [ -f "$VELOX_DIR/libVelox.dylib" ]; then + cp -f "$VELOX_DIR/libVelox.dylib" "$DIST_DIR/TimeEditor.app/Contents/MacOS/" + fi + + # Fix install_name and rpath for standalone bundle + install_name_tool -id "@rpath/libEngine.dylib" "$DIST_DIR/TimeEditor.app/Contents/MacOS/libEngine.dylib" 2>/dev/null || true + install_name_tool -id "@rpath/libVelox.dylib" "$DIST_DIR/TimeEditor.app/Contents/MacOS/libVelox.dylib" 2>/dev/null || true + install_name_tool -change "/usr/local/lib/libEngine.dylib" "@rpath/libEngine.dylib" "$DIST_DIR/TimeEditor.app/Contents/MacOS/TimeEditor" 2>/dev/null || true + install_name_tool -change "/usr/local/lib/libVelox.dylib" "@rpath/libVelox.dylib" "$DIST_DIR/TimeEditor.app/Contents/MacOS/TimeEditor" 2>/dev/null || true + install_name_tool -add_rpath "@executable_path/" "$DIST_DIR/TimeEditor.app/Contents/MacOS/TimeEditor" 2>/dev/null || true + codesign --force --deep --sign - "$DIST_DIR/TimeEditor.app" 2>/dev/null || true +fi + +# Create a Run script for convenience +echo "#!/bin/bash" > "$DIST_DIR/RunEngine.sh" +echo 'SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"' >> "$DIST_DIR/RunEngine.sh" +echo 'open "$SCRIPT_DIR/TimeEditor.app"' >> "$DIST_DIR/RunEngine.sh" +chmod +x "$DIST_DIR/RunEngine.sh" + +echo "" +echo "[✅ Packaging Complete!]" +echo "[Executable App Bundle located at: $DIST_DIR/TimeEditor.app]" +echo "[You can now package the '$DIST_DIR' folder and distribute it.]" +echo "" +read -p "Press Enter to continue..." diff --git a/Scripts/Mac/Xcode/BuildDebug.sh b/Scripts/Mac/Xcode/BuildDebug.sh new file mode 100755 index 0000000..fe8c6f0 --- /dev/null +++ b/Scripts/Mac/Xcode/BuildDebug.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# BuildDebug.sh (Mac Xcode) + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" + +echo "[=== Building TimeEngine (Mac Debug via xcodebuild) ===]" +cd "$ROOT_DIR" + +WORKSPACE_FILE=$(find . -maxdepth 1 -name "*.xcworkspace" | head -n 1) + +if [ -z "$WORKSPACE_FILE" ]; then + echo "[ERROR] Xcode Workspace not found. Please run GenerateProjectFiles.sh first." + read -p "Press Enter to exit..." + exit 1 +fi + +echo "[INFO] Running xcodebuild..." +DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer" xcodebuild -workspace "$WORKSPACE_FILE" -scheme TimeEditor -configuration Debug -parallelizeTargets +if [ $? -ne 0 ]; then + echo "[ERROR] Build failed!" + read -p "Press Enter to exit..." + exit 1 +fi + +echo "" +echo "[SUCCESS] Build completed successfully." +read -p "Press Enter to continue..." diff --git a/Scripts/Mac/CleanProjectFiles.sh b/Scripts/Mac/Xcode/CleanProjectFiles.sh old mode 100644 new mode 100755 similarity index 91% rename from Scripts/Mac/CleanProjectFiles.sh rename to Scripts/Mac/Xcode/CleanProjectFiles.sh index 8d6a0cb..7c27818 --- a/Scripts/Mac/CleanProjectFiles.sh +++ b/Scripts/Mac/Xcode/CleanProjectFiles.sh @@ -1,8 +1,8 @@ #!/bin/bash -# CleanProjectFiles.sh (Mac) +# CleanProjectFiles.sh (Mac Xcode) SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -ROOT_DIR="$( cd "$SCRIPT_DIR/../.." && pwd )" +ROOT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" echo "[🧹 Cleaning TimeEngine build artifacts, CMake, and project files...]" @@ -32,6 +32,7 @@ find "$ROOT_DIR" -type f \( -name "*.sln" -o -name "*.vcxproj" -o -name "*.vcxpr rm -rf "$ROOT_DIR/.vs" rm -rf "$ROOT_DIR"/*.xcodeproj rm -rf "$ROOT_DIR"/*.xcworkspace +rm -f "$ROOT_DIR/TimeEditor/Info.plist" echo "[✅ Cleanup complete. All build, CMake, and project artifacts removed.]" read -p "Press Enter to continue..." diff --git a/Scripts/Mac/GenerateProjectFiles.sh b/Scripts/Mac/Xcode/GenerateProjectFiles.sh old mode 100644 new mode 100755 similarity index 94% rename from Scripts/Mac/GenerateProjectFiles.sh rename to Scripts/Mac/Xcode/GenerateProjectFiles.sh index 5b57c61..95d4c48 --- a/Scripts/Mac/GenerateProjectFiles.sh +++ b/Scripts/Mac/Xcode/GenerateProjectFiles.sh @@ -1,10 +1,10 @@ #!/bin/bash -# GenerateProjectFiles.sh (Mac) +# GenerateProjectFiles.sh (Mac Xcode) SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -ROOT_DIR="$( cd "$SCRIPT_DIR/../.." && pwd )" +ROOT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" -echo "[≡ Generating TimeEngine Workspace: Clean + CMake + Premake]" +echo "[≡ Generating TimeEngine Xcode Workspace: Clean + CMake + Premake]" # Inline Clean echo "[🧹 Cleaning previous build artifacts, CMake, and project files...]" diff --git a/Scripts/Mac/Xcode/RegisterFileExtension.sh b/Scripts/Mac/Xcode/RegisterFileExtension.sh new file mode 100755 index 0000000..16f766b --- /dev/null +++ b/Scripts/Mac/Xcode/RegisterFileExtension.sh @@ -0,0 +1,113 @@ +#!/bin/bash +# RegisterFileExtension.sh (Mac Xcode) + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT_DIR="$( cd "$SCRIPT_DIR/../../.." && pwd )" + +# Search for TimeEditor executable +ENGINE_EXE="" +for path in \ + "Bin/Debug-macosx-x86_64/TimeEditor/TimeEditor" \ + "Bin/Release-macosx-x86_64/TimeEditor/TimeEditor" \ + "Bin/Debug-macosx-arm64/TimeEditor/TimeEditor" \ + "Bin/Release-macosx-arm64/TimeEditor/TimeEditor"; do + if [ -f "$ROOT_DIR/$path" ]; then + ENGINE_EXE="$ROOT_DIR/$path" + break + fi +done + +if [ -z "$ENGINE_EXE" ]; then + ENGINE_EXE=$(find "$ROOT_DIR/Bin" -type f -name "TimeEditor" | grep -v "Intermediate" | grep -v "\.dSYM" | grep -v "\.app" | head -n 1) +fi + +if [ -n "$ENGINE_EXE" ] && [ -f "$ENGINE_EXE" ]; then + echo "[TimeEngine] Packaging TimeEditor into macOS Application Bundle (.app)..." + echo "[TimeEngine] Found executable: $ENGINE_EXE" + chmod +x "$ENGINE_EXE" + + APP_DIR="$(dirname "$ENGINE_EXE")" + BUNDLE_DIR="$APP_DIR/TimeEditor.app" + CONTENTS_DIR="$BUNDLE_DIR/Contents" + MACOS_DIR="$CONTENTS_DIR/MacOS" + RESOURCES_DIR="$CONTENTS_DIR/Resources" + + mkdir -p "$MACOS_DIR" + mkdir -p "$RESOURCES_DIR" + + # Create Info.plist inside bundle + cat << 'EOF' > "$CONTENTS_DIR/Info.plist" + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + TimeEditor + CFBundleIdentifier + com.timeengine.timeeditor + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + TimeEditor + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + 10.14 + NSHighResolutionCapable + + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + teproj + + CFBundleTypeName + TimeEngine Project + CFBundleTypeRole + Editor + LSHandlerRank + Owner + + + + +EOF + + # Copy binary into bundle + cp "$ENGINE_EXE" "$MACOS_DIR/TimeEditor" + chmod +x "$MACOS_DIR/TimeEditor" + + # Copy dynamic libraries if present + find "$APP_DIR/.." -name "*.dylib" -exec cp {} "$MACOS_DIR/" \; 2>/dev/null || true + + # Ensure @executable_path/ is in rpath so dyld finds dylibs inside bundle + install_name_tool -add_rpath "@executable_path/" "$MACOS_DIR/TimeEditor" 2>/dev/null || true + + # Ad-hoc code sign bundle so macOS Gatekeeper permits execution + codesign --force --deep --sign - "$BUNDLE_DIR" 2>/dev/null || true + + echo "[TimeEngine] TimeEditor.app bundle created and signed at: $BUNDLE_DIR" + + # Register with macOS Launch Services if lsregister tool exists + LSREGISTER="/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister" + if [ -f "$LSREGISTER" ]; then + echo "[TimeEngine] Registering TimeEditor.app with Launch Services..." + "$LSREGISTER" -f "$BUNDLE_DIR" + fi + + echo "[TimeEngine] Running internal registration..." + DYLD_LIBRARY_PATH="$APP_DIR/../Engine:$APP_DIR/../Velox:$DYLD_LIBRARY_PATH" "$ENGINE_EXE" --register || true + echo "[TimeEngine] Registration process completed successfully." +else + echo "[TimeEngine] ERROR: TimeEditor executable not found!" + echo "[TimeEngine] Please build the TimeEditor project first before running this script." + read -p "Press Enter to continue..." +fi + diff --git a/llms.md b/llms.md index 3654abe..f930d61 100644 --- a/llms.md +++ b/llms.md @@ -7,13 +7,13 @@ ## Core Architecture - **Language**: C++20 -- **Graphics API**: OpenGL 4.5+, Vulkan, DirectX 11, OpenGL ES +- **Graphics API**: OpenGL 4.5+, Vulkan, DirectX 11, OpenGL ES, Metal - **Architecture**: Entity-Component System (ECS) - **Build System**: Premake5 / MSBuild - **UI**: TimeGUI (Strict ImGui Abstraction Wrapper) ## Key APIs & Systems -- **Renderer**: `Renderer2D` and `Renderer3D` (optimized batching for quads/sprites). Supports Vulkan, OpenGL ES, DirectX 11, and OpenGL core profile. +- **Renderer**: `Renderer2D` and `Renderer3D` (optimized batching for quads/sprites). Supports Vulkan, OpenGL ES, DirectX 11, Metal, and OpenGL core profile. - **Physics**: `PhysicsWorld` (Velox Physics Engine) — rigid body simulation and collision resolution via XPBD solver. - **Inbuilt 2D Sprite Editor & IDE**: Data-driven procedural scripting with recursive expression evaluation. - **Scene System**: `Scene` class manages entities and components via ECS. diff --git a/llms.txt b/llms.txt index 3654abe..f930d61 100644 --- a/llms.txt +++ b/llms.txt @@ -7,13 +7,13 @@ ## Core Architecture - **Language**: C++20 -- **Graphics API**: OpenGL 4.5+, Vulkan, DirectX 11, OpenGL ES +- **Graphics API**: OpenGL 4.5+, Vulkan, DirectX 11, OpenGL ES, Metal - **Architecture**: Entity-Component System (ECS) - **Build System**: Premake5 / MSBuild - **UI**: TimeGUI (Strict ImGui Abstraction Wrapper) ## Key APIs & Systems -- **Renderer**: `Renderer2D` and `Renderer3D` (optimized batching for quads/sprites). Supports Vulkan, OpenGL ES, DirectX 11, and OpenGL core profile. +- **Renderer**: `Renderer2D` and `Renderer3D` (optimized batching for quads/sprites). Supports Vulkan, OpenGL ES, DirectX 11, Metal, and OpenGL core profile. - **Physics**: `PhysicsWorld` (Velox Physics Engine) — rigid body simulation and collision resolution via XPBD solver. - **Inbuilt 2D Sprite Editor & IDE**: Data-driven procedural scripting with recursive expression evaluation. - **Scene System**: `Scene` class manages entities and components via ECS.