diff --git a/.gitignore b/.gitignore
index 2985a8c5..b478653e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,4 +11,4 @@ CTestTestfile.cmake
_deps
.vs
out
-docs
\ No newline at end of file
+.codex_temp
\ No newline at end of file
diff --git a/Editor.md b/Editor.md
index 13465e6e..e31ae898 100644
--- a/Editor.md
+++ b/Editor.md
@@ -1,9 +1,36 @@
-# 개요
-효율적인 게임 제작을 위해 필요한 요소인 에디터를 구현했습니다.
-에디터는 Unity 엔진의 UI와 비슷하며 조작법은 Blender와 유사 합니다.
-더욱 많은 기능을 개발중입니다.
+# 에디터 구조
+ShellEngine의 에디터는 엔진이 에디터 모드로 부팅된 상태입니다.
+이런 구조로 인해 월드, 카메라, 렌더러, 입력, GC, 스레드 모델을 대부분 공유하면서도 에디터 전용 타입만 별도로 끼워 넣을 수 있습니다.
+
+- ```Project```
+ - 새 프로젝트 생성, 프로젝트 열기, 월드 저장/불러오기, 빌드, 모듈 리로드
+- ```EditorWorld```
+ - 일반 game::World를 상속한 편집용 월드
+ - 선택 상태, 에디터 카메라, 피킹 카메라, 뷰포트 렌더 텍스처, 그리드/축, EditorUI 컴포넌트를 붙인 에디터용 게임오브젝트를 관리
+ - 에디터 전용 편집 상태를 분리해서 기록하고, 정지 시에는 world point를 되돌려 플레이 이전 상태를 복원
+- ```EditorRenderer```
+ - 일반 렌더러를 그대로 쓰지 않고 에디터 전용 패스를 조합한 ```ScriptableRenderer```
+ - 피킹, 아웃라인 pre/post pass, 일반 Opaque/Transparent, UI pass, ImGui pass를 카메라별로 필터링
+- ```EditorUI```
+ - Viewport, Hierarchy, Inspector, Project 패널과 각종 툴 UI를 생성하고, 단축키와 메뉴를 라우팅
+- ```AssetDatabase```
+ - 파일 경로와 UUID 매핑 유지
+ - 원본 파일, .meta, 캐시 .asset 사이 동기화
+ - 에셋 타입별 로더 선택 및 의존 순서 조절
# 엔진 생명주기
+에디터 실행은 src/main.cpp에서 ```EngineInit::Start()```를 호출하는 순간 시작됩니다. 그 후 에디터와 게임 모드의 실행 분기점은 ```#if SH_EDITOR``` 블록에서 나눠집니다.
+
+그 다음으로 이루어지는 과정은 다음과 같습니다.
+
+1. ```AssetResolverRegistry```에 에디터용 resolver를 등록
+ - resolver: 메모리에 안 올라온 에셋을 불러올 때 어떻게 처리 할 것인지 정의
+3. ```Project``` 인스턴스를 생성
+4. ```EditorWorld``` 팩토리를 등록
+5. 기본 ```EditorWorld```를 하나 만들고 ```GameManager```에 메인 월드로 로드
+6. ```EditorResource```와 에디터 렌더링 리소스를 초기화
+7. ```GameManager```에서 아래 이미지의 루프를 실행
+
OnEnable과 OnDisable은 객체가 활성화/비활성화 되는 즉시 실행 됩니다.
diff --git a/Multithreading.md b/Multithreading.md
index 71b1207f..03e329de 100644
--- a/Multithreading.md
+++ b/Multithreading.md
@@ -58,7 +58,7 @@ SH_RENDER_API void Renderer::Sync()
std::swap(drawcall[core::ThreadType::Game], drawcall[core::ThreadType::Render]);
}
```
-> swap방식은 메모리를 추가로 먹지만 매우 빠르게 작동하니 성능에 중요한 곳에서 사용하면 좋습니다.
+> swap방식은 메모리를 추가로 먹지만 매우 빠르게 작동하니 성능에 중요한 곳에서 사용하면 됩니다.
# 스레드 풀
core모듈의 ThreadPool클래스를 이용하여 작업을 할당하고 future로 값을 받아올 수 있습니다.
@@ -89,3 +89,18 @@ for (auto& renderPipeline : renderPipelines)
for (auto& futureCommand : futureCommands)
recordedCommands.push_back(futureCommand.get());
```
+# MPSC큐
+MSPC큐는 Multi Producer Single Consumer큐의 약자로, 다수의 스레드에서 쓰기 작업을 수행하고, 하나의 스레드에서 읽기 작업을 하는데 특화된 락-프리 큐입니다.
+
+```cpp
+core::LockFreeMPSCQueue requestID;
+// 다수의 스레드에서 실행
+requestID.Push(threadID);
+// 하나의 스레드에서 읽어오기
+requestID.Drain(
+ [this](int threadID)
+ {
+ SH_INFO_FORMAT("Request: {}", threadID);
+ }
+)
+```
diff --git a/README.md b/README.md
index bbf93308..b7409b6f 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,9 @@
# ShellEngine
+
+
+
[](https://youtu.be/SEiktv0WtOM)
[간단한 에디터 데모 영상(Youtube)](https://youtu.be/SEiktv0WtOM)
@@ -34,14 +37,14 @@ C++에서 메모리 관리를 단순화하기 위해 마크 앤 스윕 방식의
- 멀티스레딩 구조의 Sync 타이밍에 동작하여 렌더 중 해제로 인한 문제를 원천 차단
- ```GCObject```를 상속하여 사용자 정의 구조체 내부의 ```SObject```도 추적 가능
---
-### 에디터 [상세 문서](Editor.md)
+### 에디터 [상세 문서](Editor.md) | [구현 과정 (velog)](https://velog.io/@shell4026/%EC%97%90%EB%94%94%ED%84%B0-%EA%B0%9C%EB%B0%9C-%EA%B3%BC%EC%A0%95)
- 런타임 리플렉션을 활용한 Hierarchy(계층 구조) 및 Inspector(속성 편집) 자동 생성
- 사용자 정의 Inspector 지원 | [상세 문서](https://github.com/Shell4026/ShellEngine/blob/main/CustomInspector.md)
- 유저 코드 핫-리로드: 엔진 재시작 없이 런타임 중 컴포넌트 코드 추가, 수정 가능 | [구현 과정 (velog)](https://velog.io/@shell4026/ShellEngine-%ED%95%AB-%EB%A6%AC%EB%A1%9C%EB%93%9C-%EA%B5%AC%ED%98%84-%EA%B3%BC%EC%A0%95)
- Unity 스타일의 편집 환경 / Blender 스타일의 오브젝트 조작 방식
- 빌드 시스템
---
-### 커스텀 셰이더 언어 | [상세 문서](Shader.md)
+### 커스텀 셰이더 언어 | [상세 문서](Shader.md) | [구현 과정 (velog)](https://velog.io/@shell4026/%EC%85%B0%EC%9D%B4%EB%8D%94-%ED%8C%8C%EC%84%9C-%EA%B0%9C%EB%B0%9C-%EA%B3%BC%EC%A0%95)
Unity ShaderLab과 유사한 문법의 셰이더 언어와 하향식 파서(Top-Down Parser) 를 직접 구현했습니다.
파이프라인 설정(블렌딩, 컬링 등)을 셰이더 코드 안에서 선언적으로 작성할 수 있습니다.
@@ -117,6 +120,6 @@ cmake --build out/build/linux-release
```
# TODO
-- [ ]스켈레탈 메쉬
-- [ ]애니메이션, 애니메이터
-- [ ]Undo/Redo
+- [x] 스켈레탈 메쉬
+- [ ] 애니메이션, 애니메이터
+- [x] Undo/Redo
diff --git a/RenderArchitecture.md b/RenderArchitecture.md
index 901f5ee1..9aa66dbf 100644
--- a/RenderArchitecture.md
+++ b/RenderArchitecture.md
@@ -1,161 +1,251 @@
-# ShellEngine 패스 기반 렌더링 구조
+# ShellEngine 패스 기반 렌더링 구조
## 개요
+ShellEngine의 렌더링은 `RenderData`가 정의한 뷰/타겟 단위 작업을 `ScriptableRenderer`가 패스 목록으로 풀고, 각 `ScriptableRenderPass`가 자신의 리소스 사용 의도를 선언한 뒤, 백엔드 `CommandBuffer`가 실제 Vulkan 커맨드로 기록하는 구조입니다.
+
| 계층 | 책임 |
|---|---|
-| `ScriptableRenderPass` | 렌더링 의도 선언 (타겟, 샘플링 텍스처, 드로우 목록) |
-| `ScriptableRenderer` | 패스 간 리소스 상태 추적 및 전이 정보 계산 |
-| Vulkan 구현 계층 | 전이 정보를 실제 Vulkan API 호출로 번역 |
-
-ShellEngine의 Vulkan 렌더링 시스템은 드로우 호출을 단일 흐름으로 나열하는 방식 대신, **패스 단위로 렌더링 작업을 분리**하고 각 패스가 사용하는 렌더 텍스처와 일반 텍스쳐의 읽기/쓰기 관계를 추적하여, 리소스 전이와 실행 흐름을 자동으로 조립하는 구조로 설계되어 있습니다.
+| `Renderer` | 렌더 스레드 진입점, drawable/render data 큐 반영, 커맨드 제출 조율 |
+| `RenderDataManager` | 카메라/뷰 데이터를 GPU 버퍼에 업로드하고 `RenderData`를 priority 순으로 정렬 |
+| `ScriptableRenderer` | 활성 패스 선택, render queue 정렬, 리소스 전이 계산, 패스별 커맨드 버퍼 기록 |
+| `ScriptableRenderPass` | 패스 이름, render queue, 렌더 타겟/샘플링 텍스처 사용 의도, 드로우 기록 |
+| `CommandBuffer` | 배리어, 렌더 타겟 설정, draw/dispatch/blit 같은 백엔드 독립 커맨드 인터페이스 |
+| Vulkan 구현 계층 | `CommandBuffer` 호출을 dynamic rendering, pipeline bind, descriptor bind, barrier 호출로 변환 |
-해당 구조로 인해 프로젝트 목적에 맞게 코드를 크게 수정하지 않고 패스를 재배치 할 수 있으며 멀티스레딩과도 자연스럽게 연결됩니다.
+핵심은 패스가 “무엇을 렌더링하고 어떤 이미지를 읽고 쓰는지”만 표현하고, 이미지 레이아웃 전이와 Vulkan 동기화 코드는 `ScriptableRenderer`와 `VulkanCommandBuffer`가 처리한다는 점입니다.
-## 멀티스레딩
-`ScriptableRenderer`에서 렌더큐에 맞는 패스 순서를 조정하고 리소스 의존 상태를 계산 후, 스레드 풀에 넣어 각 패스의 렌더링을 병렬적으로 수행합니다.
+## 프레임 흐름
-1. **직렬 단계** - 중앙에서 리소스 상태 정리 및 의존 상태 계산
-2. **병렬 단계** - 각 패스의 커맨드 버퍼 기록을 스레드 풀에 task로 분배하여 병렬 실행
+
-리소스 전이 판단이 중앙에서 선행되므로, 이후 커맨드 기록 단계는 패스 간 의존성 없이 병렬화할 수 있습니다.
-
-## 패스 추가 방법
+1. 게임/에디터 쪽에서 `Renderer::PushDrawAble()`과 `Renderer::PushRenderData()`로 렌더링할 객체와 뷰 정보를 큐에 넣습니다.
+2. 렌더 스레드의 `Renderer::Render()`가 큐를 비우고 `RenderDataManager::UploadToGPU()`를 호출합니다.
+3. `RenderDataManager`는 각 `RenderViewer`의 view/proj/camera position을 GPU 버퍼에 업로드하고 `RenderData`를 `priority` 기준으로 정렬합니다.
+4. `VulkanRenderer::Render()`가 스왑체인 이미지를 획득한 뒤 각 `RenderData`에 frame index와 drawable 목록을 연결합니다.
+5. `ScriptableRenderer::Setup()`이 해당 `RenderData`에서 사용할 패스를 활성화합니다.
+6. `ScriptableRenderer::Execute()`가 활성 패스를 `RenderQueue` 순으로 정렬하고, 각 패스의 `Configure()`를 호출해 리소스 사용 정보를 확정합니다.
+7. 렌더러가 `BarrierInfo`를 계산한 뒤, 각 패스의 커맨드 버퍼 기록을 스레드 풀 task로 분배합니다.
+8. 모든 패스 커맨드 기록이 끝나면 `VulkanRenderer`가 기록된 커맨드들을 순서대로 제출하고 마지막 커맨드가 present semaphore를 signal합니다.
+9. 마지막에 `ExecuteTransfer()`가 readback/copy 작업을 위한 `CopyPass`를 기록하고, 스왑체인을 `Present` 상태로 전이합니다.
-새 패스를 추가할 때 필요한 작업은 다음과 같습니다.
+## RenderData와 RenderViewer
-1. `ScriptableRenderPass` 구현체 작성
-2. `renderQueue` 설정
-3. 필요 시 ```BuildDrawList``` 오버라이딩 하여 드로우 할 물체 필터링
-4. `Configure()`에서 드로우 목록 및 리소스 등록
-5. `Record()`에서 커맨드 기록
+`RenderData`는 한 번의 렌더링 작업이 사용할 뷰어, 렌더 타겟, 태그, 우선순위를 담습니다.
-기존 패스나 배리어 코드를 사용자가 명시적으로 수정할 필요가 없습니다. `CopyPass`처럼 드로우와 성격이 다른 패스도 동일한 방법으로 이루어집니다.
-
-예시 코드:
```cpp
-/// 단순히 우선 순위 정렬기능만 추가된 패스
-class TransparentPass : public ScriptableRenderPass
+class RenderData
{
public:
- SH_RENDER_API TransparentPass(std::string_view name = "Transparent", RenderQueue renderQueue = RenderQueue::Transparent);
+ void SetRenderTarget(const RenderTexture* renderTarget);
+ void SetRenderTargets(std::initializer_list renderTargets);
+ void ClearRenderTargets();
- SH_RENDER_API auto BuildDrawList(const RenderTarget& renderData) -> DrawList override
- {
- // renderData로 들어온 카메라 정보와 드로우 할 물체들의 정보를 가지고 DrawList 구조체를 만들어서 반환
- }
+ int priority = 0;
+ core::Name tag{ "Camera" };
+ std::vector renderViewers;
};
```
-## 상세
-### 설계 배경
+`RenderViewer`는 view/proj 행렬, 카메라 위치/방향, viewport/scissor, GPU camera buffer offset을 가집니다. `RenderDataManager`는 각 viewer마다 `RenderDataManager::BufferData`를 업로드하고, 그 offset을 `RenderViewer::offset`에 기록합니다. Vulkan draw 단계에서는 이 offset이 카메라 descriptor set의 dynamic offset으로 전달됩니다.
+
+최근 구조에서는 `RenderData`가 여러 렌더 타겟을 가질 수 있습니다.
-Vulkan 렌더링 시스템을 구현할 때 드로우 호출 자체보다 **리소스 상태 관리와 실행 순서**가 더 큰 문제가 됩니다.
+- `SetRenderTarget()`은 단일 타겟용 편의 함수입니다.
+- `SetRenderTargets()`는 MRT를 구성합니다.
+- 타겟이 `nullptr`이면 현재 스왑체인 이미지를 의미합니다.
+- depth 전용 `RenderTexture`는 color attachment가 아니라 depth attachment로 취급됩니다.
-구체적으로 아래 사항들을 매 패스마다 직접 추적해야 합니다.
+## 기본 패스 구성
-- 패스 간 실행 순서
-- 각 패스의 렌더 타겟 및 샘플링 텍스처
-- 이미지 레이아웃 및 접근 권한 변환
-- `vkCmdPipelineBarrier` 삽입 위치
+`GameRenderer::Init()`은 기본 패스를 생성하고, `GameRenderer::Setup()`은 `RenderData::tag`에 따라 필요한 패스만 enqueue합니다.
+
+| 태그 | 실행 패스 |
+|---|---|
+| `"Depth"` | `DepthPass` |
+| `"SSAO"` | `SSAOPass` |
+| `"Combine"` | `CombinePass` |
+| `"ImGUI"` | `GUIPass` |
+| 그 외 카메라 | `Opaque`, `Transparent`, `UI` |
-렌더링 코드를 단일 흐름으로 구성할 경우, 패스가 늘어날수록 다음과 같은 문제가 발생합니다.
+현재 기본 패스의 역할은 다음과 같습니다.
-- 패스 추가 시 앞뒤 리소스 상태 전이를 전체적으로 재검토해야 함
-- 배리어 누락 또는 잘못된 레이아웃 전이로 인한 런타임 오류 발생
-- 렌더링 의도 코드와 Vulkan 동기화 코드가 강하게 결합됨
+| 패스 | Queue | 역할 |
+|---|---|---|
+| `DepthPass` | `BeforeRendering` | depth/shadow 계열 렌더링. depth 전용 타겟도 지원 |
+| `Opaque` | `Opaque` | 기본 불투명 렌더링 |
+| `SSAOPass` | `AfterOpaque` | 풀스크린 plane으로 depth 등을 샘플링해 AO 타겟에 기록 |
+| `CombinePass` | `AfterRendering` | 풀스크린 plane으로 AO 등 후처리 결과를 최종 타겟에 합성 |
+| `Transparent` | `Transparent` | 카메라 방향 기준 back-to-front 정렬 후 개별 draw |
+| `UI` | `Transparent` | UI shader pass용 transparent pass |
+| `GUIPass` | `UI` | ImGui draw data 기록 |
+| `CopyPass` | `AfterRendering` | readback/copy transfer 전용 내부 패스 |
-ShellEngine은 이 문제를 해결하기 위해서, 그리고 유연한 확장을 위해 패스의 렌더링 의도와 API 레벨의 전이 처리를 분리하는 구조를 채택했습니다.
+`SSAOPass`와 `CombinePass`는 패스 내부에서 fullscreen plane `Mesh`와 `Drawable`을 보유하고, 외부에서 `SetMaterial()`로 지정한 material을 사용합니다. 두 패스 모두 material이 참조하는 `RenderTexture`를 `Configure()`에서 샘플링 리소스로 등록합니다.
----
+## ScriptableRenderPass
-### Configure 단계
+패스는 `passName`과 `renderQueue`를 갖고, 크게 두 단계로 동작합니다.
-`ScriptableRenderPass::Configure()`는 렌더러가 전이를 계산하는 데 필요한 데이터를 구성하는 단계입니다.
+### Configure
-1. `BuildDrawList()` - 이번 패스의 드로우 목록 생성
-2. `CollectRenderImages()` - 렌더 텍스처 수집 및 용도 분류
- - 현재 렌더 타겟 -> `ColorAttachment`
- - DrawList로 전달 된 메테리얼이 참조하는 `RenderTexture` -> `SampledRead`
-3. 동일 패스 내에서 같은 텍스처를 출력과 입력으로 동시에 사용하는 경우 검증
+`Configure(const RenderData&)`는 커맨드 기록 전에 리소스 사용 의도를 확정합니다.
-이 단계를 통해 패스의 리소스 사용 관계가 확정됩니다.
+- 기본 구현은 현재 `passName`을 가진 shader pass가 있는 drawable만 `RenderBatch`로 묶습니다.
+- `SetRenderTargetImageUsages()`가 `RenderData::GetRenderTargets()`를 순회해 color/depth attachment 사용을 등록합니다.
+- `SetImageUsages()`가 drawable/material의 `MaterialData::CachedRT`를 확인해 샘플링하는 `RenderTexture`를 등록합니다.
+- depth texture를 샘플링하는 경우 `DepthStencilSampledRead`, 일반 texture는 `SampledRead`로 등록합니다.
-> [!Note]
-> ```BuildDrawList```에는 RenderTarget구조체가 전달 됩니다.
-> ```cpp
-> struct RenderTarget
-> {
-> uint32_t frameIndex;
-> const Camera* camera;
-> const RenderTexture* target;
-> const std::vector* drawables;
-> };
-> ```
----
+예전 문서의 `BuildDrawList()` 단계는 현재 `CreateRenderBatch()`와 `Configure()` 흐름으로 대체되었습니다.
-### ScriptableRenderer
+### Record
-`ScriptableRenderer::Execute()`는 패스 간 리소스 연결 관계를 계산하고 커맨드 버퍼 기록을 조율합니다.
+`Record(CommandBuffer&, const IRenderContext&, const RenderData&)`는 실제 커맨드를 기록합니다.
-1. 활성 패스 수집
-2. renderQueue 기준 정렬
-3. 각 패스 Configure() 호출 -> 리소스 사용 정보 확정
-4. 각 패스 BuildBarrierInfo() 호출 -> 전이 정보 계산
-5. 커맨드 버퍼 기록
+- 기본 구현은 `cmd.SetRenderData(renderData, true, true, true, true)`로 렌더링을 시작합니다.
+- viewer마다 viewport/scissor를 설정합니다.
+- `RenderBatch` 단위로 `cmd.DrawMeshBatch()`를 호출합니다.
+- `TransparentPass`처럼 정렬이 필요한 패스는 batch 대신 개별 `DrawMesh()`를 호출할 수 있습니다.
+- 풀스크린 후처리 패스는 내부 drawable 하나를 직접 그립니다.
-패스는 직접 배리어를 생성하지 않으며, 리소스 사용 의도만 제공합니다. 실제 전이 계산은 렌더러가 전담합니다.
+## 리소스 전이
----
+패스는 직접 Vulkan barrier를 만들지 않습니다. 대신 `renderTextures`에 “이번 패스에서 이 이미지를 어떤 용도로 쓸 것인지”를 기록합니다.
-### BarrierInfo
+```cpp
+enum class ResourceUsage
+{
+ Undefined,
+ ColorAttachment,
+ SampledRead,
+ Present,
+ TransferSrc,
+ DepthStencilAttachment,
+ DepthStencilSampledRead
+};
+```
-`BarrierInfo`는 패스 간 리소스 의존성을 실행 가능한 형태로 담는 구조체입니다.
+`ScriptableRenderer::BuildBarrierInfo()`는 각 패스의 `renderTextures`를 보고 이전 사용 상태와 현재 사용 상태를 `BarrierInfo`로 만듭니다.
-| 필드 | 설명 |
+| 대상 | 이전 상태 출처 |
|---|---|
-| 대상 이미지 | 전이 대상 리소스 |
-| `lastUsage` | 이전 패스의 사용 상태 |
-| `curUsage` | 현재 패스의 사용 상태 |
+| `RenderTexture*` | `RenderTexture::GetUsage()` |
+| `nullptr` 스왑체인 | `ScriptableRenderer::swapChainStates[frameIndex]` |
-렌더러는 각 텍스처의 사용 이력을 추적하여 패스 사이의 상태 전이를 `BarrierInfo`로 구체화합니다.
+계산된 `BarrierInfo`는 해당 패스 커맨드 버퍼 시작 직후 `cmd.EmitBarrier()`로 기록됩니다. Vulkan에서는 `VulkanCommandBuffer::EmitBarrier()`가 `ResourceUsage`를 image layout, pipeline stage, access mask로 매핑하고 `VulkanImageBuffer::BarrierCommand()`를 호출합니다.
-**예시 - 3패스 렌더링 흐름:**
+예시 흐름:
+```text
+depth texture Undefined -> DepthStencilAttachment
+depth texture DepthStencilAttachment -> DepthStencilSampledRead
+ao texture Undefined -> ColorAttachment
+ao texture ColorAttachment -> SampledRead
+swapchain Undefined -> ColorAttachment
+readback src ColorAttachment -> TransferSrc
+swapchain ColorAttachment -> Present
```
-패스 1: 씬 렌더링 -> Undefined -> ColorAttachment
-패스 2: 후처리 샘플링 -> ColorAttachment -> SampledRead
-패스 3: 스왑체인 출력 -> SampledRead -> Present
-```
----
+## 멀티스레딩
+
+`ScriptableRenderer::Execute()`는 리소스 전이를 먼저 직렬로 계산한 뒤, 각 패스의 커맨드 버퍼 기록을 스레드 풀에 task로 분배합니다.
+
+직렬 단계:
+
+- 활성 패스 정렬
+- 모든 패스 `Configure()`
+- 모든 패스 `BarrierInfo` 계산
-### Vulkan 구현 계층
+병렬 단계:
-`VulkanRenderImpl::EmitBarrier()`는 렌더러가 계산한 `BarrierInfo`를 Vulkan API 호출로 번역합니다.
+- 패스별 `CommandBuffer` 할당
+- `Begin()`
+- `EmitBarrier()`
+- `Record()`
+- `End()`
-- `ImageUsage`를 image layout, pipeline stage, access mask로 매핑
-- `VulkanImageBuffer::BarrierCommand()`를 통해 `vkCmdPipelineBarrier` 기록
+커맨드 버퍼 기록은 병렬이지만, 제출은 `recordedCmds` 순서대로 수행됩니다. 따라서 패스 간 이미지 상태 전이는 정렬된 패스 순서를 기준으로 안정적으로 계산되고, 실제 제출 순서도 그 순서를 유지합니다.
-이 계층은 전이가 필요한지 판단을 내리지 않으며, 상위에서 계산된 전이 정보를 API 호출로 변환하는 역할만 수행합니다.
+## Vulkan 구현
-## 한계
+`VulkanCommandBuffer`는 현재 Vulkan dynamic rendering을 사용합니다.
-현재 구조는 아래 기능은 포함되어 있지 않습니다.
+- `SetRenderData()`가 `RenderData::GetRenderTargets()`로 `VkRenderingAttachmentInfoKHR` 배열을 구성합니다.
+- 여러 color attachment를 지원하며, 첫 번째 타겟 또는 depth 전용 타겟에서 depth attachment를 가져옵니다.
+- 스왑체인 렌더링에서는 `nullptr` 타겟을 swapchain image로 해석합니다.
+- MSAA 타겟이 있으면 color attachment에 MSAA image를 사용하고 resolve image를 원본 color image로 지정합니다.
+- `RenderTargetLayout`은 color format 목록, depth format, MSAA 여부를 담고 pipeline cache key로 사용됩니다.
-- 의존성 기반 패스 자동 재배치
-- 버퍼 리소스 상태 추적 (현재는 `ImageUsage` 위주로 구현됨)
+draw 단계에서는 shader pass 이름으로 material의 `ShaderPass` 목록을 찾고, topology/skinned 여부/render target layout에 맞는 pipeline을 가져옵니다. descriptor set은 usage 기준으로 다음처럼 바인딩됩니다.
-## 구조 요약
+| Set usage | 데이터 |
+|---|---|
+| `Camera` | `RenderDataManager` GPU buffer, dynamic offset 사용 |
+| `Object` | drawable별 material data |
+| `Material` | material별 uniform/texture data |
+
+Shader DSL에서 `MATRIX_VIEW`, `MATRIX_PROJ`, `CAMERA`를 사용하면 parser가 `CAMERA` uniform buffer를 자동으로 구성합니다. 현재 camera buffer는 `view`, `proj`, `pos`를 담고, `MATRIX_VIEW`와 `MATRIX_PROJ`는 각각 `CAMERA.view`, `CAMERA.proj`로 치환됩니다.
+
+## MaterialData와 텍스처 추적
+
+렌더 텍스처 샘플링 추적은 `MaterialData`의 `CachedRT`에 저장됩니다.
+
+```cpp
+struct CachedRT
+{
+ uint32_t set = 0;
+ uint32_t binding = 0;
+ core::SObjWeakPtr pass;
+ core::SObjWeakPtr rt;
+};
+```
-ScriptableRenderPass (N개)
-- renderTextures: 리소스 사용 의도 선언
+패스의 `Configure()`는 material/drawable이 가진 cached render texture 중 현재 `passName`과 lighting pass 이름이 같은 항목만 읽기 리소스로 등록합니다. 이 구조 덕분에 후처리 material이 depth, SSAO, color texture를 참조하더라도 패스 쪽에서 별도 barrier 코드를 직접 작성하지 않아도 됩니다.
-ScriptableRenderer
-- 패스 순서 정렬
-- BuildBarrierInfo(): 패스 간 상태 전이 계산
-- 커맨드 버퍼 병렬 기록
+## 패스 추가 방법
+
+새 패스를 추가할 때는 다음 순서로 작업합니다.
+
+1. `ScriptableRenderPass`를 상속한 클래스를 만든다.
+2. 생성자에서 `passName`과 `RenderQueue`를 정한다.
+3. 기본 batch 드로우면 기본 `Configure()`를 쓰고, 풀스크린/특수 패스면 `Configure()`에서 `SetRenderTargetImageUsages()`와 `SetImageUsages()`를 직접 호출한다.
+4. `Record()`에서 `CommandBuffer` API로 렌더 타겟 설정, viewport/scissor, draw/dispatch/blit을 기록한다.
+5. `ScriptableRenderer` 파생 클래스의 `Init()`에서 `AddRenderPass()`로 생성한다.
+6. `Setup()`에서 `RenderData::tag`나 상황에 맞춰 `EnqueRenderPass()` 한다.
-VulkanRenderImpl
-- EmitBarrier(): ImageUsage -> Vulkan barrier 변환
-- vkCmdPipelineBarrier 기록
+예시:
+
+```cpp
+class MyPostPass : public ScriptableRenderPass
+{
+public:
+ MyPostPass(const IRenderContext& ctx)
+ : ScriptableRenderPass(core::Name{ "MyPostPass" }, RenderQueue::AfterRendering),
+ ctx(ctx)
+ {
+ }
+
+protected:
+ void Configure(const RenderData& renderData) override
+ {
+ renderTextures.clear();
+ SetRenderTargetImageUsages(renderData);
+ SetImageUsages(*material);
+ }
+
+ void Record(CommandBuffer& cmd, const IRenderContext& ctx, const RenderData& renderData) override
+ {
+ cmd.SetRenderData(renderData, false, false, true, false);
+ for (std::size_t i = 0; i < renderData.renderViewers.size(); ++i)
+ {
+ SetViewportScissor(cmd, ctx, renderData.renderViewers[i]);
+ cmd.DrawMesh(*fullscreenDrawable, passName, i);
+ }
+ }
+
+private:
+ const IRenderContext& ctx;
+ Material* material = nullptr;
+ Drawable* fullscreenDrawable = nullptr;
+};
+```
diff --git a/Shader.md b/Shader.md
index 73a1055a..2f0e31a0 100644
--- a/Shader.md
+++ b/Shader.md
@@ -1,95 +1,459 @@
-# 개요
-코드를 통해 렌더링 파이프라인을 설정 할 필요 없이, 셰이더를 통해 처리 할 수 있습니다.
+# Shader 문서
-Unity엔진의 ShaderLab + GLSL의 문법을 합친 것이 특징 입니다.
+ShellEngine의 `.shader` 파일은 Unity ShaderLab처럼 렌더 상태와 패스를 선언하고, 각 `Stage` 안에는 GLSL 코드를 직접 작성하는 형식입니다. 엔진은 이 파일을 파싱해 Vulkan용 GLSL을 생성하고, `glslc`로 SPIR-V를 컴파일한 뒤 `Shader`, `ShaderPass`, 머티리얼 유니폼 레이아웃으로 연결합니다.
-# 상세 원리
-1. [ShaderLexer](https://github.com/Shell4026/ShellEngine/blob/main/src/Render/ShaderLexer.cpp)클래스로 코드를 토큰 단위로 나눕니다
-2. [ShaderParser](https://github.com/Shell4026/ShellEngine/blob/main/src/Render/ShaderParser.cpp)클래스로 토큰을 하나하나 읽어 문법을 검사하고 파싱 트리 구조를 완성해나갑니다.
-3. ShaderParse내 Generate함수로 파싱 트리를 기반으로 GLSL문법으로 변환한 후 셰이더를 컴파일 합니다.
+관련 구현은 주로 다음 파일에 있습니다.
-## 예시 코드
+- `src/Render/ShaderLexer.cpp`: 소스 문자열을 토큰으로 분해
+- `src/Render/ShaderParser.cpp`: 토큰을 AST로 파싱하고 GLSL 코드 생성
+- `src/Render/ShaderGenerator.cpp`: 패스별 `.vert`, `.frag` 임시 파일 생성
+- `src/Game/Asset/ShaderLoader.cpp`: `.shader` 로드, `glslc` 실행, SPIR-V 로드
+- `src/Render/ShaderPass.cpp`: 파싱 결과에서 어트리뷰트/유니폼 레이아웃 구성
+- `src/Render/VulkanImpl/VulkanShaderPass.cpp`: Vulkan 디스크립터 세트와 파이프라인 레이아웃 생성
+
+## 처리 흐름
+
+1. `ShaderLoader::Load(path)`가 `.shader` 파일을 텍스트로 읽습니다.
+2. `ShaderLexer::Lex()`가 키워드, 식별자, 숫자, 문자열, 연산자, 중괄호 등을 토큰으로 나눕니다.
+3. `ShaderParser::Parse()`가 토큰을 `ShaderAST::ShaderNode`로 변환합니다.
+4. 파서는 `Stage` 본문을 분석하며 `VERTEX`, `MATRIX_PROJ`, `LIGHT` 같은 엔진 예약 심볼을 발견하면 필요한 입력 어트리뷰트와 버퍼를 자동으로 추가합니다.
+5. `GenerateStageCode()`가 각 `Stage`의 최종 GLSL 코드를 만듭니다.
+6. `ShaderGenerator::GenerateShaderFile()`이 패스별 `.vert`, `.frag` 파일을 캐시 경로에 저장합니다.
+7. `ShaderLoader`가 `glslc`로 각 스테이지를 `.spv`로 컴파일합니다.
+8. `ShaderPassBuilder`가 SPIR-V를 `ShaderPass`로 만들고, Vulkan 구현에서는 `VkShaderModule`, descriptor set layout, pipeline layout을 구성합니다.
+9. 최종 `Shader` 객체는 프로퍼티 목록과 패스 목록을 보관하고, `Material`은 이를 기준으로 유니폼/샘플러 데이터를 업로드합니다.
+
+## 기본 구조
+
+```glsl
+#version 430 core
+
+Shader "Shader Name"
+{
+ Property
+ {
+ float ambient;
+ vec4 color;
+ sampler2D albedo;
+ }
+
+ Pass
+ {
+ LightingPass "Opaque"
+ Cull Back;
+ ZWrite On;
+ ZTest On;
+
+ Stage Vertex
+ {
+ layout(location = 0) out vec2 fragUV;
+
+ void main()
+ {
+ fragUV = UV;
+ gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0);
+ }
+ }
+
+ Stage Fragment
+ {
+ layout(location = 0) in vec2 fragUV;
+ layout(location = 0) out vec4 outColor;
+
+ uniform float ambient;
+ uniform vec4 color;
+ uniform sampler2D albedo;
+
+ void main()
+ {
+ outColor = texture(albedo, fragUV) * color;
+ outColor.rgb += ambient;
+ }
+ }
+ }
+}
+```
+
+최상단 `#version`은 현재 파서에서 `#version 430 core` 형태를 기대합니다. `Shader "..."`는 생성될 `Shader` 객체의 이름입니다. `Property`는 머티리얼에서 조작할 수 있는 값 목록이며 생략할 수 있습니다. `Pass`는 하나 이상 선언할 수 있고, 각 패스는 렌더 패스 이름과 렌더 상태, `Vertex`/`Fragment` 스테이지를 가집니다.
+
+## Property
+
+`Property`에 선언한 변수는 `Material::SetProperty()`로 값을 넣을 수 있는 머티리얼 프로퍼티가 됩니다. 스테이지 안에서 `uniform`으로 사용하려면 같은 타입과 이름이 `Property`에 먼저 선언되어 있어야 합니다.
+
+지원되는 주요 타입은 다음과 같습니다.
+
+| Shader 타입 | C++ 저장 타입 |
+| --- | --- |
+| `int` | `int` |
+| `float` | `float` |
+| `vec2` | `glm::vec2` |
+| `vec3` | `glm::vec3` |
+| `vec4` | `glm::vec4` |
+| `mat3` | `glm::mat3` |
+| `mat4` | `glm::mat4` |
+| `sampler2D` | `Texture` |
+
+배열도 선언할 수 있습니다.
+
+```glsl
+Property
+{
+ vec3 kernel[64];
+ sampler2D normalTex;
+}
+```
+
+### Local 프로퍼티
+
+`[Local]`을 붙인 프로퍼티는 머티리얼 공통 값이 아니라 오브젝트별 값으로 취급됩니다. 예를 들어 같은 머티리얼을 공유하되 오브젝트마다 텍스처를 다르게 넣고 싶을 때 사용합니다.
+
+```glsl
+Property
+{
+ vec4 color;
+ [Local] sampler2D tex;
+}
+```
+
+일반 프로퍼티는 `UniformStructLayout::Usage::Material` 세트로, `[Local]` 프로퍼티는 `UniformStructLayout::Usage::Object` 세트로 배치됩니다. 런타임에서는 `MaterialData`와 `MaterialPropertyBlock`이 이 구분을 기준으로 버퍼와 샘플러를 갱신합니다.
+
+## Pass
+
+`Pass`는 실제 draw에 사용되는 렌더링 단위입니다. 같은 `LightingPass` 이름을 가진 패스들은 `Shader::GetShaderPasses(name)`로 조회되어 해당 렌더 단계에서 순서대로 실행됩니다.
+
+```glsl
+Pass "Optional Pass Name"
+{
+ LightingPass "Opaque"
+ Cull Back;
+ ZWrite On;
+ ZTest On;
+ ColorMask RGBA;
+
+ Stage Vertex { ... }
+ Stage Fragment { ... }
+}
+```
+
+패스 이름은 생략할 수 있습니다. 생략하면 파서가 `Pass1`, `Pass2`처럼 자동 이름을 붙입니다.
+
+### 렌더 상태
+
+| 구문 | 값 | 기본값 | 설명 |
+| --- | --- | --- | --- |
+| `LightingPass "name"` | 문자열 | 빈 이름 | 렌더러가 패스를 찾을 때 쓰는 분류 이름 |
+| `Cull` | `Off`, `Front`, `Back` | `Back` | 컬링 모드 |
+| `ZWrite` | `On`, `Off` | `On` | 깊이 버퍼 쓰기 여부 |
+| `ZTest` | `On`, `Off` | `On` | 깊이 테스트 여부 |
+| `ColorMask` | `RGBA` 조합 또는 숫자 | `15` | 컬러 채널 쓰기 마스크 |
+
+`ColorMask`는 비트마스크로 저장됩니다. `R=1`, `G=2`, `B=4`, `A=8`이며 `ColorMask 0;`은 컬러 출력을 막을 때 사용합니다.
+
+### Stencil
+
+```glsl
+Stencil
+{
+ Ref 1;
+ ReadMask 255;
+ WriteMask 255;
+ Comp Always;
+ Pass Replace;
+ Fail Keep;
+ ZFail Keep;
+}
+```
+
+`Comp` 값은 `Never`, `Less`, `Equal`, `LessEqual`, `Greater`, `NotEqual`, `GreaterEqual`, `Always`를 사용할 수 있습니다. `Pass`, `Fail`, `ZFail` 값은 `Keep`, `Zero`, `Replace`, `IncrementClamp`, `DecrementClamp`, `Invert`, `IncrementWrap`, `DecrementWrap`를 사용할 수 있습니다.
+
+## Stage
+
+현재 그래픽 셰이더는 `Stage Vertex`와 `Stage Fragment`를 사용합니다. 각 `Stage` 내부는 GLSL과 거의 같은 문법을 쓰지만, 파서가 일부 선언과 예약 심볼을 인식해 최종 GLSL 앞부분을 자동 생성합니다.
+
+```glsl
+Stage Vertex
+{
+ layout(location = 0) out vec2 fragUV;
+
+ const float scale = 1.0;
+
+ void main()
+ {
+ fragUV = UV;
+ gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX * scale, 1.0);
+ }
+}
+```
+
+### layout 입출력
+
+`layout(location = N) in/out ...;` 구문은 스테이지 입출력으로 파싱됩니다.
+
+```glsl
+layout(location = 0) out vec2 fragUV;
+layout(location = 1) out vec3 fragNormal;
+```
+
+버텍스 스테이지에서 엔진 예약 입력(`VERTEX`, `UV`, `NORMAL` 등)을 사용하면 직접 `in`을 선언하지 않아도 파서가 자동으로 입력 어트리뷰트를 추가합니다.
+
+## 엔진 예약 심볼
+
+예약 심볼은 셰이더 코드 안에서 일반 GLSL 변수처럼 사용할 수 있습니다. 파서는 사용 여부를 감지해 필요한 layout, uniform buffer, storage buffer, push constant를 자동으로 생성합니다.
+
+| 심볼 | 타입/의미 | 자동 생성 |
+| --- | --- | --- |
+| `VERTEX` | `vec3`, 정점 위치 | vertex input location 0 |
+| `UV` | `vec2`, UV | vertex input location 1 |
+| `NORMAL` | `vec3`, 노멀 | vertex input location 2 |
+| `TANGENT` | `vec3`, 탄젠트 | vertex input location 3 |
+| `BONE_INDICES` | `ivec4`, 본 인덱스 | skinned input location 4 |
+| `BONE_WEIGHTS` | `vec4`, 본 가중치 | skinned input location 5 |
+| `MATRIX_MODEL` | 모델 행렬 | push constant `CONSTANTS.model` |
+| `MATRIX_VIEW` | 카메라 view 행렬 | camera UBO `CAMERA.view` |
+| `MATRIX_PROJ` | 카메라 projection 행렬 | camera UBO `CAMERA.proj` |
+| `CAMERA` | 카메라 UBO 인스턴스 | `view`, `proj`, `pos` 멤버 |
+| `LIGHT` | 라이트 SSBO | object set의 storage buffer |
+| `SKIN` | 스키닝 SSBO | object set의 storage buffer |
+| `MATRIX_SKIN` | 스키닝 행렬 | `BONE_*`와 `SKIN.ibm[]` 기반 계산식 삽입 |
+| `TEXTURE_SHADOW` | 그림자 텍스처 | object set의 local sampler |
+
+`MATRIX_MODEL`, `MATRIX_VIEW`, `MATRIX_PROJ`는 최종 GLSL에서 각각 `CONSTANTS.model`, `CAMERA.view`, `CAMERA.proj`로 치환됩니다.
+
+### LIGHT 버퍼 형태
+
+현재 파서는 `LIGHT` 사용 시 다음과 같은 구조를 생성합니다.
+
+```glsl
+struct Light
+{
+ vec4 pos;
+ vec4 other;
+ vec4 shadowRect;
+ mat4 lightSpaceMatrix;
+};
+
+layout(std430, set = 1, binding = N) readonly buffer UNIFORM_LIGHT
+{
+ int count;
+ Light lights[];
+} LIGHT;
+```
+
+따라서 코드에서는 `LIGHT.count`, `LIGHT.lights[i].pos`, `LIGHT.lights[i].other`처럼 접근합니다.
+
+```glsl
+for (int i = 0; i < LIGHT.count; ++i)
+{
+ int type = int(LIGHT.lights[i].other.w);
+ vec3 lightPos = LIGHT.lights[i].pos.xyz;
+}
+```
+
+### MATRIX_SKIN
+
+`MATRIX_SKIN`을 사용하면 파서가 `BONE_WEIGHTS`, `BONE_INDICES`, `SKIN` 버퍼를 자동 등록하고 함수 본문 앞에 다음 계산식을 삽입합니다.
+
+```glsl
+mat4 MATRIX_SKIN =
+ BONE_WEIGHTS.x * SKIN.ibm[BONE_INDICES.x] +
+ BONE_WEIGHTS.y * SKIN.ibm[BONE_INDICES.y] +
+ BONE_WEIGHTS.z * SKIN.ibm[BONE_INDICES.z] +
+ BONE_WEIGHTS.w * SKIN.ibm[BONE_INDICES.w];
+```
+
+스키닝 셰이더에서는 보통 다음처럼 사용합니다.
+
+```glsl
+vec4 skinnedPos = MATRIX_SKIN * vec4(VERTEX, 1.0);
+vec3 skinnedNormal = mat3(MATRIX_SKIN) * NORMAL;
+```
+
+## Uniform 바인딩 규칙
+
+스테이지 내부의 단일 `uniform` 선언은 반드시 `Property`에 같은 타입과 이름으로 선언되어 있어야 합니다.
+
+```glsl
+Property
+{
+ float roughness;
+ sampler2D albedo;
+}
+
+Stage Fragment
+{
+ uniform float roughness;
+ uniform sampler2D albedo;
+}
+```
+
+파서는 스칼라/벡터/행렬 프로퍼티를 `UBO`라는 uniform block으로 묶습니다. `sampler2D`는 별도의 combined image sampler binding이 됩니다.
+
+일반 프로퍼티는 material set에 배치되고, `[Local]` 프로퍼티는 object set에 배치됩니다. 내부 enum 기준은 다음과 같습니다.
+
+| Usage | set | 용도 |
+| --- | --- | --- |
+| `Camera` | `0` | 카메라 공통 버퍼 |
+| `Object` | `1` | 오브젝트별 값, push/local/lighting/skinning |
+| `Material` | `2` | 머티리얼 공유 값 |
+
+명시적 descriptor layout도 선언할 수 있습니다. 이 경우 `Property` 검사를 거치지 않고 직접 버퍼 또는 샘플러 layout으로 추가됩니다.
+
+```glsl
+layout(set = 2, binding = 0) uniform MyBlock
+{
+ vec4 color;
+ float strength;
+} customData;
+
+layout(set = 2, binding = 1) uniform sampler2D customTex;
+```
+
+UBO는 `std140`, SSBO는 `std430` 기준으로 `UniformStructLayout`이 CPU 측 offset과 size를 계산합니다. `ShaderParser::Optimize()`는 자동 생성된 `UBO` 멤버를 스칼라, `vec2`, 그 외 큰 타입 순서로 정렬해 패딩을 줄입니다.
+
+## constexpr와 specialization constant
+
+스테이지 안에서 `constexpr`를 선언하면 Vulkan specialization constant로 변환됩니다.
+
+```glsl
+Stage Fragment
+{
+ constexpr int SAMPLE_COUNT = 16;
+
+ void main()
+ {
+ for (int i = 0; i < SAMPLE_COUNT; ++i)
+ {
+ ...
+ }
+ }
+}
+```
+
+생성되는 GLSL은 `layout (constant_id = N) const ...` 형태입니다. 런타임에서는 `Material::SetConstant(name, value)`가 패스별 constant 데이터를 갱신하고, `VulkanPipelineManager`는 constant 데이터 해시를 파이프라인 캐시 키에 포함합니다.
+
+현재 `ShaderPass`가 크기를 계산하는 specialization constant 타입은 `bool`, `int`, `float` 계열입니다.
+
+## 생성되는 GLSL 예시
+
+작성한 코드:
+
+```glsl
+Stage Vertex
+{
+ layout(location = 0) out vec2 fragUV;
+
+ void main()
+ {
+ fragUV = UV;
+ gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0);
+ }
+}
+```
+
+대략 다음과 같은 GLSL로 확장됩니다.
+
+```glsl
+#version 430 core
+layout(location = 0) in vec3 VERTEX;
+layout(location = 1) in vec2 UV;
+layout(location = 0) out vec2 fragUV;
+
+layout(set = 0, binding = 0) uniform UNIFORM_CAMERA
+{
+ mat4 view;
+ mat4 proj;
+ vec3 pos;
+} CAMERA;
+
+layout(push_constant) uniform UNIFORM_CONSTANTS
+{
+ mat4 model;
+} CONSTANTS;
+
+void main()
+{
+ fragUV = UV;
+ gl_Position = CAMERA.proj * CAMERA.view * CONSTANTS.model * vec4(VERTEX, 1.0);
+}
+```
+
+## 예시: 조명 텍스처 셰이더
```glsl
#version 430 core
-Shader "Default Shader"
+Shader "Lit Texture"
{
Property
{
float ambient;
sampler2D tex;
- sampler2D normalTex;
}
-
+
Pass
{
- LightingPass "Forward"
-
+ LightingPass "Opaque"
+
Stage Vertex
{
- layout(location = 0) out vec2 fragUvs;
+ layout(location = 0) out vec2 fragUV;
layout(location = 1) out vec3 fragPos;
- layout(location = 2) out mat3 TBN;
+ layout(location = 2) out vec3 fragNormal;
+
void main()
{
- gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0f);
- fragUvs = UV;
- fragPos = (MATRIX_MODEL * vec4(VERTEX, 1.0f)).xyz;
-
- mat3 normalMatrix = transpose(inverse(mat3(MATRIX_MODEL)));
-
- vec3 t = normalize(normalMatrix * TANGENT);
- vec3 n = normalize(normalMatrix * NORMAL);
- vec3 b = cross(n, t);
-
- TBN = mat3(t, b, n);
+ fragUV = UV;
+ fragPos = (MATRIX_MODEL * vec4(VERTEX, 1.0)).xyz;
+ fragNormal = normalize(mat3(transpose(inverse(MATRIX_MODEL))) * NORMAL);
+ gl_Position = MATRIX_PROJ * MATRIX_VIEW * vec4(fragPos, 1.0);
}
}
+
Stage Fragment
{
+ layout(location = 0) in vec2 fragUV;
+ layout(location = 1) in vec3 fragPos;
+ layout(location = 2) in vec3 fragNormal;
layout(location = 0) out vec4 outColor;
- layout(location = 0) in vec2 uvs;
- layout(location = 1) in vec3 fragPos;
- layout(location = 2) in mat3 TBN;
uniform float ambient;
uniform sampler2D tex;
- uniform sampler2D normalTex;
- void main()
+ void main()
{
- vec3 normal = texture(normalTex, uvs).xyz;
- normal = normal * 2.0 - 1.0;
- normal = normalize(TBN * normal);
-
float diffuse = 0.0;
+
for (int i = 0; i < LIGHT.count; ++i)
{
- int type = int(LIGHT.other[i].w);
+ int type = int(LIGHT.lights[i].other.w);
if (type == 0)
{
- vec3 toLightDir = -LIGHT.pos[i].xyz;
- float intensity = LIGHT.pos[i].w;
- diffuse += max(dot(normal, toLightDir), 0.0) * intensity;
+ vec3 toLightDir = -LIGHT.lights[i].pos.xyz;
+ float intensity = LIGHT.lights[i].pos.w;
+ diffuse += max(dot(fragNormal, toLightDir), 0.0) * intensity;
}
else if (type == 1)
{
- vec3 toLightVec = LIGHT.pos[i].xyz - fragPos;
+ vec3 toLightVec = LIGHT.lights[i].pos.xyz - fragPos;
vec3 toLightDir = normalize(toLightVec);
float lightDis = length(toLightVec);
- float attenuation = clamp(1.0 - (lightDis / LIGHT.pos[i].w), 0.0, 1.0);
- diffuse += max(dot(normal, toLightDir), 0.0) * attenuation;
+ float attenuation = clamp(1.0 - (lightDis / LIGHT.lights[i].pos.w), 0.0, 1.0);
+ diffuse += max(dot(fragNormal, toLightDir), 0.0) * attenuation;
}
}
- outColor = texture(tex, uvs);
- outColor.xyz *= diffuse + ambient;
+
+ outColor = texture(tex, fragUV);
+ outColor.rgb *= diffuse + ambient;
}
}
}
}
```
-### 스텐실을 이용한 아웃라인 셰이더 예시
+
+## 예시: 스텐실 아웃라인
+
+첫 번째 패스는 원본 오브젝트를 스텐실에 기록하고 컬러 출력을 막습니다. 두 번째 패스는 노멀 방향으로 정점을 밀어낸 뒤, 스텐실 값이 다른 영역만 칠합니다.
+
```glsl
#version 430 core
@@ -100,11 +464,12 @@ Shader "Outline Shader"
float outlineWidth;
vec4 color;
}
+
Pass
{
- LightingPass "Forward"
+ LightingPass "Opaque"
ColorMask 0;
-
+
Stencil
{
Ref 1;
@@ -115,26 +480,31 @@ Shader "Outline Shader"
Fail Replace;
ZFail Replace;
}
-
+
Stage Vertex
{
void main()
{
- gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0f);
+ gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0);
}
}
+
Stage Fragment
{
layout(location = 0) out vec4 outColor;
+
void main()
{
- outColor = vec4(0.0f, 0.0f, 0.0f, 0.0f);
+ outColor = vec4(0.0);
}
}
}
+
Pass
{
- LightingPass "Forward"
+ LightingPass "Opaque"
+ Cull Back;
+
Stencil
{
Ref 1;
@@ -145,25 +515,25 @@ Shader "Outline Shader"
Fail Keep;
ZFail Keep;
}
- Cull Back;
-
+
Stage Vertex
{
uniform float outlineWidth;
void main()
{
- vec4 vert = vec4(VERTEX + NORMAL * outlineWidth, 1.0f);
+ vec4 vert = vec4(VERTEX + NORMAL * outlineWidth, 1.0);
gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vert;
}
}
+
Stage Fragment
{
layout(location = 0) out vec4 outColor;
uniform vec4 color;
- void main()
+ void main()
{
outColor = color;
}
@@ -171,49 +541,15 @@ Shader "Outline Shader"
}
}
```
-### 로컬 키워드를 사용해 오브젝트별 텍스쳐 설정 가능한 반투명 셰이더 예시
-로컬 키워드로 된 유니폼 변수는 MeshRenderer MaterialPropertyBlock을 지정해준 후 값을 넣어주면 됩니다.
-
-```glsl
-#version 430 core
-Shader "Unlit Transparent Shader2"
-{
- Property
- {
- vec3 color;
- [Local] sampler2D tex;
- }
-
- Pass
- {
- LightingPass "Transparent"
-
- Cull Off;
- ZWrite Off;
- Stage Vertex
- {
- layout(location = 0) out vec2 fragUvs;
-
- void main()
- {
- gl_Position = MATRIX_PROJ * MATRIX_VIEW * MATRIX_MODEL * vec4(VERTEX, 1.0f);
- fragUvs = UV;
- }
- }
- Stage Fragment
- {
- layout(location = 0) out vec4 outColor;
- layout(location = 0) in vec2 uvs;
- uniform vec3 color;
- uniform sampler2D tex;
+## 작성 시 주의할 점
- void main()
- {
- outColor = texture(tex, uvs);
- outColor.xyz *= color.xyz;
- }
- }
- }
-}
-```
+- `Stage` 안의 일반 `uniform`은 `Property`에 먼저 선언해야 합니다. 선언이 없으면 파서가 오류를 냅니다.
+- `LIGHT`는 `LIGHT.pos[i]`가 아니라 `LIGHT.lights[i].pos` 형태로 접근해야 합니다.
+- `MATRIX_MODEL`을 사용하면 push constant가 생성됩니다. Vulkan 구현은 현재 모델 행렬 하나(`mat4`)를 push constant로 사용합니다.
+- `MATRIX_VIEW`, `MATRIX_PROJ`, `CAMERA` 중 하나를 사용하면 camera UBO가 생성됩니다.
+- `[Local]`은 오브젝트별 데이터를 위한 선언입니다. 같은 머티리얼을 여러 오브젝트가 공유할 때 개별 값을 넣어야 하는 프로퍼티에 사용합니다.
+- `sampler2D`는 UBO 멤버가 아니라 별도 sampler descriptor로 생성됩니다.
+- `mat2`는 파서와 `Shader` 프로퍼티 타입에는 존재하지만, 현재 `ShaderPass::FillAttributes()`의 UBO 멤버 구성에는 반영되어 있지 않습니다. 머티리얼 프로퍼티로 안정적으로 쓰려면 `mat3` 또는 `mat4`를 사용하거나 해당 경로를 보강해야 합니다.
+- 자동 생성되는 UBO 멤버는 최종 레이아웃 최적화를 위해 선언 순서와 다르게 정렬될 수 있습니다. CPU 측 업로드는 `UniformStructLayout`의 offset을 기준으로 하므로 이름으로 접근하는 경로를 사용해야 합니다.
+- 패스의 `LightingPass` 이름은 렌더러 쪽에서 조회하는 키입니다. 새 이름을 만들 때는 해당 렌더 단계가 실제로 그 이름을 요청하는지 함께 확인해야 합니다.
diff --git a/docs/RenderFrameFlow.svg b/docs/RenderFrameFlow.svg
new file mode 100644
index 00000000..b1b46b02
--- /dev/null
+++ b/docs/RenderFrameFlow.svg
@@ -0,0 +1,134 @@
+
diff --git a/include/Core/ArrayView.hpp b/include/Core/ArrayView.hpp
index fd49d469..b92b3a3f 100644
--- a/include/Core/ArrayView.hpp
+++ b/include/Core/ArrayView.hpp
@@ -9,17 +9,17 @@ namespace sh::core
class ArrayView
{
private:
- const T* ptr;
+ T* const ptr;
std::size_t len;
public:
- ArrayView(const T* ptr, std::size_t len) noexcept :
+ ArrayView(T* ptr, std::size_t len) noexcept :
ptr(ptr), len(len)
{}
auto operator[](int i) const noexcept -> T const& { return ptr[i]; }
auto size() const noexcept -> std::size_t { return len; }
- auto begin() noexcept -> const T* { return ptr; }
- auto end() noexcept -> const T* { return ptr + len; }
+ auto begin() noexcept -> T* { return ptr; }
+ auto end() noexcept -> T* { return ptr + len; }
};
-}
\ No newline at end of file
+}//namespace
\ No newline at end of file
diff --git a/include/Core/Asset.h b/include/Core/Asset.h
index 26df0e4e..fb5d5153 100644
--- a/include/Core/Asset.h
+++ b/include/Core/Asset.h
@@ -24,24 +24,6 @@ namespace sh::core
{
friend class AssetExporter;
friend class AssetImporter;
- private:
- struct Header
- {
- char type[4];
- uint32_t version;
- std::array uuid;
- uint64_t originalDataSize;
- uint64_t compressedDataSize;
- int64_t writeTime;
- };
- const char* type;
- uint32_t assetVersion = 0;
- int64_t writeTime = 0;
- protected:
- UUID assetUUID;
- mutable std::vector data;
- public:
- constexpr static uint32_t VERSION = 2;
protected:
SH_CORE_API explicit Asset(const char* type);
@@ -54,16 +36,6 @@ namespace sh::core
SH_CORE_API virtual void SetAsset(const core::SObject& obj) = 0;
- SH_CORE_API auto GetVersion() const -> uint32_t;
- /// @brief 에셋의 헤더 + 데이터 포함 전체 사이즈를 반환 한다.
- /// @return 에셋 사이즈
- SH_CORE_API auto GetAssetSize() const -> uint64_t;
- /// @brief 에셋의 데이터 사이즈를 반환 한다.
- /// @return 에셋 데이터 사이즈
- SH_CORE_API auto GetAssetDataSize() const -> uint64_t;
- SH_CORE_API auto GetAssetUUID() const -> const UUID&;
- SH_CORE_API auto GetType() const -> const char*;
-
/// @brief 마지막 쓰기 시간을 해당 경로에 있는 파일의 마지막 쓰기 시간으로 변경한다. 파일이 없으면 아무 일도 일어나지 않는다.
/// @param filePath 파일 경로
SH_CORE_API void SetWriteTime(const std::filesystem::path& filePath);
@@ -72,9 +44,36 @@ namespace sh::core
SH_CORE_API void SetWriteTime(int64_t time);
/// @brief 마지막 쓰기 시간을 가져오는 함수.
/// @return 시간
- SH_CORE_API auto GetWriteTime() const -> int64_t;
- SH_CORE_API auto IsEmpty() const -> bool;
+ SH_CORE_API auto GetVersion() const -> uint32_t { return assetVersion; }
+ /// @brief 에셋의 헤더 + 데이터 포함 전체 사이즈를 반환 한다.
+ /// @return 에셋 사이즈
+ SH_CORE_API auto GetAssetSize() const -> uint64_t { return sizeof(Header) + data.size(); }
+ /// @brief 에셋의 데이터 사이즈를 반환 한다.
+ /// @return 에셋 데이터 사이즈
+ SH_CORE_API auto GetAssetDataSize() const -> uint64_t { return data.size(); }
+ SH_CORE_API auto GetAssetUUID() const -> const UUID& { return assetUUID; }
+ SH_CORE_API auto GetType() const -> const char* { return type; }
+ SH_CORE_API auto GetWriteTime() const -> int64_t { return writeTime; }
+ SH_CORE_API auto IsEmpty() const -> bool { return data.empty(); }
+ public:
+ constexpr static uint32_t VERSION = 2;
+ protected:
+ UUID assetUUID;
+ mutable std::vector data;
+ private:
+ struct Header
+ {
+ char type[4];
+ uint32_t version;
+ std::array uuid;
+ uint64_t originalDataSize;
+ uint64_t compressedDataSize;
+ int64_t writeTime;
+ };
+ uint32_t assetVersion = 0;
+ int64_t writeTime = 0;
+ const char* type;
};
}//namespace
\ No newline at end of file
diff --git a/include/Core/Name.h b/include/Core/Name.h
index cc8e1d11..23f799e3 100644
--- a/include/Core/Name.h
+++ b/include/Core/Name.h
@@ -1,5 +1,6 @@
#pragma once
#include "Export.h"
+#include "Util.h"
#include
#include
@@ -10,6 +11,24 @@ namespace sh::core
/// @brief 같은 문자열은 같은 주소를 가르키며 생성 시 스레드 안전하다.
class Name
{
+ public:
+ SH_CORE_API Name(std::string_view str);
+ SH_CORE_API Name(const Name& other) noexcept;
+ SH_CORE_API Name(Name&& other) noexcept;
+ SH_CORE_API ~Name();
+
+ SH_CORE_API operator const std::string& () const;
+ SH_CORE_API auto ToString() const -> const std::string&;
+
+ SH_CORE_API auto operator=(const Name& other) noexcept -> Name&;
+ SH_CORE_API auto operator=(Name&& other) noexcept -> Name&;
+
+ auto operator==(const Name& other) const -> bool { return hash == other.hash; }
+ auto operator!=(const Name& other) const -> bool { return hash != other.hash; }
+ auto operator==(const std::string_view str) const -> bool { return hash == core::Util::ConstexprHash(str); }
+ auto operator!=(const std::string_view str) const -> bool { return hash != core::Util::ConstexprHash(str); }
+ auto operator==(std::size_t hash) const -> bool { return this->hash == hash; }
+ auto operator!=(std::size_t hash) const -> bool { return this->hash != hash; }
private:
friend struct std::hash;
@@ -20,30 +39,13 @@ namespace sh::core
std::string debugString;
#endif
std::size_t hash;
- public:
- SH_CORE_API Name(std::string_view str);
- SH_CORE_API Name(const Name& other) noexcept;
- SH_CORE_API Name(Name&& other) noexcept;
- SH_CORE_API ~Name();
-
- SH_CORE_API auto operator=(const Name& other) noexcept -> Name&;
- SH_CORE_API auto operator=(Name&& other) noexcept -> Name&;
- SH_CORE_API auto operator==(const Name& other) const -> bool;
- SH_CORE_API auto operator!=(const Name& other) const -> bool;
- SH_CORE_API auto operator==(const std::string_view str) const -> bool;
- SH_CORE_API auto operator!=(const std::string_view str) const -> bool;
- SH_CORE_API auto operator==(std::size_t hash) const -> bool;
- SH_CORE_API auto operator!=(std::size_t hash) const -> bool;
-
- SH_CORE_API operator const std::string& () const;
- SH_CORE_API auto ToString() const -> const std::string&;
};
- static auto operator==(std::string_view str, const sh::core::Name& name) -> bool
+ inline static auto operator==(std::string_view str, const sh::core::Name& name) -> bool
{
return name == str;
}
- static auto operator!=(std::string_view str, const sh::core::Name& name) -> bool
+ inline static auto operator!=(std::string_view str, const sh::core::Name& name) -> bool
{
return name != str;
}
@@ -59,4 +61,4 @@ namespace std
return name.hash;
}
};
-}
+}//namespace
\ No newline at end of file
diff --git a/include/Core/Reflection/STypeInfo.hpp b/include/Core/Reflection/STypeInfo.hpp
index 8011515c..8ef6f6fd 100644
--- a/include/Core/Reflection/STypeInfo.hpp
+++ b/include/Core/Reflection/STypeInfo.hpp
@@ -93,6 +93,7 @@ namespace sh::core::reflection
hash(data.hash), super(data.super),
name(data.name), type(GetType())
{
+ typeInfoMap.insert({ type.hash, this });
}
STypeInfo(const STypeInfo& other) = delete;
SH_CORE_API ~STypeInfo();
@@ -116,6 +117,13 @@ namespace sh::core::reflection
SH_CORE_API auto GetFunction(const core::Name& name) const -> Function*;
SH_CORE_API auto GetFunction(std::string_view name) const -> Function*;
SH_CORE_API auto GetFunctions() const -> const std::vector>& { return functions; }
+
+ /// @brief TypeInfo를 STypeInfo로 변환하는 함수.
+ ///
+ /// TypeInfo가 가르키는 타입이 포인터였어도 STypeInfo는 원본 클래스의 타입을 가르킨다.
+ /// @param typeInfo 일반 타입
+ /// @return 변환 할 수 없으면 nullptr
+ SH_CORE_API static auto ConvertFromTypeInfo(const core::reflection::TypeInfo& typeInfo) -> const STypeInfo*;
public:
const core::Name name;
const TypeInfo& type;
@@ -127,6 +135,8 @@ namespace sh::core::reflection
std::vector sobjPtrs;
std::vector sobjPtrContainers;
std::vector> functions;
+
+ SH_CORE_API static std::unordered_map typeInfoMap; // key = typeHash
};//STypeInfo
/// @brief 리플렉션 데이터의 DLL간 공유를 위한 구조체
diff --git a/include/Editor/AssetExtensions.h b/include/Editor/AssetExtensions.h
index a0e08d05..4c7c695f 100644
--- a/include/Editor/AssetExtensions.h
+++ b/include/Editor/AssetExtensions.h
@@ -12,6 +12,7 @@ namespace sh::editor
{
None,
Model,
+ Mesh,
Texture,
Material,
World,
@@ -21,7 +22,8 @@ namespace sh::editor
Font,
Binary,
Sound,
- ScriptableObject
+ ScriptableObject,
+ ComputeShader,
};
public:
SH_EDITOR_API static void AddExtension(const std::string& ext, Type type);
diff --git a/include/Editor/AssetLoaderRegistry.h b/include/Editor/AssetLoaderRegistry.h
index a6064f14..7f350a53 100644
--- a/include/Editor/AssetLoaderRegistry.h
+++ b/include/Editor/AssetLoaderRegistry.h
@@ -19,10 +19,12 @@ namespace sh::editor
bool bObjDataInMeta;
};
public:
- SH_EDITOR_API void RegisterLoader(AssetExtensions::Type ext, std::unique_ptr&& loader, int priority, bool bObjDataInMeta);
+ SH_EDITOR_API void RegisterLoader(AssetExtensions::Type ext, std::unique_ptr loader, int priority, bool bObjDataInMeta);
+ SH_EDITOR_API auto GetLoader(const char* assetType) const -> const Importer*;
SH_EDITOR_API auto GetLoader(AssetExtensions::Type ext) const -> const Importer*;
SH_EDITOR_API void Clear();
private:
std::unordered_map loaders;
+ std::unordered_map assetTypeMap;
};
}//namespace
\ No newline at end of file
diff --git a/include/Editor/BuildSystem.h b/include/Editor/BuildSystem.h
index a0455179..de8b2246 100644
--- a/include/Editor/BuildSystem.h
+++ b/include/Editor/BuildSystem.h
@@ -32,6 +32,7 @@ namespace sh::editor
void ExtractUUIDs(std::unordered_set& set, const core::Json& world);
void PackingAssets(core::AssetBundle& bundle, game::World& world);
void ExportGameManager(const std::filesystem::path& outputPath);
+ void CopyRuntimeBinaries(const std::filesystem::path& outputPath);
private:
Project* currentProject = nullptr;
diff --git a/include/Editor/Component/OutlineComponent.h b/include/Editor/Component/OutlineComponent.h
index 5bc7049a..beb41396 100644
--- a/include/Editor/Component/OutlineComponent.h
+++ b/include/Editor/Component/OutlineComponent.h
@@ -17,5 +17,7 @@ namespace sh::editor
SH_EDITOR_API void BeginUpdate() override;
SH_EDITOR_API auto Serialize() const -> core::Json override;
+ protected:
+ SH_EDITOR_API void CreateDrawable(bool) override;
};
}//namespace
\ No newline at end of file
diff --git a/include/Editor/DragDropHelper.hpp b/include/Editor/DragDropHelper.hpp
new file mode 100644
index 00000000..47dbbfe3
--- /dev/null
+++ b/include/Editor/DragDropHelper.hpp
@@ -0,0 +1,46 @@
+#pragma once
+#include "Core/SObject.h"
+#include "Core/Reflection/STypeInfo.hpp"
+
+#include "Game/ImGUImpl.h"
+
+namespace sh::editor::dragdrop
+{
+ template
+ auto AcceptAsset() -> T*
+ {
+ const ImGuiPayload* current = ImGui::GetDragDropPayload();
+ if (current == nullptr || !current->IsDataType("SObject"))
+ return nullptr;
+
+ auto* obj = *reinterpret_cast(current->Data);
+ if (!obj->GetType().IsChildOf(T::GetStaticType()))
+ return nullptr;
+
+ if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("SObject"))
+ return static_cast(*reinterpret_cast(payload->Data));
+ return nullptr;
+ }
+
+ inline auto AcceptAsset(const core::reflection::STypeInfo& type) -> core::SObject*
+ {
+ const ImGuiPayload* current = ImGui::GetDragDropPayload();
+ if (current == nullptr || !current->IsDataType("SObject"))
+ return nullptr;
+
+ auto* obj = *reinterpret_cast(current->Data);
+ if (!obj->GetType().IsChildOf(type))
+ return nullptr;
+
+ if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("SObject"))
+ return *reinterpret_cast(payload->Data);
+ return nullptr;
+ }
+
+ inline auto AcceptAnyAsset() -> core::SObject*
+ {
+ if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("SObject"))
+ return static_cast(*reinterpret_cast(payload->Data));
+ return nullptr;
+ }
+}//namespace
\ No newline at end of file
diff --git a/include/Editor/EditorOutlinePass.h b/include/Editor/EditorOutlinePass.h
index 8ee4a871..a301c690 100644
--- a/include/Editor/EditorOutlinePass.h
+++ b/include/Editor/EditorOutlinePass.h
@@ -3,6 +3,7 @@
#include "Core/SContainer.hpp"
+#include "Render/RenderData.h"
#include "Render/ScriptableRenderPass.h"
#include "Game/Component/Render/Camera.h"
@@ -22,9 +23,9 @@ namespace sh::editor
SH_EDITOR_API void SetOutTexture(render::RenderTexture& tex);
protected:
- SH_EDITOR_API void Configure(const render::RenderTarget& renderData) override;
- SH_EDITOR_API void Record(render::CommandBuffer& cmd, const render::IRenderContext& ctx, const render::RenderTarget& renderData) override;
+ SH_EDITOR_API void Configure(const render::RenderData& renderData) override;
+ SH_EDITOR_API void Record(render::CommandBuffer& cmd, const render::IRenderContext& ctx, const render::RenderData& renderData) override;
private:
- core::SObjWeakPtr output = nullptr;
+ render::RenderData rd;
};
}//namespace
\ No newline at end of file
diff --git a/include/Editor/EditorPostOutlinePass.h b/include/Editor/EditorPostOutlinePass.h
index 31ed0e5a..c8478ccd 100644
--- a/include/Editor/EditorPostOutlinePass.h
+++ b/include/Editor/EditorPostOutlinePass.h
@@ -19,8 +19,8 @@ namespace sh::editor
SH_EDITOR_API ~EditorPostOutlinePass();
SH_EDITOR_API void SetOutlineMaterial(render::Material& mat);
protected:
- SH_EDITOR_API void Configure(const render::RenderTarget& renderData) override;
- SH_EDITOR_API auto BuildDrawList(const render::RenderTarget& renderData) -> render::DrawList override;
+ SH_EDITOR_API void Configure(const render::RenderData& renderData) override;
+ SH_EDITOR_API void Record(render::CommandBuffer& cmd, const render::IRenderContext& ctx, const render::RenderData& renderData) override;
private:
const render::IRenderContext& ctx;
diff --git a/include/Editor/EditorRenderer.h b/include/Editor/EditorRenderer.h
index 320a2140..7dc4b83f 100644
--- a/include/Editor/EditorRenderer.h
+++ b/include/Editor/EditorRenderer.h
@@ -3,42 +3,32 @@
#include "EditorOutlinePass.h"
#include "EditorPostOutlinePass.h"
-#include "Render/ScriptableRenderer.h"
-
#include "Game/Component/Render/Camera.h"
+#include "Game/GameRenderer.h"
#include
namespace sh::game
{
class ImGUImpl;
- class UIPass;
+ class GUIPass;
+ class World;
}
namespace sh::editor
{
- class EditorRenderer : public render::ScriptableRenderer
+ class EditorRenderer : public game::GameRenderer
{
public:
- SH_EDITOR_API EditorRenderer(render::IRenderContext& ctx, game::ImGUImpl& guictx);
+ SH_EDITOR_API EditorRenderer(render::IRenderContext& ctx, game::ImGUImpl& guictx, game::World& world);
- SH_EDITOR_API auto GetOutlinePass() const -> EditorOutlinePass* { return outlinePass; }
- SH_EDITOR_API auto GetPostOutlinePass() const -> EditorPostOutlinePass* { return postOutlinePass; }
+ SH_EDITOR_API void Init() override;
- SH_EDITOR_API void SetImGUICamera(const render::Camera& camera);
- SH_EDITOR_API void SetEditorCamera(const render::Camera& camera);
- SH_EDITOR_API void SetPickingCamera(const render::Camera& camera);
+ auto GetOutlinePass() const -> EditorOutlinePass* { return outlinePass; }
+ auto GetPostOutlinePass() const -> EditorPostOutlinePass* { return postOutlinePass; }
protected:
- SH_EDITOR_API void Setup(const render::RenderTarget& data) override;
+ SH_EDITOR_API void Setup(const render::RenderData& data) override;
private:
- const render::Camera* editorCamera = nullptr;
- const render::Camera* ImGUICamera = nullptr;
- const render::Camera* pickingCamera = nullptr;
-
+ render::ScriptableRenderPass* pickingPass = nullptr;
EditorOutlinePass* outlinePass = nullptr;
EditorPostOutlinePass* postOutlinePass = nullptr;
- game::UIPass* uiPass = nullptr;
- //render::TransparentPipeline* transParentPass = nullptr;
-
- std::unordered_map> allowedCamera;
- std::unordered_map> ignoreCamera;
};
}//namespace
\ No newline at end of file
diff --git a/include/Editor/UI/CustomHierarchy.h b/include/Editor/UI/CustomHierarchy.h
index 978dd57e..b0bdcf9f 100644
--- a/include/Editor/UI/CustomHierarchy.h
+++ b/include/Editor/UI/CustomHierarchy.h
@@ -32,7 +32,7 @@ namespace sh::editor
virtual ~ICustomHierarchy() = default;
virtual void OnHierarchyInstantiated(EditorWorld& world) {};
- virtual void OnHierarchyDraged(EditorWorld& world, const ImGuiPayload& payload) {};
+ virtual void OnHierarchyDraged(EditorWorld& world, core::SObject& payload) {};
};
class CustomHierarchyManager : public core::Singleton
diff --git a/include/Editor/UI/DefaultHierarchy.h b/include/Editor/UI/DefaultHierarchy.h
index 99270773..9aef59a6 100644
--- a/include/Editor/UI/DefaultHierarchy.h
+++ b/include/Editor/UI/DefaultHierarchy.h
@@ -12,12 +12,12 @@ namespace sh::editor
{
HIERARCHY(ModelHierarchy, render::Model)
public:
- SH_EDITOR_API void OnHierarchyDraged(EditorWorld& world, const ImGuiPayload& payload) override;
+ SH_EDITOR_API void OnHierarchyDraged(EditorWorld& world, core::SObject& payload) override;
};
class PrefabHierarchy : public ICustomHierarchy
{
HIERARCHY(PrefabHierarchy, game::Prefab)
public:
- SH_EDITOR_API void OnHierarchyDraged(EditorWorld& world, const ImGuiPayload& payload) override;
+ SH_EDITOR_API void OnHierarchyDraged(EditorWorld& world, core::SObject& payload) override;
};
}//namespace
\ No newline at end of file
diff --git a/include/Editor/UI/Hierarchy.h b/include/Editor/UI/Hierarchy.h
index ea374cc5..1783f1d8 100644
--- a/include/Editor/UI/Hierarchy.h
+++ b/include/Editor/UI/Hierarchy.h
@@ -27,7 +27,7 @@ namespace sh::editor
/// @brief 뒤늦게 추가 된 기능으로 덮어 씌워진다.
/// @param dragItem 드래그 드롭 아이템 이름
/// @param func 작동 함수
- SH_EDITOR_API void RegisterDragItemFunction(const std::string& dragItem, const std::function& func);
+ SH_EDITOR_API void RegisterDragItemFunction(const std::string& dragItem, const std::function& func);
SH_EDITOR_API void AddOtherWorld(game::World& world);
SH_EDITOR_API void RegisterPopupMenu(std::string_view name, const std::function& fn);
@@ -54,7 +54,7 @@ namespace sh::editor
core::EventSubscriber gameObjectEventSubscriber;
core::EventSubscriber gameObjectEventSubscriberOther;
- std::vector>> dragFunc;
+ std::vector>> dragFunc;
struct PopupMenu
{
diff --git a/include/Editor/UI/ProjectExplorer.h b/include/Editor/UI/ProjectExplorer.h
index 4538cb81..fc25d666 100644
--- a/include/Editor/UI/ProjectExplorer.h
+++ b/include/Editor/UI/ProjectExplorer.h
@@ -1,6 +1,8 @@
#pragma once
#include "Editor/Export.h"
+#include "Core/SObject.h"
+
#include "Game/ImGUImpl.h"
#include
diff --git a/include/Game/Asset/ComputeShaderAsset.h b/include/Game/Asset/ComputeShaderAsset.h
new file mode 100644
index 00000000..42507bc0
--- /dev/null
+++ b/include/Game/Asset/ComputeShaderAsset.h
@@ -0,0 +1,32 @@
+#pragma once
+#include "../Export.h"
+
+#include "Core/Asset.h"
+#include "Core/SContainer.hpp"
+#include "Core/ISerializable.h"
+
+#include "Render/ComputeShader.h"
+
+namespace sh::game
+{
+ class ComputeShaderAsset : public core::Asset
+ {
+ SASSET(ComputeShaderAsset, "comp")
+ public:
+ constexpr static const char* ASSET_NAME = "comp";
+ public:
+ SH_GAME_API ComputeShaderAsset();
+ SH_GAME_API ComputeShaderAsset(const render::ComputeShader& shader);
+
+ SH_GAME_API void SetAsset(const core::SObject& obj) override;
+
+ SH_GAME_API auto GetShaderObjectJson() const -> const core::Json& { return shaderObjJson; }
+ protected:
+ SH_GAME_API void SetAssetData() const override;
+ SH_GAME_API auto ParseAssetData() -> bool override;
+ private:
+ core::SObjWeakPtr shaderPtr;
+
+ core::Json shaderObjJson;
+ };
+}//namespace
diff --git a/include/Game/Asset/ComputeShaderLoader.h b/include/Game/Asset/ComputeShaderLoader.h
new file mode 100644
index 00000000..a076a4c7
--- /dev/null
+++ b/include/Game/Asset/ComputeShaderLoader.h
@@ -0,0 +1,37 @@
+#pragma once
+#include "../Export.h"
+
+#include "Core/Asset.h"
+#include "Core/IAssetLoader.h"
+
+#include "Render/ComputeShader.h"
+
+#include
+
+namespace sh::render
+{
+ class IRenderContext;
+}
+namespace sh::game
+{
+
+ class ComputeShaderLoader : public core::IAssetLoader
+ {
+ public:
+ SH_GAME_API ComputeShaderLoader(const render::IRenderContext& ctx);
+ SH_GAME_API ~ComputeShaderLoader();
+
+ SH_GAME_API void SetCachePath(const std::filesystem::path& path) { cachePath = path; }
+
+ SH_GAME_API auto Load(const std::filesystem::path& filePath) const -> core::SObject* override;
+ SH_GAME_API auto Load(const core::Asset& asset) const -> core::SObject* override;
+
+ SH_GAME_API auto GetCachePath() const -> const std::filesystem::path& { return cachePath; }
+ SH_GAME_API auto GetAssetName() const -> const char* override { return ASSET_NAME; }
+ private:
+ static constexpr const char* ASSET_NAME = "comp";
+ const render::IRenderContext& ctx;
+
+ std::filesystem::path cachePath;
+ };
+}//namespace
diff --git a/include/Game/Asset/MeshAsset.h b/include/Game/Asset/MeshAsset.h
index 6aa21404..cc1b2c9b 100644
--- a/include/Game/Asset/MeshAsset.h
+++ b/include/Game/Asset/MeshAsset.h
@@ -5,6 +5,7 @@
#include "Core/SContainer.hpp"
#include "Render/Mesh.h"
+#include "Render/SkinnedMesh.h"
namespace sh::game
{
class MeshAsset : public core::Asset
@@ -13,8 +14,11 @@ namespace sh::game
public:
struct Header
{
- uint64_t vertexCount;
- uint64_t indexCount;
+ uint64_t vertexCount = 0;
+ uint64_t indexCount = 0;
+ uint64_t boneVertexCount = 0; // 0이면 일반 메쉬
+ uint64_t subMeshCount = 0;
+ uint64_t ibmCount = 0;
};
struct MeshData
{
@@ -24,6 +28,7 @@ namespace sh::game
public:
SH_GAME_API MeshAsset();
SH_GAME_API MeshAsset(const render::Mesh& mesh);
+ SH_GAME_API MeshAsset(const render::SkinnedMesh& mesh);
SH_GAME_API void SetAsset(const core::SObject& obj) override;
@@ -39,4 +44,4 @@ namespace sh::game
Header header;
};
-}//namespace
\ No newline at end of file
+}//namespace
diff --git a/include/Game/Asset/ModelAsset.h b/include/Game/Asset/ModelAsset.h
index 06257aa8..5a34b29c 100644
--- a/include/Game/Asset/ModelAsset.h
+++ b/include/Game/Asset/ModelAsset.h
@@ -5,58 +5,52 @@
#include "Core/SContainer.hpp"
#include "Render/Model.h"
+#include "Render/Skeleton.h"
#include
namespace sh::game
{
/// @brief 모델 에셋 클래스
/// @brief 데이터 구조
- /// @brief 모델 헤더 | (메쉬 헤더 | 메쉬 데이터(버텍스, 인덱스)) | 노드 헤더
+ /// @brief 모델 헤더 | (노드 헤더 | 노드 데이터)... | (스켈레톤 헤더 | 스켈레톤 데이터)...
class ModelAsset : public core::Asset
{
SASSET(ModelAsset, "mdel")
public:
+ SH_GAME_API ModelAsset();
+ SH_GAME_API ModelAsset(const render::Model& model);
+ SH_GAME_API ~ModelAsset();
+
+ SH_GAME_API void SetAsset(const core::SObject& obj) override;
+
+ SH_GAME_API auto GetData() const -> const std::vector&;
+ SH_GAME_API auto GetSkeletonData() const -> const std::vector&;
+ protected:
+ SH_GAME_API void SetAssetData() const override;
+ SH_GAME_API auto ParseAssetData() -> bool override;
+ public:
+ constexpr static const char* ASSET_NAME = "mdel";
+ private:
struct ModelHeader
{
- uint64_t meshCount;
- uint64_t nodeCount;
- };
- struct MeshHeader
- {
- std::array uuid;
- uint64_t vertexCount;
- uint64_t indexCount;
+ uint64_t nodeCount = 0;
+ uint64_t skeletonCount = 0;
};
struct NodeHeader
{
- glm::mat4 modelMatrix;
- int32_t meshIndex; // 메쉬가 없는 경우 -1
- int32_t parentIndex; // 루트인 경우 -1
- char name[128];
+ int nameSize = 0;
+ int skeletonIdx = -1;
+ int childrenCount = 0;
+ glm::mat4 modelMatrix{ 1.f };
+ std::array meshUUID;
};
- struct ModelData
+ struct SkeletonHeader
{
- const uint8_t* dataPtr;
- std::size_t size;
+ uint64_t jointCount = 0;
};
- private:
- core::SObjWeakPtr modelPtr;
- ModelHeader header;
- private:
- auto SetHeader(ModelHeader& header) const -> std::vector;
- public:
- constexpr static const char* ASSET_NAME = "mdel";
- protected:
- SH_GAME_API void SetAssetData() const override;
- SH_GAME_API auto ParseAssetData() -> bool override;
- public:
- SH_GAME_API ModelAsset();
- SH_GAME_API ModelAsset(const render::Model& model);
- SH_GAME_API ~ModelAsset();
- SH_GAME_API void SetAsset(const core::SObject& obj) override;
-
- SH_GAME_API auto GetHeader() const -> ModelHeader;
- SH_GAME_API auto GetData() const -> ModelData;
+ core::SObjWeakPtr modelPtr;
+ std::vector nodeData;
+ std::vector skeletonData;
};
}//namespace
\ No newline at end of file
diff --git a/include/Game/Asset/ShaderAsset.h b/include/Game/Asset/ShaderAsset.h
index 06113798..fe44c221 100644
--- a/include/Game/Asset/ShaderAsset.h
+++ b/include/Game/Asset/ShaderAsset.h
@@ -12,15 +12,8 @@ namespace sh::game
class ShaderAsset : public core::Asset
{
SASSET(ShaderAsset, "shad")
- private:
- core::SObjWeakPtr shaderPtr;
-
- core::Json shaderObjJson;
public:
constexpr static const char* ASSET_NAME = "shad";
- protected:
- SH_GAME_API void SetAssetData() const override;
- SH_GAME_API auto ParseAssetData() -> bool override;
public:
SH_GAME_API ShaderAsset();
SH_GAME_API ShaderAsset(const render::Shader& shader);
@@ -28,5 +21,12 @@ namespace sh::game
SH_GAME_API void SetAsset(const core::SObject& obj) override;
SH_GAME_API auto GetShaderObjectJson() const -> const core::Json&;
+ protected:
+ SH_GAME_API void SetAssetData() const override;
+ SH_GAME_API auto ParseAssetData() -> bool override;
+ private:
+ core::SObjWeakPtr shaderPtr;
+
+ core::Json shaderObjJson;
};
}//namespace
\ No newline at end of file
diff --git a/include/Game/Component/Render/Camera.h b/include/Game/Component/Render/Camera.h
index 7ce847db..bb046178 100644
--- a/include/Game/Component/Render/Camera.h
+++ b/include/Game/Component/Render/Camera.h
@@ -3,9 +3,8 @@
#include "Game/Vector.h"
#include "Game/Component/Component.h"
-#include "Render/Renderer.h"
+#include "Render/RenderData.h"
#include "Render/RenderTexture.h"
-#include "Render/Camera.h"
#include "glm/mat4x4.hpp"
@@ -33,65 +32,64 @@ namespace sh::game
SH_GAME_API void BeginUpdate() override;
SH_GAME_API void OnDestroy() override;
- SH_GAME_API void SetActive(bool b) override;
-
- SH_GAME_API auto GetProjMatrix() const -> const glm::mat4&;
- SH_GAME_API auto GetViewMatrix() const -> const glm::mat4&;
+ SH_GAME_API auto ScreenPointToRay(const Vec2& mousePos) const -> phys::Ray;
+ SH_GAME_API auto ScreenPointToRayOrtho(const Vec2& mousePos) const -> phys::Ray;
- SH_GAME_API void SetDepth(int depth);
- SH_GAME_API auto GetDepth() const -> int { return depth; }
+ SH_GAME_API void SetPriority(int priority);
SH_GAME_API void SetFov(float degree);
+ void SetRenderTexture(render::RenderTexture* renderTexture) { this->renderTexture = renderTexture; }
+ void SetLookPos(const Vec3& pos) { lookPos = pos; }
+ void SetUpVector(const Vec3& up) { this->up = up; }
+ void SetProjection(Projection proj) { projection = proj; }
+ void SetNearPlane(float nearPlane) { this->nearPlane = nearPlane; }
+ void SetFarPlane(float farPlane) { this->farPlane = farPlane; }
+ void SetTag(const core::Name& tag) { renderData.tag = tag; }
+ auto GetPriority() const -> int { return priority; }
/// @brief 세로 시야각을 가져오는 함수.
/// @return Degree로 반환한다.
- SH_GAME_API auto GetFov() const -> float { return fov; }
+ auto GetFov() const -> float { return fov; }
/// @brief 가로 시야각을 가져오는 함수.
/// @return Degree로 반환한다.
- SH_GAME_API auto GetFovx() const -> float { return fovx; }
-
- SH_GAME_API void SetRenderTexture(render::RenderTexture* renderTexture);
- SH_GAME_API auto GetRenderTexture() const -> render::RenderTexture*;
-
- SH_GAME_API auto GetNative() -> render::Camera&;
- SH_GAME_API auto GetNative() const -> const render::Camera&;
-
- SH_GAME_API auto ScreenPointToRay(const Vec2& mousePos) const -> phys::Ray;
- SH_GAME_API auto ScreenPointToRayOrtho(const Vec2& mousePos) const -> phys::Ray;
-
- SH_GAME_API void SetLookPos(const Vec3& pos);
- SH_GAME_API auto GetLookPos() const -> const Vec3&;
- SH_GAME_API void SetUpVector(const Vec3& up);
- SH_GAME_API auto GetUpVector() const -> Vec3;
-
- SH_GAME_API void SetWidth(float width);
- SH_GAME_API auto GetWidth() const -> float;
- SH_GAME_API void SetHeight(float height);
- SH_GAME_API auto GetHeight() const -> float;
-
- SH_GAME_API void SetProjection(Projection proj);
- SH_GAME_API auto GetProjection() const -> Projection;
-
- SH_GAME_API void SetNearPlane(float near);
- SH_GAME_API auto GetNearPlane() const -> float;
- SH_GAME_API void SetFarPlane(float near);
- SH_GAME_API auto GetFarPlane() const -> float;
-
- SH_GAME_API void OnPropertyChanged(const core::reflection::Property& prop) override;
+ auto GetFovx() const -> float { return fovx; }
+ auto GetWidth() const -> float { return width; }
+ auto GetHeight() const -> float { return height; }
+ auto GetNearPlane() const -> float { return nearPlane; }
+ auto GetFarPlane() const -> float { return farPlane; }
+ auto GetUpVector() const -> const Vec3& { return up; }
+ auto GetProjMatrix() const -> const glm::mat4& { return renderData.renderViewers.front().projMatrix; }
+ auto GetViewMatrix() const -> const glm::mat4& { return renderData.renderViewers.front().viewMatrix; }
+ auto GetLookPos() const -> const Vec3& { return lookPos; }
+ auto GetTag() const -> const core::Name& { return renderData.tag; }
+ auto GetProjection() const -> Projection { return projection; }
+ auto GetRenderTexture() const -> render::RenderTexture* { return renderTexture; }
+ auto GetRenderData() const -> const render::RenderData& { return renderData; }
+ protected:
+ SH_GAME_API virtual void UpdateViewMatrix();
+ SH_GAME_API virtual void UpdateProjMatrix();
protected:
- render::Camera camera;
-
PROPERTY(lookPos)
Vec3 lookPos;
private:
PROPERTY(renderTexture)
render::RenderTexture* renderTexture;
- PROPERTY(depth)
- int depth;
+ PROPERTY(priority)
+ int priority = 0;
PROPERTY(fov)
- float fov;
- float fovx;
+ float fov = 60.f;
+ float fovRadians = 0.f;
+ float fovx = 0.f;
PROPERTY(projection)
Projection projection = Projection::Perspective;
+ Vec3 up{ 0.f, 1.f, 0.f };
+ PROPERTY(nearPlane)
+ float nearPlane = 0.1f;
+ PROPERTY(farPlane)
+ float farPlane = 1000.f;
+ float width = 1.f;
+ float height = 1.f;
+
+ render::RenderData renderData;
};
-}
\ No newline at end of file
+}//namespace
\ No newline at end of file
diff --git a/include/Game/Component/Render/DebugRenderer.h b/include/Game/Component/Render/DebugRenderer.h
index 97a8fef5..3ff3f266 100644
--- a/include/Game/Component/Render/DebugRenderer.h
+++ b/include/Game/Component/Render/DebugRenderer.h
@@ -12,17 +12,18 @@ namespace sh::game
public:
SH_GAME_API DebugRenderer(GameObject& owner);
- SH_GAME_API auto Serialize() const -> core::Json override;
+ SH_GAME_API auto Serialize() const -> core::Json override { return core::Json{}; }
SH_GAME_API void SetPosition(const Vec3& pos);
- SH_GAME_API auto GetPosition() const -> const Vec3&;
SH_GAME_API void SetScale(const Vec3& scale);
- SH_GAME_API auto GetScale() const -> const Vec3&;
SH_GAME_API void SetRotation(const Vec3& rot);
SH_GAME_API void SetQuat(const glm::quat& quat);
+
+ SH_GAME_API auto GetPosition() const -> const Vec3& { return position; }
+ SH_GAME_API auto GetScale() const -> const Vec3& { return scale; }
SH_GAME_API auto GetQuat() const -> glm::quat { return quat; }
protected:
- SH_GAME_API void CreateDrawable() override;
+ SH_GAME_API void CreateDrawable(bool) override;
SH_GAME_API void UpdateDrawable() override;
private:
PROPERTY(position)
diff --git a/include/Game/Component/Render/DirectionalLight.h b/include/Game/Component/Render/DirectionalLight.h
index 5a2ca5ab..781eaaa4 100644
--- a/include/Game/Component/Render/DirectionalLight.h
+++ b/include/Game/Component/Render/DirectionalLight.h
@@ -1,33 +1,47 @@
#pragma once
#include "Game/Export.h"
-#include "Game/ILight.h"
-#include "Game/Component/Component.h"
+#include "Game/Component/Render/LightBase.h"
+
+#include "Render/RenderData.h"
+#include "Render/ShadowMapManager.h"
+
+#include
+
+namespace sh::render
+{
+ class RenderTexture;
+}
namespace sh::game
{
- class DirectionalLight : public Component, public ILight
+ class DirectionalLight : public LightBase
{
COMPONENT(DirectionalLight)
- private:
- PROPERTY(direction)
- Vec3 direction{ -1.f, -1.f, -1.f };
- PROPERTY(intensity)
- float intensity = 1.f;
public:
SH_GAME_API DirectionalLight(GameObject& owner);
SH_GAME_API ~DirectionalLight();
- SH_GAME_API void OnDestroy() override;
-
- SH_GAME_API bool Intersect(const render::AABB& aabb) const override;
-
- SH_GAME_API void SetIntensity(float intensity);
- SH_GAME_API auto GetIntensity() const -> float;
+ SH_GAME_API auto Intersect(const render::AABB& aabb) const -> bool override;
+ SH_GAME_API void SetIntensity(float intensity) override;
SH_GAME_API void SetDirection(const Vec3& dir);
- SH_GAME_API auto GetDirection() const -> const Vec3&;
+ /// @brief 라이트의 광원 공간 변환 행렬 (proj * view)을 반환한다.
+ SH_GAME_API auto GetLightSpaceMatrix() const -> glm::mat4 override;
+
+ SH_GAME_API auto GetShadowViewMatrix() const -> glm::mat4 override;
+ SH_GAME_API auto GetShadowProjMatrix() const -> glm::mat4 override;
+ SH_GAME_API auto GetShadowPos() const -> glm::vec3 override;
+ SH_GAME_API auto GetShadowLookAt() const -> glm::vec3 override;
SH_GAME_API auto GetPos() const -> const Vec3& override;
- SH_GAME_API auto GetLightType() const -> ILight::Type override;
+
+ auto GetLightType() const -> ILight::Type override { return ILight::Type::Directional; }
+ auto GetIntensity() const -> float override { return intensity; }
+ auto GetDirection() const -> const Vec3& { return direction; }
+ private:
+ PROPERTY(direction)
+ Vec3 direction{ -1.f, -1.f, -1.f };
+ PROPERTY(intensity)
+ float intensity = 1.f;
};
-}//namespace
\ No newline at end of file
+}//namespace
diff --git a/include/Game/Component/Render/LightBase.h b/include/Game/Component/Render/LightBase.h
new file mode 100644
index 00000000..d33e3520
--- /dev/null
+++ b/include/Game/Component/Render/LightBase.h
@@ -0,0 +1,67 @@
+#pragma once
+#include "Game/Export.h"
+#include "Game/Component/Component.h"
+#include "Game/ILight.h"
+
+#include "Render/ShadowMapManager.h"
+
+namespace sh::game
+{
+ /// @brief 광원 컴포넌트 추상 클래스
+ class LightBase : public Component, public ILight, public render::IShadowCaster
+ {
+ SCLASS(LightBase)
+ public:
+ SH_GAME_API LightBase(GameObject& owner);
+
+ SH_GAME_API void Awake() override;
+ SH_GAME_API void OnDestroy() override;
+ SH_GAME_API void OnEnable() override;
+ SH_GAME_API void OnDisable() override;
+ SH_GAME_API void OnPropertyChanged(const core::reflection::Property& prop) override;
+
+ SH_GAME_API void SetCastShadow(bool bCastShadow);
+
+ /// @brief 그림자 아틀라스 텍스처를 반환한다 (모든 광원이 공유)
+ SH_GAME_API auto GetShadowMap() const -> render::RenderTexture*;
+ /// @brief 아틀라스 내에서 이 광원의 슬롯 정보를 반환한다.
+ SH_GAME_API auto GetShadowSlot() const -> render::ShadowMapManager::Slot;
+
+ virtual void SetIntensity(float intensity) = 0;
+ virtual auto GetIntensity() const -> float = 0;
+ virtual auto GetLightType() const -> Type = 0;
+ virtual auto GetLightSpaceMatrix() const -> glm::mat4 = 0;
+
+ virtual auto GetShadowBias() const -> float { return shadowBias; }
+ virtual auto GetShadowMapResolution() const -> uint32_t { return shadowMapResolution; }
+ virtual auto GetShadowViewMatrix() const -> glm::mat4 = 0;
+ virtual auto GetShadowProjMatrix() const -> glm::mat4 = 0;
+ virtual auto GetShadowPos() const -> glm::vec3 = 0;
+ virtual auto GetShadowLookAt() const -> glm::vec3 = 0;
+
+ void SetShadowBias(float bias) { shadowBias = bias; }
+ void SetShadowMapResolution(uint32_t res) { shadowMapResolution = res; }
+ auto IsCastShadow() const -> bool { return bCastShadow; }
+ auto GetShadowOrthoSize() const -> float { return shadowOrthoSize; }
+ auto GetShadowNearPlane() const -> float { return shadowNearPlane; }
+ auto GetShadowFarPlane() const -> float { return shadowFarPlane; }
+ protected:
+ SH_GAME_API void UpdateLightOctree();
+ SH_GAME_API void RegisterToShadowManager();
+ SH_GAME_API void UnregisterFromShadowManager();
+ private:
+ PROPERTY(shadowBias)
+ float shadowBias = 0.005f;
+ PROPERTY(shadowMapResolution)
+ uint32_t shadowMapResolution = 1024;
+ PROPERTY(shadowOrthoSize)
+ float shadowOrthoSize = 20.f;
+ PROPERTY(shadowNearPlane)
+ float shadowNearPlane = 0.1f;
+ PROPERTY(shadowFarPlane)
+ float shadowFarPlane = 100.f;
+ PROPERTY(bCastShadow)
+ bool bCastShadow = false;
+ bool bRegistered = false;
+ };
+}//namespace
\ No newline at end of file
diff --git a/include/Game/Component/Render/MeshRenderer.h b/include/Game/Component/Render/MeshRenderer.h
index 0f1ad437..661c4001 100644
--- a/include/Game/Component/Render/MeshRenderer.h
+++ b/include/Game/Component/Render/MeshRenderer.h
@@ -7,7 +7,6 @@
#include "Render/MaterialPropertyBlock.h"
#include "Render/Drawable.h"
-#include "Core/Util.h"
#include "Core/Observer.hpp"
#include "glm/mat4x4.hpp"
@@ -25,38 +24,40 @@ namespace sh::game
SH_GAME_API MeshRenderer(GameObject& owner);
SH_GAME_API ~MeshRenderer();
- SH_GAME_API void SetMesh(const render::Mesh* mesh);
- SH_GAME_API auto GetMesh() const -> const render::Mesh*;
-
- SH_GAME_API void SetMaterial(render::Material* mat);
- SH_GAME_API auto GetMaterial() const -> render::Material*;
-
SH_GAME_API void OnDestroy() override;
SH_GAME_API void Awake() override;
SH_GAME_API void Start() override;
/// @brief 렌더러에 이 타이밍에 drawable 객체를 넣는다.
SH_GAME_API void LateUpdate() override;
+ SH_GAME_API void Deserialize(const core::Json& json) override;
+ SH_GAME_API void OnPropertyChanged(const core::reflection::Property& prop) override;
+
+ SH_GAME_API void SetMesh(const render::Mesh* mesh);
+ SH_GAME_API auto GetMesh() const -> const render::Mesh* { return mesh; }
+
+ SH_GAME_API void SetMaterial(render::Material* mat);
+ SH_GAME_API void SetMaterial(std::size_t index, render::Material* mat);
+ SH_GAME_API auto GetMaterial(std::size_t index = 0) const -> render::Material*;
+ SH_GAME_API auto GetMaterialCount() const -> std::size_t;
SH_GAME_API void SetMaterialPropertyBlock(std::unique_ptr&& block);
- SH_GAME_API auto GetMaterialPropertyBlock() const -> render::MaterialPropertyBlock*;
+ SH_GAME_API void SetMaterialPropertyBlock(std::size_t index, std::unique_ptr&& block);
+ SH_GAME_API auto GetMaterialPropertyBlock(std::size_t index = 0) const -> render::MaterialPropertyBlock*;
/// @brief PropertyBlock의 데이터를 GPU에 업로드 하는 함수
SH_GAME_API void UpdatePropertyBlockData();
SH_GAME_API void SetRenderTagId(uint32_t tagId);
- SH_GAME_API auto GetRenderTagId() const -> uint32_t;
-
- SH_GAME_API void OnPropertyChanged(const core::reflection::Property& prop) override;
+ SH_GAME_API auto GetRenderTagId() const -> uint32_t { return renderTag; }
SH_GAME_API auto GetWorldAABB() const -> const render::AABB { return worldAABB; }
protected:
/// @brief Drawable을 생성하거나 이미 존재 시 갱신하는 함수.
- SH_GAME_API virtual void CreateDrawable();
+ SH_GAME_API virtual void CreateDrawable(bool bUseSubMesh = true);
/// @brief Update()마다 호출 되는 Drawable 설정 함수
SH_GAME_API virtual void UpdateDrawable();
+ SH_GAME_API void SearchLocalProperties();
+ SH_GAME_API void SetDefaultLocalProperties();
private:
- void SearchLocalProperties();
- void SetDefaultLocalProperties();
-
template
void SetData(const T& data, std::vector& uniformData, std::size_t offset, std::size_t size)
{
@@ -68,28 +69,29 @@ namespace sh::game
std::memcpy(uniformData.data() + offset, &data, sizeof(T));
}
- void FillLightStruct(render::Drawable& drawable, render::Shader& shader) const;
+ void FillLightStruct(render::Drawable& drawable, render::Shader& shader);
protected:
- PROPERTY(drawable, core::PropertyOption::invisible, core::PropertyOption::noSave)
- render::Drawable* drawable;
+ PROPERTY(drawables, core::PropertyOption::invisible, core::PropertyOption::noSave)
+ std::vector drawables;
+ PROPERTY(mesh)
+ const render::Mesh* mesh;
+ PROPERTY(mats)
+ std::vector mats;
private:
- struct Light
+ struct alignas(16) Light
{
- alignas(16) int lightCount = 0;
- alignas(16) glm::vec4 lightPos[10];
- alignas(16) glm::vec4 other[10];
+ glm::vec4 pos;
+ glm::vec4 other;
+ glm::vec4 shadow; // offset, size
+ glm::mat4 lightSpaceMatrix;
};
-
- PROPERTY(mesh)
- const render::Mesh* mesh;
- PROPERTY(mat)
- render::Material* mat;
-
+ std::vector lightDatas;
render::AABB worldAABB;
- std::unique_ptr propertyBlock;
+ std::vector> propertyBlocks;
- std::vector> localUniformLocations;
+ using LocalUniformLocations = std::vector>;
+ std::vector localUniformLocationsList;
core::Observer::Listener onMatrixUpdateListener;
core::Observer::Listener onShaderChangedListener;
@@ -97,4 +99,4 @@ namespace sh::game
PROPERTY(renderTag)
uint32_t renderTag = 1;
};
-}//namespace
\ No newline at end of file
+}//namespace
diff --git a/include/Game/Component/Render/PickingCamera.h b/include/Game/Component/Render/PickingCamera.h
index 0cae30da..5a2cd0e3 100644
--- a/include/Game/Component/Render/PickingCamera.h
+++ b/include/Game/Component/Render/PickingCamera.h
@@ -39,7 +39,7 @@ namespace sh::game
int x = 0, y = 0;
- uint8_t* pixels = nullptr;
+ std::vector pixels;
std::future> bufferFuture;
diff --git a/include/Game/Component/Render/PickingRenderer.h b/include/Game/Component/Render/PickingRenderer.h
index bbc4312d..d8e6e510 100644
--- a/include/Game/Component/Render/PickingRenderer.h
+++ b/include/Game/Component/Render/PickingRenderer.h
@@ -44,5 +44,7 @@ namespace sh::game
SH_GAME_API void SetMeshRenderer(const MeshRenderer& meshRenderer);
SH_GAME_API auto Serialize() const -> core::Json override;
+ protected:
+ void CreateDrawable(bool) override;
};
}//namespace
\ No newline at end of file
diff --git a/include/Game/Component/Render/PointLight.h b/include/Game/Component/Render/PointLight.h
index c0b3bb32..dbb311df 100644
--- a/include/Game/Component/Render/PointLight.h
+++ b/include/Game/Component/Render/PointLight.h
@@ -1,39 +1,45 @@
#pragma once
#include "Game/Export.h"
-#include "Game/ILight.h"
-#include "Game/Component/Component.h"
+#include "Game/Component/Render/LightBase.h"
namespace sh::game
{
- class PointLight : public Component, public ILight
+ class PointLight : public LightBase
{
COMPONENT(PointLight)
- private:
- PROPERTY(range)
- float range = 5.f;
- PROPERTY(intensity)
- float intensity = 1.f;
-
- Vec3 lastPos{ 0.f, 0.f, 0.f };
- bool bUpdateOctree = false;
public:
SH_GAME_API PointLight(GameObject& owner);
SH_GAME_API ~PointLight();
- SH_GAME_API void Destroy() override;
-
SH_GAME_API void BeginUpdate() override;
- SH_GAME_API bool Intersect(const render::AABB& aabb) const override;
+ SH_GAME_API auto Intersect(const render::AABB& aabb) const -> bool override;
SH_GAME_API void SetRadius(float radius);
- SH_GAME_API void SetIntensity(float intensity);
- SH_GAME_API auto GetRadius() const -> float;
- SH_GAME_API auto GetIntensity() const -> float;
+ /// @brief 라이트의 광원 공간 변환 행렬 (proj * view)을 반환한다.
+ SH_GAME_API auto GetLightSpaceMatrix() const -> glm::mat4 override;
+
+ SH_GAME_API auto GetShadowViewMatrix() const -> glm::mat4 override;
+ SH_GAME_API auto GetShadowProjMatrix() const -> glm::mat4 override;
+ SH_GAME_API auto GetShadowPos() const -> glm::vec3 override;
+ SH_GAME_API auto GetShadowLookAt() const -> glm::vec3 override;
+
SH_GAME_API auto GetPos() const -> const Vec3& override;
- SH_GAME_API auto GetLightType() const -> ILight::Type override;
-
+
SH_GAME_API void OnPropertyChanged(const core::reflection::Property& prop) override;
+
+ void SetIntensity(float intensity) override { this->intensity = intensity; }
+ auto GetIntensity() const -> float override { return intensity; }
+ auto GetLightType() const -> ILight::Type override { return ILight::Type::Point; }
+ auto GetRadius() const -> float { return range; }
+ private:
+ PROPERTY(range)
+ float range = 5.f;
+ PROPERTY(intensity)
+ float intensity = 1.f;
+
+ Vec3 lastPos{ 0.f, 0.f, 0.f };
+ bool bUpdateOctree = false;
};
}//namespace
\ No newline at end of file
diff --git a/include/Game/Component/Render/SSAOComponent.h b/include/Game/Component/Render/SSAOComponent.h
new file mode 100644
index 00000000..26a97036
--- /dev/null
+++ b/include/Game/Component/Render/SSAOComponent.h
@@ -0,0 +1,74 @@
+#pragma once
+#include "Game/Export.h"
+#include "Game/Component/Component.h"
+
+#include "Render/RenderData.h"
+
+#include
+namespace sh::render
+{
+ class Material;
+ class RenderTexture;
+ class Texture;
+}
+
+namespace sh::game
+{
+ class Camera;
+
+ /// @brief 카메라에 부착해 SSAO 를 활성화한다.
+ class SSAOComponent : public Component
+ {
+ COMPONENT(SSAOComponent)
+ public:
+ SH_GAME_API SSAOComponent(GameObject& owner);
+ SH_GAME_API ~SSAOComponent();
+
+ SH_GAME_API void Awake() override;
+ SH_GAME_API void OnDestroy() override;
+ SH_GAME_API void BeginUpdate() override;
+
+ SH_GAME_API void SetMaterial(render::Material* mat);
+
+ auto GetDepthRT() const -> render::RenderTexture* { return depthRT; }
+ auto GetAORT() const -> render::RenderTexture* { return aoRT; }
+ auto GetRadius() const -> float { return radius; }
+ auto GetBias() const -> float { return bias; }
+ auto GetPower() const -> float { return power; }
+ private:
+ void EnsureRenderTextures();
+ void UpdateMaterialUniforms();
+ static void CreateKernel();
+ private:
+ PROPERTY(mat, core::PropertyOption::sobjPtr, core::PropertyOption::invisible, core::PropertyOption::noSave)
+ render::Material* mat = nullptr;
+
+ PROPERTY(radius)
+ float radius = 0.5f;
+ PROPERTY(bias)
+ float bias = 0.025f;
+ PROPERTY(power)
+ float power = 1.5f;
+
+ PROPERTY(depthRT, core::PropertyOption::sobjPtr, core::PropertyOption::invisible, core::PropertyOption::noSave)
+ render::RenderTexture* depthRT = nullptr;
+ PROPERTY(normalRT, core::PropertyOption::sobjPtr, core::PropertyOption::invisible, core::PropertyOption::noSave)
+ render::RenderTexture* normalRT = nullptr;
+ PROPERTY(aoRT, core::PropertyOption::sobjPtr, core::PropertyOption::invisible, core::PropertyOption::noSave)
+ render::RenderTexture* aoRT = nullptr;
+ PROPERTY(camera, core::PropertyOption::sobjPtr, core::PropertyOption::invisible, core::PropertyOption::noSave);
+ Camera* camera = nullptr;
+ PROPERTY(noiseTex, core::PropertyOption::sobjPtr, core::PropertyOption::invisible, core::PropertyOption::noSave);
+ render::Texture* noiseTex = nullptr;
+
+ render::RenderData depthRenderData;
+ render::RenderData ssaoRenderData;
+ render::RenderData combineRenderData;
+
+ uint32_t cachedWidth = 0;
+ uint32_t cachedHeight = 0;
+
+ static std::array kernel;
+ static bool bKernelInit;
+ };
+}//namespace
diff --git a/include/Game/Component/Render/SkinnedMeshRenderer.h b/include/Game/Component/Render/SkinnedMeshRenderer.h
new file mode 100644
index 00000000..15bfdb0a
--- /dev/null
+++ b/include/Game/Component/Render/SkinnedMeshRenderer.h
@@ -0,0 +1,43 @@
+#pragma once
+#include "Game/Export.h"
+#include "Game/Component/Render/MeshRenderer.h"
+
+#include "Render/SkinnedMesh.h"
+
+#include
+#include
+#include
+
+namespace sh::game
+{
+ class Transform;
+
+ /// @brief 스켈레탈 메쉬 렌더러
+ class SkinnedMeshRenderer : public MeshRenderer
+ {
+ COMPONENT(SkinnedMeshRenderer)
+ public:
+ SH_GAME_API SkinnedMeshRenderer(GameObject& owner);
+ SH_GAME_API ~SkinnedMeshRenderer();
+
+ SH_GAME_API void Awake() override;
+ SH_GAME_API void OnPropertyChanged(const core::reflection::Property& prop) override;
+
+ SH_GAME_API void SetSkinnedMesh(render::SkinnedMesh* mesh);
+ SH_GAME_API void SetBones(std::vector bones);
+
+ SH_GAME_API auto GetBones() const -> const std::vector& { return bones; }
+ SH_GAME_API auto GetInverseBindMatrices() const -> const std::vector& { return inverseBindMatrices; }
+ protected:
+ SH_GAME_API void UpdateDrawable() override;
+ private:
+ void ComputeBoneMatrices();
+ void UploadBoneMatrices();
+ void InitIBM();
+ private:
+ PROPERTY(bones, core::PropertyOption::invisible)
+ std::vector bones;
+ std::vector inverseBindMatrices;
+ std::vector finalBoneMatrices;
+ };
+}//namespace
diff --git a/include/Game/Component/TestComputeShader.h b/include/Game/Component/TestComputeShader.h
new file mode 100644
index 00000000..6a299106
--- /dev/null
+++ b/include/Game/Component/TestComputeShader.h
@@ -0,0 +1,23 @@
+#pragma once
+#include "Export.h"
+#include "Game/Component/Component.h"
+
+namespace sh::render
+{
+ class ComputeShader;
+}
+
+namespace sh::game
+{
+ class TestComputeShader : public Component
+ {
+ COMPONENT(TestComputeShader)
+ public:
+ SH_GAME_API TestComputeShader(GameObject& owner);
+
+ SH_GAME_API void Update() override;
+ private:
+ PROPERTY(shader)
+ render::ComputeShader* shader = nullptr;
+ };
+}//namespace
diff --git a/include/Game/GUIPass.h b/include/Game/GUIPass.h
new file mode 100644
index 00000000..ccdd80d7
--- /dev/null
+++ b/include/Game/GUIPass.h
@@ -0,0 +1,27 @@
+#pragma once
+#include "Export.h"
+
+#include "Render/ScriptableRenderPass.h"
+
+#include
+#include
+namespace sh::game
+{
+ class ImGUImpl;
+
+ /// @brief ImGUI전용 패스
+ class GUIPass : public render::ScriptableRenderPass
+ {
+ public:
+ SH_GAME_API GUIPass();
+
+ SH_GAME_API void SetImGUIContext(ImGUImpl& gui);
+ protected:
+ SH_GAME_API void Configure(const render::RenderData& renderData) override;
+ SH_GAME_API void Record(render::CommandBuffer& cmd, const render::IRenderContext& ctx, const render::RenderData& renderData) override;
+ public:
+ const render::RenderTexture* viewportTexture = nullptr;
+ private:
+ ImGUImpl* gui = nullptr;
+ };
+}//namespace
\ No newline at end of file
diff --git a/include/Game/GameManager.h b/include/Game/GameManager.h
index 20c98896..0fb4b1ca 100644
--- a/include/Game/GameManager.h
+++ b/include/Game/GameManager.h
@@ -12,6 +12,8 @@
#include "Game/ImGUImpl.h"
#include "Game/ComponentLoader.h"
+#include "Render/RenderData.h"
+
#include
#include
#include
@@ -101,6 +103,8 @@ namespace sh::game
core::SVector defaultAssets;
+ render::RenderData guiRenderData;
+
bool bLoadingWorld = false;
};
}//namespace
\ No newline at end of file
diff --git a/include/Game/GameRenderer.h b/include/Game/GameRenderer.h
index c550bd1e..dd6a7ca8 100644
--- a/include/Game/GameRenderer.h
+++ b/include/Game/GameRenderer.h
@@ -3,25 +3,48 @@
#include "Render/ScriptableRenderer.h"
-#include "Component/Render/Camera.h"
-
#include
+#include
+namespace sh::render
+{
+ class DepthPass;
+ class OpaquePass;
+ class SSAOPass;
+ class CombinePass;
+ class TransparentPass;
+}
namespace sh::game
{
class ImGUImpl;
-
+ class World;
+ class GUIPass;
+
class GameRenderer : public render::ScriptableRenderer
{
public:
- SH_GAME_API GameRenderer(render::IRenderContext& ctx, game::ImGUImpl& guictx);
+ SH_GAME_API GameRenderer(render::IRenderContext& ctx, game::ImGUImpl& guictx, World& world);
- SH_GAME_API void Setup(const render::RenderTarget& data) override;
+ SH_GAME_API void Init() override;
+ SH_GAME_API void Setup(const render::RenderData& data) override;
- SH_GAME_API void SetUICamera(const render::Camera& camera);
- private:
- const render::Camera* uiCamera = nullptr;
+ auto GetDepthPass() const -> render::DepthPass& { return *depthPass; }
+ auto GetOpaquePass() const -> render::ScriptableRenderPass& { return *opaquePass; }
+ auto GetSSAOPass() const -> render::SSAOPass& { return *ssaoPass; }
+ auto GetCombinePass() const -> render::CombinePass& { return *combinePass; }
+ auto GetTransparentPass() const -> render::TransparentPass& { return *transparentPass; }
+ auto GetUIPass() const -> render::TransparentPass& { return *uiPass; }
+ auto GetGUIPass() const -> game::GUIPass& { return *guiPass; }
+ protected:
+ render::IRenderContext& renderCtx;
+ World& world;
+ game::ImGUImpl& guiCtx;
- std::unordered_map> allowedCamera;
- std::unordered_map> ignoreCamera;
+ render::DepthPass* depthPass = nullptr;
+ render::ScriptableRenderPass* opaquePass = nullptr;
+ render::SSAOPass* ssaoPass = nullptr;
+ render::CombinePass* combinePass = nullptr;
+ render::TransparentPass* transparentPass = nullptr;
+ render::TransparentPass* uiPass = nullptr;
+ game::GUIPass* guiPass = nullptr;
};
-}//namespace
\ No newline at end of file
+}//namespace
diff --git a/include/Game/ILight.h b/include/Game/ILight.h
index f0f102d2..91d39105 100644
--- a/include/Game/ILight.h
+++ b/include/Game/ILight.h
@@ -2,6 +2,8 @@
#include "Game/Export.h"
#include "IOctreeElement.h"
+#include
+
namespace sh::render
{
class Material;
@@ -19,7 +21,8 @@ namespace sh::game
public:
virtual ~ILight() {};
virtual void SetIntensity(float intensity) = 0;
+ virtual auto GetIntensity() const -> float = 0;
virtual auto GetLightType() const -> Type = 0;
- //virtual void SetMaterialData(render::Material& mat) = 0;
+ virtual auto GetLightSpaceMatrix() const -> glm::mat4 = 0;
};
-}//namespace
\ No newline at end of file
+}//namespace
diff --git a/include/Game/UICamera.h b/include/Game/UICamera.h
deleted file mode 100644
index 14ceb764..00000000
--- a/include/Game/UICamera.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-#include "Export.h"
-
-#include "Render/Camera.h"
-
-namespace sh::game
-{
- class UICamera : public render::Camera
- {
- public:
- SH_GAME_API UICamera();
- protected:
- SH_GAME_API void UpdateViewMatrix() override;
- SH_GAME_API void UpdateProjMatrix() override;
- };
-}//namespace
\ No newline at end of file
diff --git a/include/Game/UIPass.h b/include/Game/UIPass.h
deleted file mode 100644
index 66b9d709..00000000
--- a/include/Game/UIPass.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-#include "Export.h"
-
-#include "Render/ScriptableRenderPass.h"
-
-#include
-#include
-namespace sh::game
-{
- class ImGUImpl;
-
- class UIPass : public render::ScriptableRenderPass
- {
- public:
- SH_GAME_API UIPass();
-
- SH_GAME_API void SetImGUIContext(ImGUImpl& gui);
- protected:
- SH_GAME_API void Configure(const render::RenderTarget& renderData) override;
- SH_GAME_API auto BuildDrawList(const render::RenderTarget& renderData) -> render::DrawList override;
- public:
- render::RenderTexture* viewportTexture = nullptr;
- private:
- ImGUImpl* gui = nullptr;
- };
-}//namespace
\ No newline at end of file
diff --git a/include/Game/World.h b/include/Game/World.h
index a070ff64..1d9fb7d6 100644
--- a/include/Game/World.h
+++ b/include/Game/World.h
@@ -2,9 +2,7 @@
#include "Export.h"
#include "ComponentModule.h"
#include "Octree.h"
-#include "UICamera.h"
#include "GameObject.h"
-#include "Component/Render/Camera.h"
#include "Core/NonCopyable.h"
#include "Core/SObject.h"
@@ -39,12 +37,14 @@ namespace sh::render
{
class Renderer;
class ScriptableRenderer;
+ class ShadowMapManager;
}
namespace sh::game
{
class Component;
class ImGUImpl;
+ class Camera;
class World : public sh::core::SObject, public sh::core::INonCopyable
{
@@ -58,6 +58,8 @@ namespace sh::game
SH_GAME_API virtual ~World();
SH_GAME_API void OnDestroy() override;
+ SH_GAME_API auto Serialize() const -> core::Json override;
+ SH_GAME_API void Deserialize(const core::Json& json) override;
SH_GAME_API virtual void Clear();
@@ -74,20 +76,13 @@ namespace sh::game
/// @param name 이름
/// @return 못 찾을 시 nullptr, 찾을 시 게임 오브젝트 포인터
SH_GAME_API auto GetGameObject(std::string_view name) const -> GameObject*;
- SH_GAME_API auto GetGameObjects() const -> const std::vector& { return objs; }
- SH_GAME_API auto GetGameObjectPool() -> core::memory::MemoryPool&;
/// @brief 게임 오브젝트가 할당된 메모리를 반환 큐에 넣는 함수.
/// @param ptr 오브젝트 포인터
SH_GAME_API void PushDeAllocatedGameObject(GameObject* ptr);
- SH_GAME_API void RegisterCamera(Camera* cam);
- SH_GAME_API void UnRegisterCamera(Camera* cam);
+ SH_GAME_API void RegisterCamera(Camera& cam);
+ SH_GAME_API void UnRegisterCamera(Camera& cam);
SH_GAME_API void SetMainCamera(Camera* cam);
- SH_GAME_API auto GetMainCamera() const -> Camera*;
-
- SH_GAME_API auto GetPhysWorld() -> phys::PhysWorld*;
- SH_GAME_API auto GetLightOctree() -> Octree&;
- SH_GAME_API auto GetLightOctree() const -> const Octree&;
SH_GAME_API virtual void Start();
SH_GAME_API virtual void Update(double deltaTime);
@@ -100,41 +95,39 @@ namespace sh::game
/// @param func 함수
SH_GAME_API void AddAfterSyncTask(const std::function& func);
- SH_GAME_API auto Serialize() const->core::Json override;
- SH_GAME_API void Deserialize(const core::Json& json) override;
-
SH_GAME_API void SaveWorldPoint(const core::Json& json, std::string_view name = "default");
SH_GAME_API void SaveWorldPoint(core::Json&& json, std::string_view name = "default");
-
SH_GAME_API void LoadWorldPoint();
SH_GAME_API void LoadWorldPoint(const std::string& name);
-
SH_GAME_API auto GetWorldPoint() const -> const core::Json*;
SH_GAME_API auto GetWorldPoint(const std::string& name) const -> const core::Json*;
SH_GAME_API auto GetWorldPoint() -> core::Json*;
SH_GAME_API auto GetWorldPoint(const std::string& name) -> core::Json*;
-
SH_GAME_API void ClearWorldPoints();
SH_GAME_API void ClearWorldPoint(const std::string& name = "default");
- SH_GAME_API auto GetUiContext() const -> ImGUImpl&;
-
SH_GAME_API void PublishEvent(const core::IEvent& event);
SH_GAME_API void SubscribeEvent(core::ISubscriber& subscriber);
SH_GAME_API void Play();
SH_GAME_API void Stop();
- SH_GAME_API auto GetCameras() const -> const core::SVector& { return cameras; }
- SH_GAME_API auto IsPlaying() const -> bool;
- SH_GAME_API auto IsStart() const -> bool;
- SH_GAME_API auto IsLoaded() const -> bool;
- SH_GAME_API auto GetUICamera() -> UICamera& { return uiCamera; }
- SH_GAME_API auto GetUICamera() const -> const UICamera& { return uiCamera; }
-
SH_GAME_API virtual void ReallocateUUIDS();
- SH_GAME_API auto GetFixedAccumulator() const -> double { return dtAccumulator; }
+ auto GetUiContext() const -> ImGUImpl& { return *imgui; }
+ auto GetPhysWorld() -> phys::PhysWorld& { return physWorld; }
+ auto GetLightOctree() -> Octree& { return lightOctree; }
+ auto GetLightOctree() const -> const Octree& { return lightOctree; }
+ auto GetMainCamera() const -> Camera* { return mainCamera; }
+ auto GetShadowMapManager() -> render::ShadowMapManager& { return *shadowMapManager; }
+ auto GetShadowMapManager() const -> const render::ShadowMapManager& { return *shadowMapManager; }
+ auto GetGameObjects() const -> const std::vector& { return objs; }
+ auto GetGameObjectPool() -> core::memory::MemoryPool& { return objPool; }
+ auto GetCameras() const -> const std::vector& { return cameras; }
+ auto IsPlaying() const -> bool { return bPlaying; }
+ auto IsStart() const -> bool { return bOnStart; }
+ auto IsLoaded() const -> bool { return bLoaded; }
+ auto GetFixedAccumulator() const -> double { return dtAccumulator; }
protected:
SH_GAME_API void CleanObjs();
private:
@@ -148,9 +141,10 @@ namespace sh::game
core::EventBus eventBus;
std::unordered_map objIdx;
std::vector objs;
- core::SVector cameras;
+ std::vector cameras;
std::unique_ptr customRenderer;
+ std::unique_ptr shadowMapManager;
private:
core::GarbageCollection* gc;
ImGUImpl* imgui = nullptr;
@@ -162,7 +156,7 @@ namespace sh::game
double dt = 0.0;
double dtAccumulator = 0.0;
- PROPERTY(mainCamera)
+ PROPERTY(mainCamera, core::PropertyOption::sobjPtr)
Camera* mainCamera = nullptr;
phys::PhysWorld physWorld;
@@ -175,8 +169,6 @@ namespace sh::game
std::unordered_map savePoints;
- UICamera uiCamera;
-
core::EventSubscriber physEventSubscriber;
bool bStartLoop = false;
@@ -185,4 +177,4 @@ namespace sh::game
bool bLoaded = false;
bool bOnStart = false;
};
-}
\ No newline at end of file
+}//namespace
\ No newline at end of file
diff --git a/include/Render/BufferFactory.h b/include/Render/BufferFactory.h
index 781a4e6b..b03917e3 100644
--- a/include/Render/BufferFactory.h
+++ b/include/Render/BufferFactory.h
@@ -8,8 +8,9 @@
namespace sh::render
{
class IBuffer;
- class IUniformBuffer;
+ class IShaderBinding;
class IRenderContext;
+ class ComputeShader;
namespace vk
{
class VulkanContext;
@@ -17,10 +18,20 @@ namespace sh::render
class BufferFactory
{
- private:
- static auto CreateVkUniformBuffer(const vk::VulkanContext& context, std::size_t size, bool bTransferDst) -> std::unique_ptr;
public:
- SH_RENDER_API static auto Create(const IRenderContext& context, std::size_t size, bool bTransferDst = false) -> std::unique_ptr;
- SH_RENDER_API static auto CreateUniformBuffer(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Type type) -> std::unique_ptr;
+ struct CreateInfo
+ {
+ std::size_t size = 0;
+ bool bDynamic = false; // SSBO
+ bool bGPUOnly = false;
+ };
+ public:
+ SH_RENDER_API static auto Create(const IRenderContext& context, const CreateInfo& info) -> std::unique_ptr;
+ SH_RENDER_API static auto CreateShaderBinding(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Usage usage) -> std::unique_ptr;
+ SH_RENDER_API static auto CreateShaderBinding(const IRenderContext& context, const ComputeShader& shader) -> std::unique_ptr;
+
+ SH_RENDER_API static auto GetBufferAlignment(const IRenderContext& context) -> std::size_t;
+ private:
+ static auto CreateVkUniformBuffer(const vk::VulkanContext& context, const CreateInfo& info) -> std::unique_ptr;
};
}//namespace
\ No newline at end of file
diff --git a/include/Render/Camera.h b/include/Render/Camera.h
deleted file mode 100644
index ea1148c9..00000000
--- a/include/Render/Camera.h
+++ /dev/null
@@ -1,108 +0,0 @@
-#pragma once
-#include "Export.h"
-#include "IRenderContext.h"
-#include "RenderTexture.h"
-
-#include "Core/ISyncable.h"
-
-#include
-
-#include
-
-#ifdef near
-#undef near
-#endif
-#ifdef far
-#undef far
-#endif
-
-namespace sh::render
-{
- /// @brief 렌더러용 카메라.
- class Camera : public core::ISyncable
- {
- public:
- SH_RENDER_API Camera();
-
- SH_RENDER_API void SetRenderTexture(RenderTexture* framebuffer);
- SH_RENDER_API auto GetRenderTexture() const -> RenderTexture*;
-
- SH_RENDER_API void SetPriority(int priority);
- SH_RENDER_API auto GetPriority() const -> int;
-
- SH_RENDER_API auto GetProjMatrix(core::ThreadType thr) const -> const glm::mat4&;
- SH_RENDER_API auto GetViewMatrix(core::ThreadType thr) const -> const glm::mat4&;
-
- SH_RENDER_API void SetPos(const glm::vec3& pos);
- SH_RENDER_API void SetLookPos(const glm::vec3& pos);
- SH_RENDER_API void SetUpVector(const glm::vec3& up);
- SH_RENDER_API void SetFovRadian(float rad);
- SH_RENDER_API void SetFov(float degree);
- SH_RENDER_API void SetNearPlane(float near);
- SH_RENDER_API void SetFarPlane(float far);
- SH_RENDER_API void SetWidth(float width);
- SH_RENDER_API void SetHeight(float height);
- SH_RENDER_API void SetOrthographic(bool bEnable);
- SH_RENDER_API void SetCustomProjectionMatrix(const glm::mat4* matrix);
-
- SH_RENDER_API void SetRenderTagMask(uint32_t mask);
- SH_RENDER_API void AddRenderTagMask(uint32_t tagId);
- SH_RENDER_API void RemoveRenderTagMask(uint32_t tagId);
- SH_RENDER_API auto GetRenderTagMask() const -> uint32_t;
- /// @brief 렌더 태그가 mask에 통과 하는지 검증하는 함수.
- /// @param tagId 태그 id
- /// @return 통과 하면 true 아니면 false
- SH_RENDER_API auto CheckRenderTag(uint32_t tagId) const -> bool;
-
- SH_RENDER_API void SetActive(bool bActive);
- SH_RENDER_API auto GetActive() const -> bool;
-
- SH_RENDER_API auto GetPos(core::ThreadType thr) const -> const glm::vec3& { return pos[thr]; }
- SH_RENDER_API auto GetLookPos(core::ThreadType thr) const -> const glm::vec3& { return to[thr]; }
- SH_RENDER_API auto GetUpVector(core::ThreadType thr) const -> const glm::vec3& { return up[thr]; }
- SH_RENDER_API auto GetFovRadian(core::ThreadType thr) const -> float { return fovRadians[thr]; }
- SH_RENDER_API auto GetNearPlane(core::ThreadType thr) const -> float { return nearPlane[thr]; }
- SH_RENDER_API auto GetFarPlane(core::ThreadType thr) const -> float { return farPlane[thr]; }
- SH_RENDER_API auto GetWidth(core::ThreadType thr) const -> float { return width[thr]; }
- SH_RENDER_API auto GetHeight(core::ThreadType thr) const -> float { return height[thr]; }
-
- SH_RENDER_API auto IsOrtho() const -> bool { return !bPerspective; }
- protected:
- SH_RENDER_API void SyncDirty() override;
- SH_RENDER_API void Sync() override;
- /// @brief dirtyMask |= 256; 후 SyncDirty호출 필요
- SH_RENDER_API virtual void UpdateProjMatrix();
- /// @brief dirtyMask |= 256; 후 SyncDirty호출 필요
- SH_RENDER_API virtual void UpdateViewMatrix();
- public:
- struct alignas(16) BufferData
- {
- glm::mat4 matView;
- glm::mat4 matProj;
- };
- const uint32_t id;
- protected:
- core::SyncArray pos;
- core::SyncArray to;
- core::SyncArray up;
-
- core::SyncArray fovRadians = { 0.0f, 0.f };
- core::SyncArray nearPlane = { 0.1f, 0.1f };
- core::SyncArray farPlane = { 1000.f, 1000.f };
- core::SyncArray width = { 1.f, 1.f };
- core::SyncArray height = { 1.f, 1.f };
- core::SyncArray bufferData;
-
- uint32_t dirtyMask = 0;
- private:
- SH_RENDER_API static uint32_t nextId;
- RenderTexture* renderTexture;
-
- int priority;
- uint32_t renderTagMask = 1;
-
- bool bActive = true;
- bool bDirty = false;
- bool bPerspective = true;
- };
-} //namespace
\ No newline at end of file
diff --git a/include/Render/CommandBuffer.h b/include/Render/CommandBuffer.h
index 039e12cc..3a5d0497 100644
--- a/include/Render/CommandBuffer.h
+++ b/include/Render/CommandBuffer.h
@@ -1,20 +1,46 @@
#pragma once
-
#include "Core/NonCopyable.h"
+#include "Core/Name.h"
+
+#include "Render/IRenderThrMethod.h"
#include
namespace sh::render
{
+ class ComputeShader;
class RenderTexture;
class IBuffer;
+ class Material;
+ class Drawable;
+ class ShaderPass;
+ class RenderData;
+ struct BarrierInfo;
class CommandBuffer : public core::INonCopyable
{
+ friend struct IRenderThrMethod;
public:
virtual ~CommandBuffer() = default;
virtual void Begin(bool bOnetime) = 0;
- virtual void Blit(RenderTexture& src, int x, int y, IBuffer& dst) = 0;
virtual void End() = 0;
+ virtual void Reset() = 0;
+
+ virtual void Blit(RenderTexture& src, int x, int y, IBuffer& dst) = 0;
+ virtual void Dispatch(const ComputeShader& shader, uint32_t x, uint32_t y, uint32_t z) = 0;
+ virtual void SetRenderData(const RenderData& renderData, bool bClearColor = true, bool bClearDepth = true, bool bStoreColor = false, bool bStoreDepth = false) = 0;
+ virtual void SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) = 0;
+ virtual void SetScissor(uint32_t x, uint32_t y, uint32_t width, uint32_t height) = 0;
+ virtual void DrawMeshBatch(const std::vector& drawables, core::Name passName, std::size_t viewerIdx = 0) = 0;
+ virtual void DrawMesh(const Drawable& drawable, core::Name passName, std::size_t viewerIdx = 0) = 0;
+ virtual void EmitBarrier(const std::vector& barriers) = 0;
+ protected:
+ virtual auto GetRenderCall() const -> uint32_t = 0;
+ };
+
+ template<>
+ struct IRenderThrMethod
+ {
+ static auto GetRenderCall(const CommandBuffer& cmd) -> uint32_t { return cmd.GetRenderCall(); }
};
}//namespace
\ No newline at end of file
diff --git a/include/Render/ComputeShader.h b/include/Render/ComputeShader.h
new file mode 100644
index 00000000..727a9cad
--- /dev/null
+++ b/include/Render/ComputeShader.h
@@ -0,0 +1,56 @@
+#pragma once
+#include "Export.h"
+#include "ShaderAST.h"
+#include "IBuffer.h"
+#include "IShaderBinding.h"
+
+#include "Core/SObject.h"
+#include "Core/Name.h"
+#include "Core/Reflection.hpp"
+
+#include
+#include
+#include
+namespace sh::render
+{
+ struct ComputeShaderCreateInfo;
+
+ class IRenderContext;
+ class ComputeShader : public core::SObject
+ {
+ SCLASS(ComputeShader)
+ public:
+ SH_RENDER_API ComputeShader(const IRenderContext& ctx, ComputeShaderCreateInfo createInfo);
+ SH_RENDER_API ~ComputeShader();
+
+ /// @brief AST와 SPIR-V 바이너리를 직렬화 한다.
+ /// @return 직렬화 된 json
+ SH_RENDER_API auto Serialize() const -> core::Json override;
+ /// @brief AST와 SPIR-V 바이너리를 역직렬화 한다.
+ /// @param json 직렬화 된 json
+ SH_RENDER_API void Deserialize(const core::Json& json) override;
+
+ SH_RENDER_API void SetFloats(const std::string& name, float* values, std::size_t count);
+
+ SH_RENDER_API auto GetSpirv() const -> const std::vector& { return spirv; }
+ SH_RENDER_API auto GetShaderAST() const -> const ShaderAST::ComputeShaderNode& { return shaderNode; }
+
+ SH_RENDER_API auto GetNumthreadsX() const -> uint32_t { return shaderNode.numthreadsX; }
+ SH_RENDER_API auto GetNumthreadsY() const -> uint32_t { return shaderNode.numthreadsY; }
+ SH_RENDER_API auto GetNumthreadsZ() const -> uint32_t { return shaderNode.numthreadsZ; }
+ SH_RENDER_API auto GetNode() const -> const ShaderAST::ComputeShaderNode& { return shaderNode; }
+ SH_RENDER_API auto GetBuffers() const -> const std::vector>& { return buffers; }
+ SH_RENDER_API auto GetShaderBinding() const -> IShaderBinding* { return shaderBinding.get(); }
+ SH_RENDER_API auto GetBuffer(const std::string& name) -> IBuffer*;
+ private:
+ auto GetBinding(const std::string& name) -> std::optional;
+ private:
+ const IRenderContext& ctx;
+
+ ShaderAST::ComputeShaderNode shaderNode;
+ std::vector spirv;
+
+ std::vector> buffers; // binding, buffer
+ std::unique_ptr shaderBinding;
+ };
+}//namespace
diff --git a/include/Render/ComputeShaderCreateInfo.h b/include/Render/ComputeShaderCreateInfo.h
new file mode 100644
index 00000000..bfeac71f
--- /dev/null
+++ b/include/Render/ComputeShaderCreateInfo.h
@@ -0,0 +1,12 @@
+#pragma once
+#include "ShaderAST.h"
+
+#include
+namespace sh::render
+{
+ struct ComputeShaderCreateInfo
+ {
+ ShaderAST::ComputeShaderNode shaderNode;
+ std::vector spirv;
+ };
+}//namespace
diff --git a/include/Render/ComputeShaderLexer.h b/include/Render/ComputeShaderLexer.h
new file mode 100644
index 00000000..3ae2d5cd
--- /dev/null
+++ b/include/Render/ComputeShaderLexer.h
@@ -0,0 +1,51 @@
+#pragma once
+#include "Export.h"
+
+#include
+#include
+#include
+namespace sh::render
+{
+ class ComputeShaderLexer
+ {
+ public:
+ enum class TokenType
+ {
+ Preprocessor,
+ Numthreads,
+ RBuffer, WBuffer, RWBuffer,
+ Const,
+ LBracket, // (
+ RBracket, // )
+ LBrace, // {
+ RBrace, // }
+ LSquareBracket, // [
+ RSquareBracket, // ]
+ Comma, // ,
+ String, Identifier, Literal, Number, Operator, Semicolon,
+ EndOfFile,
+ Unknown
+ };
+ struct Token
+ {
+ TokenType type;
+ std::string text;
+ int line;
+ int column;
+ };
+ public:
+ SH_RENDER_API static auto Lex(const std::string& source) -> std::vector;
+ private:
+ static auto IsIdentifierChar(char c) -> bool;
+ };
+
+ class ComputeShaderLexerException : public std::exception
+ {
+ public:
+ ComputeShaderLexerException(const std::string& err) : err(err) {}
+
+ auto what() const noexcept -> const char* override { return err.c_str(); }
+ private:
+ std::string err;
+ };
+}//namespace
diff --git a/include/Render/ComputeShaderParser.h b/include/Render/ComputeShaderParser.h
new file mode 100644
index 00000000..0b608a36
--- /dev/null
+++ b/include/Render/ComputeShaderParser.h
@@ -0,0 +1,56 @@
+#pragma once
+#include "Export.h"
+#include "ShaderAST.h"
+#include "ComputeShaderLexer.h"
+
+#include
+#include
+#include
+#include
+
+namespace sh::render
+{
+ class ComputeShaderParser
+ {
+ public:
+ SH_RENDER_API auto Parse(const std::vector& tokens) -> ShaderAST::ComputeShaderNode;
+ protected:
+ /// @brief 현재 토큰을 반환하는 함수.
+ auto PeekToken() const -> const ComputeShaderLexer::Token&;
+ /// @brief 현재 토큰 타입 검사 함수.
+ auto CheckToken(ComputeShaderLexer::TokenType token) const -> bool;
+ /// @brief 다음 토큰으로 이동하는 함수.
+ auto NextToken() -> const ComputeShaderLexer::Token&;
+ /// @brief 직전에 소모된 토큰을 반환하는 함수.
+ auto PreviousToken() -> const ComputeShaderLexer::Token&;
+ /// @brief 해당 토큰이 존재하면 다음 토큰으로 이동하며 없다면 예외를 던진다.
+ void ConsumeToken(ComputeShaderLexer::TokenType token);
+ /// @brief 해당 토큰들 중 하나라도 존재하면 다음 토큰으로 이동하며 없다면 예외를 던진다.
+ void ConsumeToken(const std::initializer_list& tokens);
+
+ auto GetCurrentTokenPosString() const -> std::string;
+ auto GetTokenErrorString(const std::string& msg) const -> std::string;
+
+ auto ParseComputeShader() -> ShaderAST::ComputeShaderNode;
+ void ParsePreprocessor(ShaderAST::ComputeShaderNode& node);
+ void ParseNumthreads(ShaderAST::ComputeShaderNode& node);
+ void ParseBuffer(ShaderAST::ComputeShaderNode& node, ShaderAST::BufferAccess access);
+ void ParseDeclaration(ShaderAST::ComputeShaderNode& node, const std::string& qualifier = "");
+ auto ParseFunctionBody() -> std::string;
+
+ static auto IdentifierToVariableType(const ComputeShaderLexer::Token& token) -> ShaderAST::VariableType;
+ static auto VariableTypeToString(ShaderAST::VariableType type) -> std::string;
+ static void GenerateCode(ShaderAST::ComputeShaderNode& node);
+ private:
+ const std::vector* tokens = nullptr;
+ std::size_t pos = 0;
+ uint32_t lastBinding = 0;
+ };
+
+ class ComputeShaderParserException : public std::runtime_error
+ {
+ public:
+ ComputeShaderParserException(const char* err) : std::runtime_error{ err }
+ {}
+ };
+}//namespace
diff --git a/include/Render/Drawable.h b/include/Render/Drawable.h
index a00729a9..72d5374d 100644
--- a/include/Render/Drawable.h
+++ b/include/Render/Drawable.h
@@ -30,25 +30,24 @@ namespace sh::render
/// @param mesh 메쉬
SH_RENDER_API void SetMesh(const Mesh& mesh);
SH_RENDER_API void SetMaterial(const Material& mat);
-
- SH_RENDER_API auto GetMesh() const -> const Mesh*;
- SH_RENDER_API auto GetMaterial() const -> const Material*;
- SH_RENDER_API auto GetMaterialData() const -> const MaterialData&;
- SH_RENDER_API auto GetMaterialData() -> MaterialData&;
-
SH_RENDER_API void SetModelMatrix(const glm::mat4& mat);
- SH_RENDER_API auto GetModelMatrix(core::ThreadType thr) const -> const glm::mat4&;
-
- SH_RENDER_API auto CheckAssetValid() const -> bool;
-
SH_RENDER_API void SetRenderTagId(uint32_t tagId);
- SH_RENDER_API auto GetRenderTagId() const -> uint32_t;
-
SH_RENDER_API void SetTopology(Mesh::Topology topology);
- SH_RENDER_API auto GetTopology(core::ThreadType thr = core::ThreadType::Game) const -> Mesh::Topology;
-
SH_RENDER_API void SetPriority(int priority);
- SH_RENDER_API auto GetPriority(core::ThreadType thr = core::ThreadType::Game) const -> int;
+ SH_RENDER_API void SetSubMeshIndex(uint32_t idx);
+
+ SH_RENDER_API auto CheckAssetValid() const -> bool;
+
+ SH_RENDER_API auto GetMesh() const -> const Mesh* { return mesh; }
+ SH_RENDER_API auto GetMaterial() const -> const Material* { return mat; }
+ SH_RENDER_API auto GetMaterialData() const -> const MaterialData& { return materialData; }
+ SH_RENDER_API auto GetMaterialData() -> MaterialData& { return materialData; }
+ SH_RENDER_API auto GetModelMatrix(core::ThreadType thr) const -> const glm::mat4& { return modelMatrix[thr]; }
+ SH_RENDER_API auto GetRenderTagId() const -> uint32_t { return renderTag; }
+ SH_RENDER_API auto GetTopology(core::ThreadType thr = core::ThreadType::Game) const -> Mesh::Topology { return topology[thr]; }
+ SH_RENDER_API auto GetPriority(core::ThreadType thr = core::ThreadType::Game) const -> int { return priority[thr]; }
+ SH_RENDER_API auto GetSubMeshIndex() const -> uint32_t { return subMeshIndex; }
+ SH_RENDER_API auto IsSkinnedMesh() const -> bool { return bSkinned; }
protected:
SH_RENDER_API void SyncDirty() override;
SH_RENDER_API void Sync() override;
@@ -73,6 +72,9 @@ namespace sh::render
};
std::array syncDatas;
+ uint32_t subMeshIndex = 0;
+
+ bool bSkinned = false;
bool bDirty = false;
bool bMatrixDirty = false;
};
diff --git a/include/Render/Formats.hpp b/include/Render/Formats.hpp
index 8be04fb1..ea1b82c8 100644
--- a/include/Render/Formats.hpp
+++ b/include/Render/Formats.hpp
@@ -1,5 +1,8 @@
#pragma once
+#include
+#include
+
namespace sh::render
{
enum class TextureFormat
@@ -10,22 +13,73 @@ namespace sh::render
SBGR24,
SBGRA32,
RGB24,
+ RG32F,
RGBA32,
BGR24,
BGRA32,
R8,
D32S8,
D24S8,
- D16S8
+ D16S8,
+ D32,
+ D16
};
- static auto GetTextureFormatChannel(TextureFormat format) -> int
+ inline static auto GetTextureFormatChannel(TextureFormat format) -> int
{
switch (format)
{
+ case TextureFormat::RG32F:
+ return 2;
case TextureFormat::R8:
return 1;
}
return 4;
}
-}//namespace
\ No newline at end of file
+
+ inline static auto GetTextureFormatPixelSize(TextureFormat format) -> std::size_t
+ {
+ switch (format)
+ {
+ case TextureFormat::RG32F:
+ return sizeof(uint16_t) * 2;
+ case TextureFormat::R8:
+ return 1;
+ default:
+ return 4;
+ }
+ }
+
+ inline static auto IsDepthTexture(TextureFormat format) -> bool
+ {
+ if (format == TextureFormat::D32S8 ||
+ format == TextureFormat::D24S8 ||
+ format == TextureFormat::D16S8 ||
+ format == TextureFormat::D32 ||
+ format == TextureFormat::D16)
+ return true;
+ return false;
+ }
+
+ inline static auto TextureFormatToString(TextureFormat format) -> const char*
+ {
+ switch (format)
+ {
+ case TextureFormat::None: return "None";
+ case TextureFormat::SRGB24: return "SRGB24";
+ case TextureFormat::SRGBA32: return "SRGBA32";
+ case TextureFormat::SBGR24: return "SBGR24";
+ case TextureFormat::SBGRA32: return "SBGRA32";
+ case TextureFormat::RGB24: return "RGB24";
+ case TextureFormat::RG32F: return "RG32F";
+ case TextureFormat::RGBA32: return "RGBA32";
+ case TextureFormat::R8: return "R8";
+ case TextureFormat::D32S8: return "D32S8";
+ case TextureFormat::D24S8: return "D24S8";
+ case TextureFormat::D16S8: return "D16S8";
+ case TextureFormat::D32: return "D32";
+ case TextureFormat::D16: return "D16";
+ default: return "None";
+ }
+ }
+}//namespace
diff --git a/include/Render/IBuffer.h b/include/Render/IBuffer.h
index 36ff0847..f4626993 100644
--- a/include/Render/IBuffer.h
+++ b/include/Render/IBuffer.h
@@ -1,5 +1,6 @@
#pragma once
+#include
namespace sh::render
{
class IBuffer
@@ -8,7 +9,9 @@ namespace sh::render
virtual ~IBuffer() = default;
virtual void SetData(const void* data) = 0;
- virtual auto GetData() const -> void* = 0;
+ virtual void SetData(const void* data, std::size_t offset, std::size_t size) = 0;
+ virtual auto GetData() const -> std::vector = 0;
virtual auto GetSize() const -> size_t = 0;
+ virtual auto Resize(std::size_t size) -> bool = 0;
};
}//namespace
\ No newline at end of file
diff --git a/include/Render/IRenderContext.h b/include/Render/IRenderContext.h
index ae294e41..662ddfca 100644
--- a/include/Render/IRenderContext.h
+++ b/include/Render/IRenderContext.h
@@ -1,7 +1,6 @@
#pragma once
#include "Export.h"
#include "CommandBuffer.h"
-#include "IRenderImpl.h"
#include "Core/NonCopyable.h"
@@ -9,6 +8,8 @@
namespace sh::render
{
+ class RenderDataManager;
+
enum class RenderAPI
{
OpenGL,
@@ -23,12 +24,15 @@ namespace sh::render
virtual void Clear() = 0;
virtual auto GetRenderAPIType() const -> RenderAPI = 0;
- virtual auto AllocateCommandBuffer() -> CommandBuffer* = 0;
+ virtual auto AllocateCommandBuffer(bool bCompute) -> CommandBuffer* = 0;
virtual void DeallocateCommandBuffer(CommandBuffer& cmd) = 0;
+ virtual void SubmitCommand(CommandBuffer& cmd) = 0;
virtual void SetViewport(const glm::vec2& start, const glm::vec2& end) = 0;
virtual auto GetViewportStart() const -> const glm::vec2& = 0;
virtual auto GetViewportEnd() const -> const glm::vec2& = 0;
- virtual auto GetRenderImpl() const -> IRenderImpl& = 0;
+
+ virtual auto GetRenderDataManager() const -> const RenderDataManager& = 0;
+ virtual auto GetRenderDataManager() -> RenderDataManager& = 0;
};
}//namespace
\ No newline at end of file
diff --git a/include/Render/IRenderImpl.h b/include/Render/IRenderImpl.h
deleted file mode 100644
index 5d06543c..00000000
--- a/include/Render/IRenderImpl.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-#include "RenderData.h"
-#include "CommandBuffer.h"
-#include "Core/Name.h"
-
-namespace sh::render
-{
- class IRenderImpl
- {
- public:
- virtual ~IRenderImpl() = default;
-
- virtual void EmitBarrier(CommandBuffer& cmd, const std::vector& barriers) const = 0;
- virtual void RecordCommand(CommandBuffer& cmd, const core::Name& passName, const RenderTarget& renderData, const DrawList& drawList, bool bStoreImage) const = 0;
- };
-}//namespace
\ No newline at end of file
diff --git a/include/Render/IUniformBuffer.h b/include/Render/IShaderBinding.h
similarity index 63%
rename from include/Render/IUniformBuffer.h
rename to include/Render/IShaderBinding.h
index c34af835..46f17cd8 100644
--- a/include/Render/IUniformBuffer.h
+++ b/include/Render/IShaderBinding.h
@@ -9,17 +9,19 @@ namespace sh::render
{
class IRenderContext;
class ShaderPass;
+ class ComputeShader;
class Texture;
class IBuffer;
- class IUniformBuffer : public core::INonCopyable
+ class IShaderBinding : public core::INonCopyable
{
public:
- SH_RENDER_API virtual ~IUniformBuffer() = default;
+ SH_RENDER_API virtual ~IShaderBinding() = default;
- SH_RENDER_API virtual void Create(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Type type) = 0;
+ SH_RENDER_API virtual void Create(const IRenderContext& context, const ShaderPass& shader, UniformStructLayout::Usage usage) = 0;
+ SH_RENDER_API virtual void Create(const IRenderContext& context, const ComputeShader& shader) = 0;
SH_RENDER_API virtual void Clear() = 0;
SH_RENDER_API virtual void Link(uint32_t binding, const IBuffer& buffer, std::size_t bufferSize = 0) = 0;
SH_RENDER_API virtual void Link(uint32_t binding, const Texture& texture) = 0;
};
-}//namespace
\ No newline at end of file
+}//namespace
diff --git a/include/Render/Material.h b/include/Render/Material.h
index 4da6483f..7b3bfc05 100644
--- a/include/Render/Material.h
+++ b/include/Render/Material.h
@@ -25,6 +25,8 @@
#include
#include
#include
+#include
+#include
namespace sh::render
{
@@ -53,6 +55,12 @@ namespace sh::render
template
void SetProperty(const std::string& name, const T& data);
+ template
+ void SetProperty(const std::string& name, const std::vector& data);
+ template
+ void SetProperty(const std::string& name, const std::array& data);
+ template
+ void SetPropertyArray(const std::string& name, const T* data, std::size_t count);
SH_RENDER_API void SetProperty(const std::string& name, const glm::vec4& data);
SH_RENDER_API void SetProperty(const std::string& name, const Texture* data);
SH_RENDER_API void SetProperty(const std::string& name, Texture* data);
@@ -70,8 +78,6 @@ namespace sh::render
SH_RENDER_API auto Serialize() const -> core::Json override;
SH_RENDER_API void Deserialize(const core::Json& json) override;
-
- SH_RENDER_API auto GetCachedRenderTextures() const -> const core::SHashMap& { return cachedRts; }
private:
void Clear();
void SetDefaultProperties();
@@ -100,7 +106,6 @@ namespace sh::render
std::vector> dirtyProps;
std::vector> cachedConstantData;
- core::SHashMap cachedRts;
core::Observer::Listener onBufferUpdateListener;
@@ -124,7 +129,7 @@ namespace sh::render
propertyBlock.SetProperty(name, data);
- for (auto& location : propInfo->locations)
+ for (const Shader::PropertyInfo::Location& location : propInfo->locations)
{
auto it = std::find(dirtyProps.begin(), dirtyProps.end(), std::pair{ location.passPtr.Get(), location.layoutPtr});
if (it == dirtyProps.end())
@@ -134,6 +139,46 @@ namespace sh::render
bPropertyDirty = true;
}
template
+ inline void Material::SetProperty(const std::string& name, const std::vector& data)
+ {
+ SetPropertyArray(name, data.data(), data.size());
+ }
+ template
+ inline void Material::SetProperty(const std::string& name, const std::array& data)
+ {
+ SetPropertyArray(name, data.data(), data.size());
+ }
+ template
+ inline void Material::SetPropertyArray(const std::string& name, const T* data, std::size_t count)
+ {
+ if (!core::IsValid(shader))
+ return;
+
+ const Shader::PropertyInfo* propInfo = shader->GetProperty(name);
+ assert(propInfo != nullptr);
+ if (propInfo == nullptr)
+ return;
+
+ assert(propInfo->type == core::reflection::GetType());
+ if (propInfo->type != core::reflection::GetType())
+ return;
+
+ assert(propInfo->locations.empty() || propInfo->locations.front().memberPtr->IsArray());
+ if (!propInfo->locations.empty() && !propInfo->locations.front().memberPtr->IsArray())
+ return;
+
+ propertyBlock.SetArrayProperty(name, data, count);
+
+ for (const Shader::PropertyInfo::Location& location : propInfo->locations)
+ {
+ auto it = std::find(dirtyProps.begin(), dirtyProps.end(), std::pair{ location.passPtr.Get(), location.layoutPtr });
+ if (it == dirtyProps.end())
+ dirtyProps.push_back({ location.passPtr.Get(), location.layoutPtr });
+ }
+
+ bPropertyDirty = true;
+ }
+ template
inline void Material::SetConstant(const std::string& name, const T& value)
{
if (shader == nullptr)
@@ -227,4 +272,4 @@ namespace sh::render
static_assert(true);
return {};
}
-}
\ No newline at end of file
+}
diff --git a/include/Render/MaterialData.h b/include/Render/MaterialData.h
index 6082f3c7..5142cf34 100644
--- a/include/Render/MaterialData.h
+++ b/include/Render/MaterialData.h
@@ -1,8 +1,9 @@
#pragma once
#include "Export.h"
#include "IBuffer.h"
-#include "IUniformBuffer.h"
+#include "IShaderBinding.h"
#include "ShaderEnum.h"
+#include "IRenderThrMethod.h"
#include "Core/ISyncable.h"
#include "Core/SContainer.hpp"
@@ -18,6 +19,7 @@ namespace sh::render
class Shader;
class ShaderPass;
class IRenderContext;
+ class RenderTexture;
/// @brief 셰이더에 전달 할 데이터를 가지고 있는 클래스
/// @brief 모든 변경 사항은 게임 스레드에서만 작성 시 스레드 안전하다.
@@ -25,12 +27,13 @@ namespace sh::render
public core::ISyncable,
public core::INonCopyable
{
+ friend struct IRenderThrMethod;
private:
struct PassData
{
const ShaderPass* pass;
- std::map>> uniformData; //set, binding
- std::map> uniformBuffer; // set
+ std::map>> buffers; //set, binding
+ std::map> shaderBindings; // set
};
struct SyncData
{
@@ -41,14 +44,14 @@ namespace sh::render
uint32_t binding;
std::vector data;
};
- struct UniformBufferSyncData
+ struct ShaderBindingSyncData
{
const ShaderPass* pass;
uint32_t set;
uint32_t binding;
const Texture* tex = nullptr;
};
- std::variant data;
+ std::variant data;
};
public:
SH_RENDER_API MaterialData() = default;
@@ -56,39 +59,43 @@ namespace sh::render
SH_RENDER_API void Create(const IRenderContext& context, const Shader& shader, bool bPerObject = false);
SH_RENDER_API void Clear();
- /// @brief 유니폼에 데이터를 지정한다.
+ /// @brief 버퍼에 데이터를 지정한다.
/// @param pass 패스 번호
/// @param binding 바인딩 번호
/// @param data 데이터 위치 포인터
/// @param dataSize 데이터 사이즈
- SH_RENDER_API void SetUniformData(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding, const void* data, std::size_t dataSize);
- /// @brief 유니폼에 데이터를 지정한다.
+ SH_RENDER_API void SetBindingData(const ShaderPass& shaderPass, UniformStructLayout::Usage usage, uint32_t binding, const void* data, std::size_t dataSize);
+ /// @brief 버퍼에 데이터를 지정한다.
/// @param pass 패스 번호
/// @param binding 바인딩 번호
/// @param data 데이터
- SH_RENDER_API void SetUniformData(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding, const std::vector& data);
- /// @brief 유니폼에 데이터를 지정한다.
- /// @param pass 패스 번호
- /// @param binding 바인딩 번호
- /// @param data 데이터
- SH_RENDER_API void SetUniformData(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding, std::vector&& data);
+ SH_RENDER_API void SetBindingData(const ShaderPass& shaderPass, UniformStructLayout::Usage usage, uint32_t binding, std::vector data);
/// @brief 유니폼 텍스쳐 데이터를 지정한다.
/// /// @param pass 패스 번호
/// @param binding 텍스쳐 바인딩 번호
/// @param tex 텍스쳐
- SH_RENDER_API void SetTextureData(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding, const Texture& tex);
+ SH_RENDER_API void SetTextureData(const ShaderPass& shaderPass, UniformStructLayout::Usage usage, uint32_t binding, const Texture& tex);
- SH_RENDER_API auto GetShaderBuffer(const ShaderPass& shaderPass, UniformStructLayout::Type type, uint32_t binding) const -> IBuffer*;
- SH_RENDER_API auto GetUniformBuffer(const ShaderPass& shaderPass, UniformStructLayout::Type type) const -> IUniformBuffer*;
+ SH_RENDER_API auto GetShaderBuffer(const ShaderPass& shaderPass, UniformStructLayout::Usage usage, uint32_t binding) const -> IBuffer*;
+ SH_RENDER_API auto GetShaderBinding(const ShaderPass& shaderPass, UniformStructLayout::Usage usage) const -> IShaderBinding*;
SH_RENDER_API void SyncDirty() override;
+ public:
+ struct CachedRT
+ {
+ uint32_t set = 0;
+ uint32_t binding = 0;
+ core::SObjWeakPtr pass;
+ core::SObjWeakPtr rt;
+ };
protected:
SH_RENDER_API void Sync() override;
+ auto GetCachedRTs() const -> const std::vector& { return cachedRTs; }
private:
void CreateBuffers(const IRenderContext& context, const Shader& shader, bool bPerObject);
auto GetMaterialPassData(const ShaderPass& shaderPass) const -> const MaterialData::PassData*;
void SetUniformDataAtSync(const SyncData::BufferSyncData& bufferSyncData);
- void SetTextureDataAtSync(const SyncData::UniformBufferSyncData& uniformBufferSyncData);
+ void SetTextureDataAtSync(const SyncData::ShaderBindingSyncData& uniformBufferSyncData);
private:
const IRenderContext* context = nullptr;
const Shader* shader = nullptr;
@@ -97,9 +104,17 @@ namespace sh::render
std::vector syncDatas;
+ std::vector cachedRTs;
+
bool bDirty = false;
bool bClearDirty = false;
bool bCreateDirty = false;
bool bPerObject = false;
};
+
+ template<>
+ struct IRenderThrMethod
+ {
+ static auto GetCachedRTs(const MaterialData& materialData) -> const std::vector& { return materialData.GetCachedRTs(); }
+ };
}//namespace
\ No newline at end of file
diff --git a/include/Render/MaterialPropertyBlock.h b/include/Render/MaterialPropertyBlock.h
index 4d23b179..eadc1e3f 100644
--- a/include/Render/MaterialPropertyBlock.h
+++ b/include/Render/MaterialPropertyBlock.h
@@ -6,10 +6,17 @@
#include "Core/SContainer.hpp"
#include "glm/mat4x4.hpp"
+#include "glm/vec2.hpp"
+#include "glm/vec3.hpp"
+#include "glm/vec4.hpp"
#include
#include
#include
+#include
+#include
+#include
+#include
namespace sh::render
{
@@ -21,12 +28,18 @@ namespace sh::render
template>>
void SetProperty(const std::string& name, const T& data);
+ template
+ void SetArrayProperty(const std::string& name, const T* data, std::size_t count);
SH_RENDER_API void SetProperty(const std::string& name, const Texture* data);
SH_RENDER_API auto GetScalarProperty(const std::string& name) const -> std::optional;
SH_RENDER_API auto GetVectorProperty(const std::string& name) const -> const glm::vec4*;
SH_RENDER_API auto GetMatrixProperty(const std::string& name) const -> const glm::mat4*;
SH_RENDER_API auto GetTextureProperty(const std::string& name) const -> const Texture*;
+ SH_RENDER_API auto GetIntArrayProperty(const std::string& name) const -> const std::vector*;
+ SH_RENDER_API auto GetScalarArrayProperty(const std::string& name) const -> const std::vector*;
+ SH_RENDER_API auto GetVectorArrayProperty(const std::string& name) const -> const std::vector*;
+ SH_RENDER_API auto GetMatrixArrayProperty(const std::string& name) const -> const std::vector*;
SH_RENDER_API auto GetTextureProperties() const -> const std::unordered_map&;
SH_RENDER_API auto Serialize() const -> core::Json override;
@@ -35,6 +48,10 @@ namespace sh::render
std::unordered_map scalarProperties;
std::unordered_map vecProperties;
std::unordered_map matProperties;
+ std::unordered_map> intArrayProperties;
+ std::unordered_map> scalarArrayProperties;
+ std::unordered_map> vecArrayProperties;
+ std::unordered_map> matArrayProperties;
core::SHashMap textureProperties;
};
@@ -58,4 +75,69 @@ namespace sh::render
else
static_assert(true);
}
-}//namespace
\ No newline at end of file
+ template
+ inline void MaterialPropertyBlock::SetArrayProperty(const std::string& name, const T* data, std::size_t count)
+ {
+ if constexpr (std::is_arithmetic_v)
+ {
+ if constexpr (std::is_integral_v)
+ {
+ std::vector values(count);
+ for (std::size_t i = 0; i < count; ++i)
+ values[i] = static_cast(data[i]);
+ intArrayProperties.insert_or_assign(name, std::move(values));
+ }
+ else
+ {
+ std::vector values(count);
+ for (std::size_t i = 0; i < count; ++i)
+ values[i] = static_cast(data[i]);
+ scalarArrayProperties.insert_or_assign(name, std::move(values));
+ }
+ }
+ else if constexpr (std::is_same_v)
+ {
+ std::vector values(count);
+ for (std::size_t i = 0; i < count; ++i)
+ values[i] = glm::vec4{ data[i], 0.0f, 0.0f };
+ vecArrayProperties.insert_or_assign(name, std::move(values));
+ }
+ else if constexpr (std::is_same_v)
+ {
+ std::vector values(count);
+ for (std::size_t i = 0; i < count; ++i)
+ values[i] = glm::vec4{ data[i], 0.0f };
+ vecArrayProperties.insert_or_assign(name, std::move(values));
+ }
+ else if constexpr (std::is_same_v)
+ {
+ if (count == 0)
+ vecArrayProperties.insert_or_assign(name, std::vector{});
+ else
+ vecArrayProperties.insert_or_assign(name, std::vector{ data, data + count });
+ }
+ else if constexpr (std::is_same_v)
+ {
+ std::vector values(count);
+ for (std::size_t i = 0; i < count; ++i)
+ values[i] = core::Util::ConvertMat2ToMat4(data[i]);
+ matArrayProperties.insert_or_assign(name, std::move(values));
+ }
+ else if constexpr (std::is_same_v)
+ {
+ std::vector values(count);
+ for (std::size_t i = 0; i < count; ++i)
+ values[i] = core::Util::ConvertMat3ToMat4(data[i]);
+ matArrayProperties.insert_or_assign(name, std::move(values));
+ }
+ else if constexpr (std::is_same_v)
+ {
+ if (count == 0)
+ matArrayProperties.insert_or_assign(name, std::vector{});
+ else
+ matArrayProperties.insert_or_assign(name, std::vector{ data, data + count });
+ }
+ else
+ static_assert(true);
+ }
+}//namespace
diff --git a/include/Render/Mesh.h b/include/Render/Mesh.h
index 418e465a..9b7913dd 100644
--- a/include/Render/Mesh.h
+++ b/include/Render/Mesh.h
@@ -19,6 +19,11 @@ namespace sh::render
{
class IVertexBuffer;
+ struct SubMesh
+ {
+ std::size_t indexOffset = 0;
+ std::size_t indexCount = 0;
+ };
/// @brief 모델 데이터를 지니는 클래스. 반드시 사용전 Build를 호출 해야한다.
class Mesh : public core::SObject, public core::INonCopyable, public render::IRenderResource
{
@@ -54,42 +59,44 @@ namespace sh::render
SH_RENDER_API auto operator=(const Mesh& other) -> Mesh&;
SH_RENDER_API auto operator=(Mesh&& other) noexcept -> Mesh&;
- SH_RENDER_API void SetVertex(const std::vector& verts);
- SH_RENDER_API void SetVertex(std::vector