Compare commits
7 Commits
97d03733a4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4a985c8285 | |||
|
|
71e5bf859d | ||
| 14e187fdc2 | |||
|
|
b70bd2b395 | ||
|
|
a8cd543f81 | ||
|
|
fbde75de72 | ||
|
|
47be30fdf6 |
@@ -1,40 +1,44 @@
|
|||||||
---
|
---
|
||||||
Language: Cpp
|
Language: Cpp
|
||||||
AccessModifierOffset: -4
|
AccessModifierOffset: -4
|
||||||
AlignAfterOpenBracket: BlockIndent
|
AlignAfterOpenBracket: Align
|
||||||
AlignArrayOfStructures: Right
|
AlignArrayOfStructures: Right
|
||||||
AlignConsecutiveAssignments:
|
AlignConsecutiveAssignments:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
AcrossEmptyLines: false
|
AcrossEmptyLines: false
|
||||||
AcrossComments: false
|
AcrossComments: false
|
||||||
AlignCompound: false
|
AlignCompound: false
|
||||||
|
AlignFunctionDeclarations: false
|
||||||
AlignFunctionPointers: false
|
AlignFunctionPointers: false
|
||||||
PadOperators: true
|
PadOperators: true
|
||||||
AlignConsecutiveBitFields:
|
AlignConsecutiveBitFields:
|
||||||
Enabled: false
|
Enabled: true
|
||||||
AcrossEmptyLines: false
|
AcrossEmptyLines: true
|
||||||
AcrossComments: false
|
AcrossComments: true
|
||||||
AlignCompound: false
|
AlignCompound: false
|
||||||
|
AlignFunctionDeclarations: false
|
||||||
AlignFunctionPointers: false
|
AlignFunctionPointers: false
|
||||||
PadOperators: false
|
PadOperators: false
|
||||||
AlignConsecutiveDeclarations:
|
AlignConsecutiveDeclarations:
|
||||||
Enabled: false
|
Enabled: true
|
||||||
AcrossEmptyLines: false
|
AcrossEmptyLines: false
|
||||||
AcrossComments: false
|
AcrossComments: true
|
||||||
AlignCompound: false
|
AlignCompound: false
|
||||||
|
AlignFunctionDeclarations: true
|
||||||
AlignFunctionPointers: false
|
AlignFunctionPointers: false
|
||||||
PadOperators: false
|
PadOperators: false
|
||||||
AlignConsecutiveMacros:
|
AlignConsecutiveMacros:
|
||||||
Enabled: false
|
Enabled: true
|
||||||
AcrossEmptyLines: false
|
AcrossEmptyLines: true
|
||||||
AcrossComments: false
|
AcrossComments: true
|
||||||
AlignCompound: false
|
AlignCompound: false
|
||||||
|
AlignFunctionDeclarations: false
|
||||||
AlignFunctionPointers: false
|
AlignFunctionPointers: false
|
||||||
PadOperators: false
|
PadOperators: false
|
||||||
AlignConsecutiveShortCaseStatements:
|
AlignConsecutiveShortCaseStatements:
|
||||||
Enabled: false
|
Enabled: true
|
||||||
AcrossEmptyLines: false
|
AcrossEmptyLines: true
|
||||||
AcrossComments: false
|
AcrossComments: true
|
||||||
AlignCaseArrows: false
|
AlignCaseArrows: false
|
||||||
AlignCaseColons: false
|
AlignCaseColons: false
|
||||||
AlignConsecutiveTableGenBreakingDAGArgColons:
|
AlignConsecutiveTableGenBreakingDAGArgColons:
|
||||||
@@ -42,6 +46,7 @@ AlignConsecutiveTableGenBreakingDAGArgColons:
|
|||||||
AcrossEmptyLines: false
|
AcrossEmptyLines: false
|
||||||
AcrossComments: false
|
AcrossComments: false
|
||||||
AlignCompound: false
|
AlignCompound: false
|
||||||
|
AlignFunctionDeclarations: false
|
||||||
AlignFunctionPointers: false
|
AlignFunctionPointers: false
|
||||||
PadOperators: false
|
PadOperators: false
|
||||||
AlignConsecutiveTableGenCondOperatorColons:
|
AlignConsecutiveTableGenCondOperatorColons:
|
||||||
@@ -49,6 +54,7 @@ AlignConsecutiveTableGenCondOperatorColons:
|
|||||||
AcrossEmptyLines: false
|
AcrossEmptyLines: false
|
||||||
AcrossComments: false
|
AcrossComments: false
|
||||||
AlignCompound: false
|
AlignCompound: false
|
||||||
|
AlignFunctionDeclarations: false
|
||||||
AlignFunctionPointers: false
|
AlignFunctionPointers: false
|
||||||
PadOperators: false
|
PadOperators: false
|
||||||
AlignConsecutiveTableGenDefinitionColons:
|
AlignConsecutiveTableGenDefinitionColons:
|
||||||
@@ -56,6 +62,7 @@ AlignConsecutiveTableGenDefinitionColons:
|
|||||||
AcrossEmptyLines: false
|
AcrossEmptyLines: false
|
||||||
AcrossComments: false
|
AcrossComments: false
|
||||||
AlignCompound: false
|
AlignCompound: false
|
||||||
|
AlignFunctionDeclarations: false
|
||||||
AlignFunctionPointers: false
|
AlignFunctionPointers: false
|
||||||
PadOperators: false
|
PadOperators: false
|
||||||
AlignEscapedNewlines: Right
|
AlignEscapedNewlines: Right
|
||||||
@@ -75,13 +82,16 @@ AllowShortFunctionsOnASingleLine: All
|
|||||||
AllowShortIfStatementsOnASingleLine: Never
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
AllowShortLambdasOnASingleLine: Empty
|
AllowShortLambdasOnASingleLine: Empty
|
||||||
AllowShortLoopsOnASingleLine: false
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AllowShortNamespacesOnASingleLine: false
|
||||||
AlwaysBreakAfterDefinitionReturnType: None
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
AlwaysBreakBeforeMultilineStrings: false
|
AlwaysBreakBeforeMultilineStrings: true
|
||||||
AttributeMacros:
|
AttributeMacros:
|
||||||
- __capability
|
- __capability
|
||||||
BinPackArguments: true
|
BinPackArguments: true
|
||||||
BinPackParameters: true
|
BinPackLongBracedList: true
|
||||||
|
BinPackParameters: BinPack
|
||||||
BitFieldColonSpacing: None
|
BitFieldColonSpacing: None
|
||||||
|
BracedInitializerIndentWidth: -1
|
||||||
BraceWrapping:
|
BraceWrapping:
|
||||||
AfterCaseLabel: true
|
AfterCaseLabel: true
|
||||||
AfterClass: true
|
AfterClass: true
|
||||||
@@ -102,7 +112,7 @@ BraceWrapping:
|
|||||||
SplitEmptyRecord: false
|
SplitEmptyRecord: false
|
||||||
SplitEmptyNamespace: false
|
SplitEmptyNamespace: false
|
||||||
BreakAdjacentStringLiterals: true
|
BreakAdjacentStringLiterals: true
|
||||||
BreakAfterAttributes: Never
|
BreakAfterAttributes: Leave
|
||||||
BreakAfterJavaFieldAnnotations: false
|
BreakAfterJavaFieldAnnotations: false
|
||||||
BreakAfterReturnType: None
|
BreakAfterReturnType: None
|
||||||
BreakArrays: false
|
BreakArrays: false
|
||||||
@@ -110,14 +120,16 @@ BreakBeforeBinaryOperators: All
|
|||||||
BreakBeforeConceptDeclarations: Always
|
BreakBeforeConceptDeclarations: Always
|
||||||
BreakBeforeBraces: Custom
|
BreakBeforeBraces: Custom
|
||||||
BreakBeforeInlineASMColon: OnlyMultiline
|
BreakBeforeInlineASMColon: OnlyMultiline
|
||||||
|
BreakBeforeTemplateCloser: true
|
||||||
BreakBeforeTernaryOperators: true
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakBinaryOperations: Never
|
||||||
BreakConstructorInitializers: BeforeComma
|
BreakConstructorInitializers: BeforeComma
|
||||||
BreakFunctionDefinitionParameters: false
|
BreakFunctionDefinitionParameters: false
|
||||||
BreakInheritanceList: BeforeComma
|
BreakInheritanceList: BeforeComma
|
||||||
BreakStringLiterals: true
|
BreakStringLiterals: true
|
||||||
BreakTemplateDeclarations: Yes
|
BreakTemplateDeclarations: Yes
|
||||||
ColumnLimit: 142
|
ColumnLimit: 142
|
||||||
CommentPragmas: "^ IWYU pragma:"
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
CompactNamespaces: false
|
CompactNamespaces: false
|
||||||
ConstructorInitializerIndentWidth: 4
|
ConstructorInitializerIndentWidth: 4
|
||||||
ContinuationIndentWidth: 4
|
ContinuationIndentWidth: 4
|
||||||
@@ -126,6 +138,7 @@ DerivePointerAlignment: false
|
|||||||
DisableFormat: false
|
DisableFormat: false
|
||||||
EmptyLineAfterAccessModifier: Never
|
EmptyLineAfterAccessModifier: Never
|
||||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||||
|
EnumTrailingComma: Insert
|
||||||
ExperimentalAutoDetectBinPacking: false
|
ExperimentalAutoDetectBinPacking: false
|
||||||
FixNamespaceComments: true
|
FixNamespaceComments: true
|
||||||
ForEachMacros:
|
ForEachMacros:
|
||||||
@@ -144,15 +157,16 @@ IncludeCategories:
|
|||||||
Priority: 3
|
Priority: 3
|
||||||
SortPriority: 0
|
SortPriority: 0
|
||||||
CaseSensitive: false
|
CaseSensitive: false
|
||||||
- Regex: ".*"
|
- Regex: '.*'
|
||||||
Priority: 1
|
Priority: 1
|
||||||
SortPriority: 0
|
SortPriority: 0
|
||||||
CaseSensitive: false
|
CaseSensitive: false
|
||||||
IncludeIsMainRegex: "(Test)?$"
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
IncludeIsMainSourceRegex: ""
|
IncludeIsMainSourceRegex: ''
|
||||||
IndentAccessModifiers: false
|
IndentAccessModifiers: false
|
||||||
IndentCaseBlocks: false
|
IndentCaseBlocks: false
|
||||||
IndentCaseLabels: false
|
IndentCaseLabels: false
|
||||||
|
IndentExportBlock: false
|
||||||
IndentExternBlock: AfterExternBlock
|
IndentExternBlock: AfterExternBlock
|
||||||
IndentGotoLabels: true
|
IndentGotoLabels: true
|
||||||
IndentPPDirectives: None
|
IndentPPDirectives: None
|
||||||
@@ -160,7 +174,7 @@ IndentRequiresClause: true
|
|||||||
IndentWidth: 4
|
IndentWidth: 4
|
||||||
IndentWrappedFunctionNames: false
|
IndentWrappedFunctionNames: false
|
||||||
InsertBraces: true
|
InsertBraces: true
|
||||||
InsertNewlineAtEOF: false
|
InsertNewlineAtEOF: true
|
||||||
InsertTrailingCommas: None
|
InsertTrailingCommas: None
|
||||||
IntegerLiteralSeparator:
|
IntegerLiteralSeparator:
|
||||||
Binary: 0
|
Binary: 0
|
||||||
@@ -172,13 +186,14 @@ IntegerLiteralSeparator:
|
|||||||
JavaScriptQuotes: Leave
|
JavaScriptQuotes: Leave
|
||||||
JavaScriptWrapImports: true
|
JavaScriptWrapImports: true
|
||||||
KeepEmptyLines:
|
KeepEmptyLines:
|
||||||
AtEndOfFile: false
|
AtEndOfFile: true
|
||||||
AtStartOfBlock: true
|
AtStartOfBlock: true
|
||||||
AtStartOfFile: true
|
AtStartOfFile: true
|
||||||
|
KeepFormFeed: false
|
||||||
LambdaBodyIndentation: Signature
|
LambdaBodyIndentation: Signature
|
||||||
LineEnding: CRLF
|
LineEnding: DeriveLF
|
||||||
MacroBlockBegin: "^GAL_GPU_STRUCT_BEGIN|GAL_GPU_VERTEX_BEGIN|CBE_MATERIAL_STRUCT_BEGIN$"
|
MacroBlockBegin: '^GAL_GPU_STRUCT_BEGIN|GAL_GPU_VERTEX_BEGIN|CBE_MATERIAL_STRUCT_BEGIN$'
|
||||||
MacroBlockEnd: "^GAL_GPU_STRUCT_END|GAL_GPU_VERTEX_END|CBE_MATERIAL_STRUCT_END$"
|
MacroBlockEnd: '^GAL_GPU_STRUCT_END|GAL_GPU_VERTEX_END|CBE_MATERIAL_STRUCT_END$'
|
||||||
Macros:
|
Macros:
|
||||||
- GAL_RESOURCE_HND(x)=x
|
- GAL_RESOURCE_HND(x)=x
|
||||||
- AUDIO_RESOURCE_HND(x)=x
|
- AUDIO_RESOURCE_HND(x)=x
|
||||||
@@ -190,9 +205,11 @@ ObjCBlockIndentWidth: 2
|
|||||||
ObjCBreakBeforeNestedBlockParam: true
|
ObjCBreakBeforeNestedBlockParam: true
|
||||||
ObjCSpaceAfterProperty: false
|
ObjCSpaceAfterProperty: false
|
||||||
ObjCSpaceBeforeProtocolList: true
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
OneLineFormatOffRegex: ''
|
||||||
PackConstructorInitializers: BinPack
|
PackConstructorInitializers: BinPack
|
||||||
PenaltyBreakAssignment: 2
|
PenaltyBreakAssignment: 2
|
||||||
PenaltyBreakBeforeFirstCallParameter: 19
|
PenaltyBreakBeforeFirstCallParameter: 19
|
||||||
|
PenaltyBreakBeforeMemberAccess: 150
|
||||||
PenaltyBreakComment: 300
|
PenaltyBreakComment: 300
|
||||||
PenaltyBreakFirstLessLess: 120
|
PenaltyBreakFirstLessLess: 120
|
||||||
PenaltyBreakOpenParenthesis: 0
|
PenaltyBreakOpenParenthesis: 0
|
||||||
@@ -206,8 +223,9 @@ PointerAlignment: Right
|
|||||||
PPIndentWidth: -1
|
PPIndentWidth: -1
|
||||||
QualifierAlignment: Leave
|
QualifierAlignment: Leave
|
||||||
ReferenceAlignment: Pointer
|
ReferenceAlignment: Pointer
|
||||||
ReflowComments: true
|
ReflowComments: IndentOnly
|
||||||
RemoveBracesLLVM: false
|
RemoveBracesLLVM: false
|
||||||
|
RemoveEmptyLinesInUnwrappedLines: true
|
||||||
RemoveParentheses: Leave
|
RemoveParentheses: Leave
|
||||||
RemoveSemicolon: true
|
RemoveSemicolon: true
|
||||||
RequiresClausePosition: WithPreceding
|
RequiresClausePosition: WithPreceding
|
||||||
@@ -215,11 +233,14 @@ RequiresExpressionIndentation: OuterScope
|
|||||||
SeparateDefinitionBlocks: Leave
|
SeparateDefinitionBlocks: Leave
|
||||||
ShortNamespaceLines: 1
|
ShortNamespaceLines: 1
|
||||||
SkipMacroDefinitionBody: false
|
SkipMacroDefinitionBody: false
|
||||||
SortIncludes: Never
|
SortIncludes:
|
||||||
|
Enabled: false
|
||||||
|
IgnoreCase: false
|
||||||
SortJavaStaticImport: Before
|
SortJavaStaticImport: Before
|
||||||
SortUsingDeclarations: LexicographicNumeric
|
SortUsingDeclarations: LexicographicNumeric
|
||||||
SpaceAfterCStyleCast: false
|
SpaceAfterCStyleCast: false
|
||||||
SpaceAfterLogicalNot: false
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterOperatorKeyword: false
|
||||||
SpaceAfterTemplateKeyword: true
|
SpaceAfterTemplateKeyword: true
|
||||||
SpaceAroundPointerQualifiers: After
|
SpaceAroundPointerQualifiers: After
|
||||||
SpaceBeforeAssignmentOperators: true
|
SpaceBeforeAssignmentOperators: true
|
||||||
@@ -235,6 +256,7 @@ SpaceBeforeParensOptions:
|
|||||||
AfterFunctionDefinitionName: false
|
AfterFunctionDefinitionName: false
|
||||||
AfterFunctionDeclarationName: false
|
AfterFunctionDeclarationName: false
|
||||||
AfterIfMacros: true
|
AfterIfMacros: true
|
||||||
|
AfterNot: false
|
||||||
AfterOverloadedOperator: true
|
AfterOverloadedOperator: true
|
||||||
AfterPlacementOperator: true
|
AfterPlacementOperator: true
|
||||||
AfterRequiresInClause: true
|
AfterRequiresInClause: true
|
||||||
@@ -283,4 +305,6 @@ WhitespaceSensitiveMacros:
|
|||||||
- NS_SWIFT_NAME
|
- NS_SWIFT_NAME
|
||||||
- PP_STRINGIZE
|
- PP_STRINGIZE
|
||||||
- STRINGIZE
|
- STRINGIZE
|
||||||
---
|
WrapNamespaceBodyWithEmptyLines: Always
|
||||||
|
...
|
||||||
|
|
||||||
|
|||||||
2
.github/funding.yml
vendored
Normal file
2
.github/funding.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
github: [jeslaspravin]
|
||||||
|
custom:
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||||
rev: "v19.1.7"
|
rev: "v21.1.8"
|
||||||
hooks:
|
hooks:
|
||||||
- id: clang-format
|
- id: clang-format
|
||||||
exclude_types:
|
exclude_types:
|
||||||
|
|||||||
18
AGENTS.md
18
AGENTS.md
@@ -1,14 +1,13 @@
|
|||||||
# Agents.md
|
# Agents.md
|
||||||
|
|
||||||
## 📁 Repository Overview
|
## 📁 Repository Overview
|
||||||
|
The **Cranberry Engine** is a C++ 3‑D engine built with CMake 3.26+. The source tree follows a classic monorepo layout:
|
||||||
The **Cranberry Engine** is a C++ 3‑D engine that is built using **CMake 3.26+**. The source tree follows a classic *monorepo* layout:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
Cranberry/ # Root (CMakeLists.txt, global config)
|
Cranberry/
|
||||||
├─ Scripts/ # Helpers and CMake modules
|
├─ Scripts/ # Helpers and CMake modules
|
||||||
│ ├─ CMake/ # GlobalConfig, EngineProjectConfig, utilities
|
│ ├─ CMake/ # GlobalConfig, EngineProjectConfig, utilities
|
||||||
│ └─ ... # Other scripts
|
│ └─ ...
|
||||||
├─ Source/ # Actual engine code
|
├─ Source/ # Actual engine code
|
||||||
│ ├─ Runtime/ # Core engine modules & runtime executables
|
│ ├─ Runtime/ # Core engine modules & runtime executables
|
||||||
│ │ ├─ EngineModules/
|
│ │ ├─ EngineModules/
|
||||||
@@ -23,7 +22,6 @@ Cranberry/ # Root (CMakeLists.txt, global config)
|
|||||||
All CMake configuration is centralised in `Scripts/CMake/GlobalConfig.cmake` and `EngineProjectConfig.cmake`. The root `CMakeLists.txt` simply includes these files and then pulls in the `Source/` and `Scripts/` sub‑directories.
|
All CMake configuration is centralised in `Scripts/CMake/GlobalConfig.cmake` and `EngineProjectConfig.cmake`. The root `CMakeLists.txt` simply includes these files and then pulls in the `Source/` and `Scripts/` sub‑directories.
|
||||||
|
|
||||||
## 🏗️ Build Process
|
## 🏗️ Build Process
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Configure
|
# 1. Configure
|
||||||
cmake -S . -B build \
|
cmake -S . -B build \
|
||||||
@@ -41,7 +39,6 @@ ctest --test-dir build -V
|
|||||||
> **Note** – The project supports multiple configurations (`Debug`, `Development`, `Release`, `RelWithDebInfo`). The `Cranberry_ENABLE_CLANG_TIDY` option automatically turns on `clang-tidy` and generates `compile_commands.json`.
|
> **Note** – The project supports multiple configurations (`Debug`, `Development`, `Release`, `RelWithDebInfo`). The `Cranberry_ENABLE_CLANG_TIDY` option automatically turns on `clang-tidy` and generates `compile_commands.json`.
|
||||||
|
|
||||||
## 🧪 Testing
|
## 🧪 Testing
|
||||||
|
|
||||||
Tests are written using **Doctest**. They are enabled by default (`Cranberry_ENABLE_TESTS=ON`). To run the test suite after building:
|
Tests are written using **Doctest**. They are enabled by default (`Cranberry_ENABLE_TESTS=ON`). To run the test suite after building:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -62,17 +59,15 @@ The test tree mirrors the source layout. New tests should be placed under `Sour
|
|||||||
The **code‑reviewer** agent will automatically run these checks on each commit. If any rule is violated, the PR will be marked **Failed**.
|
The **code‑reviewer** agent will automatically run these checks on each commit. If any rule is violated, the PR will be marked **Failed**.
|
||||||
|
|
||||||
## 🤖 Agent Responsibilities
|
## 🤖 Agent Responsibilities
|
||||||
|
|
||||||
| Agent | Responsibility | Trigger |
|
| Agent | Responsibility | Trigger |
|
||||||
|-------|----------------|---------|
|
|----------------|--------------------------------------------------------------|-------------------------------------------|
|
||||||
| `code-reviewer` | Validate CMake, compile flags, formatting, tests | On pull request / commit |
|
| `code-reviewer`| Validate CMake, compile flags, formatting, tests | On pull request / commit |
|
||||||
| `code-assistant` | Assist in adding new modules, generating boilerplate, updating CMake | On issue or PR requesting new feature |
|
| `code-assistant`| Assist in adding new modules, generating boilerplate, updating CMake | On issue or PR requesting new feature |
|
||||||
| `ci-agent` | Run CI pipeline (build, tests, clang‑tidy) | On push to main or PR |
|
| `ci-agent` | Run CI pipeline (build, tests, clang‑tidy) | On push to main or PR |
|
||||||
|
|
||||||
> **Tip** – When creating a new module, place the code under `Source/Runtime/<ModuleName>/` and add a `CMakeLists.txt` that uses `add_cmake_subdirectories()` to pull in sub‑directories.
|
> **Tip** – When creating a new module, place the code under `Source/Runtime/<ModuleName>/` and add a `CMakeLists.txt` that uses `add_cmake_subdirectories()` to pull in sub‑directories.
|
||||||
|
|
||||||
## 📦 CI Integration
|
## 📦 CI Integration
|
||||||
|
|
||||||
A minimal GitHub Actions workflow (`ci.yml`) can be added to the `.github/workflows` directory to automatically build and test on each push:
|
A minimal GitHub Actions workflow (`ci.yml`) can be added to the `.github/workflows` directory to automatically build and test on each push:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -100,4 +95,3 @@ Feel free to extend this file to include clang‑tidy or clang‑format checks.
|
|||||||
---
|
---
|
||||||
|
|
||||||
*This file serves as a quick reference for developers and the automated agents working on the project.*
|
*This file serves as a quick reference for developers and the automated agents working on the project.*
|
||||||
|
|
||||||
|
|||||||
2
Dependencies/CMakeLists.txt
vendored
2
Dependencies/CMakeLists.txt
vendored
@@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required( VERSION 3.26 )
|
cmake_minimum_required( VERSION 3.26 )
|
||||||
|
|
||||||
set( deps_manifest_url "https://drive.jeslaspravin.com/seafhttp/f/563b857bc80b4ed8babd/?op=view" )
|
set( deps_manifest_url "https://drive.jeslaspravin.com/seafhttp/f/4c67c4d663d84fd28d98/?op=view" )
|
||||||
set( deps_version "0.1" )
|
set( deps_version "0.1" )
|
||||||
set( deps_path ${CMAKE_CURRENT_LIST_DIR} )
|
set( deps_path ${CMAKE_CURRENT_LIST_DIR} )
|
||||||
set( deps_setup_status_file ${deps_path}/DepsSetupSuccess.txt )
|
set( deps_setup_status_file ${deps_path}/DepsSetupSuccess.txt )
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014-2025 Omar Cornut
|
Copyright (c) 2014-2026 Omar Cornut
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Open Asset Import Library (assimp)
|
Open Asset Import Library (assimp)
|
||||||
|
|
||||||
Copyright (c) 2006-2021, assimp team
|
Copyright (c) 2006-2026, assimp team
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use of this software in source and binary forms,
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
Tracy Profiler (https://github.com/wolfpld/tracy) is licensed under the
|
Tracy Profiler (https://github.com/wolfpld/tracy) is licensed under the
|
||||||
3-clause BSD license.
|
3-clause BSD license.
|
||||||
|
|
||||||
Copyright (c) 2017-2025, Bartosz Taudul <wolf@nereid.pl>
|
Copyright (c) 2017-2026, Bartosz Taudul <wolf@nereid.pl>
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|||||||
38
ReadMe.md
38
ReadMe.md
@@ -2,17 +2,18 @@
|
|||||||
|
|
||||||
![CranberryLogo]
|
![CranberryLogo]
|
||||||
|
|
||||||
**Cranberry Game Engine** is my Personal hobby game engine. I work on this as best as I could during my holidays.
|
**Cranberry Game Engine** is my Personal hobby game engine. I work on this as best as I could during my holidays. It is MIT Licensed open source engine with high code quality.
|
||||||
|
|
||||||
The goal of the engine is to have a set of tools required to develop a game without too much dependencies.
|
The goal of the engine is to have a set of tools required to develop a game without too much dependencies.
|
||||||
In the renderer side I aim to be as efficient as possible given the little time I invest in the engine.
|
In the renderer side I aim to be as efficient as possible given the little time I invest in the engine.
|
||||||
Physics will be optional for any game so I intend to keep the module and its integration into game objects as decoupled as possible.
|
Physics will be optional for any game so I intend to keep the module and its integration into game objects as decoupled as possible.
|
||||||
Audio will always be needed so that will be tightly integrated with other objects.
|
Audio will always be needed so that will be tightly integrated with other objects.
|
||||||
|
|
||||||
Some features are over engineered and I am fine with it. So if you want to contribute it is okay to over engineer as long as the changes itself is self contained.
|
|
||||||
|
|
||||||
Join the discord server at <https://discord.gg/2xfVG4QPt8> if you want to make some meaningful impact.
|
Join the discord server at <https://discord.gg/2xfVG4QPt8> if you want to make some meaningful impact.
|
||||||
|
|
||||||
|
All of the engine code is written without using AI, if that matters at all. I assure you not many engines can say that.
|
||||||
|
I am a single person right now working on this engine with very little time to spend in it while spending time with my family, so there are few places AI is used like to comment code, generate build scripts. However all such usage was heavily reviewed/modified to keep the quality high.
|
||||||
|
|
||||||
## Requirement
|
## Requirement
|
||||||
|
|
||||||
* Vulkan 1.3 supported hardware is required. Last time I checked the engine runs even on Intel 7th Gen GPU device/driver, I would like to keep it that way as long as possible.
|
* Vulkan 1.3 supported hardware is required. Last time I checked the engine runs even on Intel 7th Gen GPU device/driver, I would like to keep it that way as long as possible.
|
||||||
@@ -26,25 +27,29 @@ Can be found at [Best Practices]
|
|||||||
|
|
||||||
### Contribution
|
### Contribution
|
||||||
|
|
||||||
If you are looking to contribute, try to pick a task or issue from [Cranberry Project]
|
If you are looking to contribute, try to pick a task or issue from issues list.
|
||||||
Right now there is no contributor guideline. Regular workflow of forking the engine and creating a merge/pull request with description is good enough.
|
Right now there is no contributor guideline. Regular workflow of forking the engine and creating a merge/pull request with description is good enough.
|
||||||
I will add guidelines as nice people starts contributing.
|
I will add guidelines as nice people starts contributing.
|
||||||
|
|
||||||
Project is in early stage so if you contribute you will get opportunity to be a maintainer or lead of certain module as project grows. So now is the best time to contribute.
|
There are some points I would like to highlight
|
||||||
|
|
||||||
|
1. Project is in early stage so if you contribute you will get opportunity to be a maintainer or lead of certain module as project grows. So now is the best time to contribute.
|
||||||
|
2. If you are a student you have so much to gain by working on this project.
|
||||||
|
3. Some features are over engineered and I am fine with it. So if you want to contribute it is okay to over engineer as long as the changes itself is self contained.
|
||||||
|
|
||||||
### Getting started with first build
|
### Getting started with first build
|
||||||
|
|
||||||
* Install CMake from [CMake]
|
* Install CMake from [CMake]
|
||||||
* Install Visual Studio 2022/2026 from [VisualStudio]
|
* Install Visual Studio 2022/2026 from [VisualStudio]
|
||||||
* Install Vulkan SDK from [VulkanSDK]. Necessary because I am using `dxc` built for SPIR-V.
|
* Install Vulkan SDK from [VulkanSDK]. Necessary because I am using `dxc` built for SPIR-V.
|
||||||
* Run GenerateProject.ps1 to generate Visual Studio solution. This will by default generate solution for `Editor-DynamicLinked-VS2022` preset under `Build_VS2022` folder
|
* Run GenerateProject.ps1 to generate Visual Studio solution. This will by default generate solution for `Editor-DynamicLinked-VS2026` preset under `Build_VS2026` folder
|
||||||
|
|
||||||
***<p align="center">(or)</p>***
|
***<p align="center">(or)</p>***
|
||||||
|
|
||||||
* You can also run `GenerateProject.ps1` with one of following presets(eg., `GenerateProject.ps1` or `GenerateProject.ps1 <preset> [Some Cmake arguments]..`)
|
* You can also run `GenerateProject.ps1` with one of following presets(eg., `GenerateProject.ps1` or `GenerateProject.ps1 <preset> [Some Cmake arguments]..`)
|
||||||
Note that if using preset and not going to change library dependencies run `SetupDeps.ps1` first(It will download archive with necessary libraries)
|
Note that if using preset and not going to change library dependencies run `SetupDeps.ps1` first(It will download archive with necessary libraries)
|
||||||
* `Editor-DynamicLinked-VS2022` preset creates command `cmake -B Build_VS2022 -G "Visual Studio 17 2022" -A x64 -Thost=x64 -DCMAKE_INSTALL_PREFIX:STRING=${sourceDir}/Installed -DCranberry_STATIC_MODULES:BOOL=OFF`
|
* `Editor-DynamicLinked-VS2026` preset creates command `cmake -B Build_VS2026 -G "Visual Studio 18 2026" -A x64 -Thost=x64 -DCMAKE_INSTALL_PREFIX:STRING=${sourceDir}/Installed -DCranberry_STATIC_MODULES:BOOL=OFF`
|
||||||
* `Runtime-DynamicLinked-VS2022` preset creates command `cmake -B RuntimeBuild_VS2022 -G "Visual Studio 17 2022" -A x64 -Thost=x64 -DCMAKE_INSTALL_PREFIX:STRING=${sourceDir}/Installed -DCranberry_STATIC_MODULES:BOOL=OFF -DCranberry_EDITOR_BUILD:BOOL=OFF`
|
* `Runtime-DynamicLinked-VS2026` preset creates command `cmake -B RuntimeBuild_VS2026 -G "Visual Studio 18 2026" -A x64 -Thost=x64 -DCMAKE_INSTALL_PREFIX:STRING=${sourceDir}/Installed -DCranberry_STATIC_MODULES:BOOL=OFF -DCranberry_EDITOR_BUILD:BOOL=OFF`
|
||||||
|
|
||||||
### Optional
|
### Optional
|
||||||
|
|
||||||
@@ -64,18 +69,20 @@ Many features listed below are supported but tooling still needs to be developed
|
|||||||
|
|
||||||
* Reflection generator for C++
|
* Reflection generator for C++
|
||||||
* Reflection supports metadata classes and flags
|
* Reflection supports metadata classes and flags
|
||||||
* Shaders are reflected and Parameters can be addressed with names
|
* Shaders are assembled with input structure defined in C++. Offers compile time type check for inputs.
|
||||||
* PBR Materials
|
* PBR Materials
|
||||||
* Image base lighting
|
* Image base lighting
|
||||||
* Point, Spot, Directional lights
|
* Point, Spot, Directional lights
|
||||||
* Shadows, Cube mapped shadows and Cascaded shadows
|
* Shadows, Cube mapped shadows and Cascaded shadows
|
||||||
* Prototype garbage collector
|
* Mark and sweep garbage collector with reference list
|
||||||
* Object tree actions like deep copy, traversal etc.,
|
* Object tree actions like deep copy, traversal etc.,
|
||||||
* Multiwindow widgets and Input handling
|
* Multiwindow widgets and Input handling
|
||||||
* Supported inputs mouse and keyboard
|
* Supported inputs mouse and keyboard
|
||||||
* World/Actor/Components
|
* World/Actor/Components
|
||||||
* Unity style prefabs
|
* Unity style prefabs
|
||||||
* Job system using [CoPaT]
|
* Job system using [CoPaT]
|
||||||
|
* World editor and any asset editor(Some are Actor Prefab editor and Material Instance editor)
|
||||||
|
* Full fledged editor with complete Undo/Redo integration for actions and data
|
||||||
|
|
||||||
## Third parties
|
## Third parties
|
||||||
|
|
||||||
@@ -116,9 +123,11 @@ Licenses for third party packages used is placed under `Licenses` folder
|
|||||||
|
|
||||||
![CranberryActorPrefabEdSS]
|
![CranberryActorPrefabEdSS]
|
||||||
|
|
||||||
## PS
|
## ❤️ Support this project
|
||||||
|
|
||||||
If you found any piece of this software helpful or used it yourself, Please feel free to share it with your circle. I had invested substantial amount of my personal time in this project and would love some feedback in return😄
|
If you found any piece of this software helpful or used it yourself, please feel free to share it with your circle or support me. I had invested substantial amount of my personal time in this project.
|
||||||
|
|
||||||
|
* [Github Sponsor]
|
||||||
|
|
||||||
[//]: # (Below are link reference definitions)
|
[//]: # (Below are link reference definitions)
|
||||||
[CMake]: https://cmake.org/download/
|
[CMake]: https://cmake.org/download/
|
||||||
@@ -130,7 +139,8 @@ If you found any piece of this software helpful or used it yourself, Please feel
|
|||||||
[TestEngineSS]: <https://blogs.jeslaspravin.com/assets/images/CranberryEngine/TestEngine(08-01-2023).jpg> "Test Engine screenshot of PBR rendering with shadows and tone mapping"
|
[TestEngineSS]: <https://blogs.jeslaspravin.com/assets/images/CranberryEngine/TestEngine(08-01-2023).jpg> "Test Engine screenshot of PBR rendering with shadows and tone mapping"
|
||||||
[CranberryEdSS]: <https://blogs.jeslaspravin.com/assets/images/CranberryEngine/CranberryEditor(24-12-2025).jpg> "Cranberry engine editor main window screenshot"
|
[CranberryEdSS]: <https://blogs.jeslaspravin.com/assets/images/CranberryEngine/CranberryEditor(24-12-2025).jpg> "Cranberry engine editor main window screenshot"
|
||||||
[CranberryActorPrefabEdSS]: <https://blogs.jeslaspravin.com/assets/images/CranberryEngine/ActorPrefabEditor(24-12-2025).jpg> "Actor prefab editor with two actor prefab being edited side by side"
|
[CranberryActorPrefabEdSS]: <https://blogs.jeslaspravin.com/assets/images/CranberryEngine/ActorPrefabEditor(24-12-2025).jpg> "Actor prefab editor with two actor prefab being edited side by side"
|
||||||
[Best Practices]: <https://git.jeslaspravin.com/jeslaspravin/Cranberry/raw/branch/main/Docs/BestPractises.md>
|
[Best Practices]: <https://git.jeslaspravin.com/jeslaspravin/Cranberry/src/branch/main/Docs/BestPractices.md>
|
||||||
[Cranberry Project]: <https://git.jeslaspravin.com/jeslaspravin/Cranberry/projects/2>
|
|
||||||
|
|
||||||
[pre-commit webpage]: https://pre-commit.com/
|
[pre-commit webpage]: https://pre-commit.com/
|
||||||
|
|
||||||
|
[Github Sponsor]: <https://github.com/sponsors/jeslaspravin>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
# \author Jeslas Pravin
|
# \author Jeslas Pravin
|
||||||
# \date June 2022
|
# \date June 2022
|
||||||
# \copyright
|
# \copyright
|
||||||
# Copyright (C) Jeslas Pravin, 2022-2025
|
# Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
# @jeslaspravin pravinjeslas@gmail.com
|
# @jeslaspravin pravinjeslas@gmail.com
|
||||||
# License can be read in LICENSE file at this repository's root
|
# License can be read in LICENSE file at this repository's root
|
||||||
#
|
#
|
||||||
@@ -148,3 +148,41 @@ function( copy_targets_to_thirdparty )
|
|||||||
)
|
)
|
||||||
endif( ${Cranberry_STATIC_MODULES} AND ${Cranberry_STATIC_AS_OBJS} )
|
endif( ${Cranberry_STATIC_MODULES} AND ${Cranberry_STATIC_AS_OBJS} )
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
# Use on target with editor only dependency of dynamic linked shared libraries from several external targets.
|
||||||
|
# Adds necessary delay load target lists and compiler definitions to easily delay load the dependencies.
|
||||||
|
#
|
||||||
|
# TARGET - Dependent target.
|
||||||
|
# TARGETS_DEPS - Dependency targets that has shared lib or executable output.
|
||||||
|
# TARGET_DEPS_PREFIX - Prefixes to add to TARGET compile definitions for delay loading at runtime. Must have same length as TARGETS_DEPS
|
||||||
|
# OUT_DELAY_LOAD_LIST - Delay load list to fill with data.
|
||||||
|
#
|
||||||
|
function( add_editor_dep_delay_loaded )
|
||||||
|
set( one_value_args TARGET OUT_DELAY_LOAD_LIST )
|
||||||
|
set( multi_value_args TARGET_DEPS TARGET_DEPS_PREFIX )
|
||||||
|
cmake_parse_arguments( ed_dep "" "${one_value_args}" "${multi_value_args}" ${ARGN} )
|
||||||
|
|
||||||
|
set( local_delay_loaded_dlls )
|
||||||
|
|
||||||
|
# Add existing list to local list
|
||||||
|
if( DEFINED ed_dep_OUT_DELAY_LOAD_LIST AND DEFINED ${ed_dep_OUT_DELAY_LOAD_LIST} )
|
||||||
|
list( APPEND local_delay_loaded_dlls ${${ed_dep_OUT_DELAY_LOAD_LIST}} )
|
||||||
|
endif()
|
||||||
|
|
||||||
|
foreach( dep_target dep_prefix IN ZIP_LISTS ed_dep_TARGET_DEPS ed_dep_TARGET_DEPS_PREFIX )
|
||||||
|
# Add to delay loaded list, cannot use TARGET_FILE_BASE_NAME on imported or aliased targets so remove extension manually here.
|
||||||
|
list( APPEND local_delay_loaded_dlls "$<PATH:REMOVE_EXTENSION,$<TARGET_FILE_NAME:${dep_target}>>" )
|
||||||
|
|
||||||
|
target_compile_definitions( ${ed_dep_TARGET}
|
||||||
|
PRIVATE
|
||||||
|
CBE_${dep_prefix}_BIN_PATH="$<TARGET_FILE_DIR:${dep_target}>"
|
||||||
|
CBE_${dep_prefix}_LIB_NAME="$<TARGET_FILE_NAME:${dep_target}>"
|
||||||
|
)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Set the updated list to parent
|
||||||
|
if( DEFINED ed_dep_OUT_DELAY_LOAD_LIST AND local_delay_loaded_dlls )
|
||||||
|
set( ${ed_dep_OUT_DELAY_LOAD_LIST} ${local_delay_loaded_dlls} PARENT_SCOPE )
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endfunction( add_editor_dep_delay_loaded )
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
# \author Jeslas Pravin
|
# \author Jeslas Pravin
|
||||||
# \date January 2022
|
# \date January 2022
|
||||||
# \copyright
|
# \copyright
|
||||||
# Copyright (C) Jeslas Pravin, 2022-2025
|
# Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
# @jeslaspravin pravinjeslas@gmail.com
|
# @jeslaspravin pravinjeslas@gmail.com
|
||||||
# License can be read in LICENSE file at this repository's root
|
# License can be read in LICENSE file at this repository's root
|
||||||
#
|
#
|
||||||
@@ -44,6 +44,9 @@ set( Cranberry_SPIRV_TOOLS_PATH "${Cranberry_CPP_LIBS_PATH}/SPIRV-Tools" CACHE P
|
|||||||
# LLVM installed path
|
# LLVM installed path
|
||||||
set( Cranberry_LLVM_INSTALL_PATH "${Cranberry_CPP_LIBS_PATH}/llvm" CACHE PATH "LLVM installed path(For libclang)" )
|
set( Cranberry_LLVM_INSTALL_PATH "${Cranberry_CPP_LIBS_PATH}/llvm" CACHE PATH "LLVM installed path(For libclang)" )
|
||||||
|
|
||||||
|
# Assimp installed path
|
||||||
|
set (Cranberry_ASSIMP_INSTALL_PATH "${Cranberry_CPP_LIBS_PATH}/assimp" CACHE PATH "Assimp installed path" )
|
||||||
|
|
||||||
option( Cranberry_ENABLE_MIMALLOC "Compile with mimalloc?" ON )
|
option( Cranberry_ENABLE_MIMALLOC "Compile with mimalloc?" ON )
|
||||||
set( Cranberry_MIMALLOC_INSTALL_PATH "${Cranberry_CPP_LIBS_PATH}/mimalloc" CACHE PATH "mimalloc installed path" )
|
set( Cranberry_MIMALLOC_INSTALL_PATH "${Cranberry_CPP_LIBS_PATH}/mimalloc" CACHE PATH "mimalloc installed path" )
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ ignore_file_pattern = [
|
|||||||
".*\\.natstepfilter",
|
".*\\.natstepfilter",
|
||||||
".*\\.natvis",
|
".*\\.natvis",
|
||||||
".*\\.md",
|
".*\\.md",
|
||||||
|
".*\\.yml",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Overrides ignore files pattern
|
# Overrides ignore files pattern
|
||||||
|
|||||||
@@ -16,11 +16,35 @@ set( private_includes
|
|||||||
${Cranberry_CPP_LIBS_PATH}/tinyobjloader
|
${Cranberry_CPP_LIBS_PATH}/tinyobjloader
|
||||||
${Cranberry_CPP_LIBS_PATH}/stb
|
${Cranberry_CPP_LIBS_PATH}/stb
|
||||||
)
|
)
|
||||||
|
set( private_libraries )
|
||||||
|
|
||||||
|
set( delay_load_dlls )
|
||||||
|
|
||||||
|
# Finding Assimp libraries
|
||||||
|
make_find_package_hints( assimp ${Cranberry_ASSIMP_INSTALL_PATH} clang_hints )
|
||||||
|
find_package( assimp 6
|
||||||
|
REQUIRED CONFIG
|
||||||
|
HINTS ${clang_hints} )
|
||||||
|
|
||||||
|
if( DEFINED assimp_FOUND AND ${assimp_FOUND} AND TARGET assimp::assimp )
|
||||||
|
message( STATUS "Assimp found" )
|
||||||
|
map_imported_targets_config( TARGETS assimp::assimp ENABLE_DEBUG )
|
||||||
|
list( APPEND private_libraries assimp::assimp )
|
||||||
|
|
||||||
|
else()
|
||||||
|
message( FATAL_ERROR "Assimp package not found, Make sure to configure \"Cranberry_ASSIMP_INSTALL_PATH\" config CACHE with path to assimp install" )
|
||||||
|
endif()
|
||||||
|
|
||||||
generate_engine_editor_library()
|
generate_engine_editor_library()
|
||||||
target_compile_options( ${target_name} PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/MP> )
|
|
||||||
|
|
||||||
generate_reflection()
|
generate_reflection()
|
||||||
|
add_editor_dep_delay_loaded( TARGET ${target_name}
|
||||||
|
OUT_DELAY_LOAD_LIST delay_load_dlls
|
||||||
|
TARGET_DEPS assimp::assimp
|
||||||
|
TARGET_DEPS_PREFIX ASSIMP
|
||||||
|
)
|
||||||
|
mark_delay_loaded_dlls( IGNORE_MODULES )
|
||||||
|
|
||||||
|
target_compile_options( ${target_name} PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/MP> )
|
||||||
|
|
||||||
# Add shader folders
|
# Add shader folders
|
||||||
add_shader_directories( TARGET_NAME ${target_name}
|
add_shader_directories( TARGET_NAME ${target_name}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date December 2025
|
* \date December 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -16,11 +16,14 @@
|
|||||||
#include <AssetEditors/IEngineAssetEditor.hpp>
|
#include <AssetEditors/IEngineAssetEditor.hpp>
|
||||||
#include <Widgets/WgViewportImGuiLayer.h>
|
#include <Widgets/WgViewportImGuiLayer.h>
|
||||||
#include <Widgets/WgDetailsImGuiLayer.h>
|
#include <Widgets/WgDetailsImGuiLayer.h>
|
||||||
|
#include <Widgets/ImGui/CbeImGui.hpp>
|
||||||
|
#include <IApplicationModule.h>
|
||||||
#include <EditorEngine.hpp>
|
#include <EditorEngine.hpp>
|
||||||
#include <Widgets/ImGui/WgImGui.h>
|
#include <Widgets/ImGui/WgImGui.h>
|
||||||
#include <CBEPackage.hpp>
|
#include <CBEPackage.hpp>
|
||||||
#include <EditorHelpers.h>
|
#include <EditorHelpers.h>
|
||||||
#include <EditorHelpersTransacted.hpp>
|
#include <EditorHelpersTransacted.hpp>
|
||||||
|
#include <CranberryEngineApp.h>
|
||||||
|
|
||||||
namespace cbe
|
namespace cbe
|
||||||
{
|
{
|
||||||
@@ -40,7 +43,8 @@ public:
|
|||||||
void onCloseEditor() final;
|
void onCloseEditor() final;
|
||||||
MessageResponse sendMessage(MAYBE_UNUSED const MessageData &msgDat, MAYBE_UNUSED ECoreEdMessageType msgType) final;
|
MessageResponse sendMessage(MAYBE_UNUSED const MessageData &msgDat, MAYBE_UNUSED ECoreEdMessageType msgType) final;
|
||||||
void refreshEditor() final;
|
void refreshEditor() final;
|
||||||
void onObjectsEdited(MAYBE_UNUSED ArrayView<Object *> editedObjs) final;
|
void onObjectsEdited(MAYBE_UNUSED ArrayView<WeakObjectPtr> editedObjs) final;
|
||||||
|
void drawMenuBar() final;
|
||||||
|
|
||||||
/* Overrides ends */
|
/* Overrides ends */
|
||||||
private:
|
private:
|
||||||
@@ -67,9 +71,10 @@ ActorPrefabAssetEditor::ActorPrefabAssetEditor(Object *inAsset, Object *inEditin
|
|||||||
.edRoot = this,
|
.edRoot = this,
|
||||||
.viewportFlags = EViewportFlags::EnableAll | EViewportFlags::NoWorldEdit,
|
.viewportFlags = EViewportFlags::EnableAll | EViewportFlags::NoWorldEdit,
|
||||||
});
|
});
|
||||||
detailsLayer = std::make_shared<WgDetailsImGuiLayer>(WgDetailsCreateInfo{
|
detailsLayer = std::make_shared<WgDetailsImGuiLayer>(cbe::WgDetailsCreateInfo{
|
||||||
.edRoot = this,
|
.edRoot = this,
|
||||||
.detailWndName = detailsWndName,
|
.detailWndName = detailsWndName,
|
||||||
|
.flags = EDetailsFlags::SendCompSelMsgs,
|
||||||
});
|
});
|
||||||
|
|
||||||
WgImGui *wgImGui = gCBEditorEngine->getImGuiWidget();
|
WgImGui *wgImGui = gCBEditorEngine->getImGuiWidget();
|
||||||
@@ -114,45 +119,142 @@ void ActorPrefabAssetEditor::onCloseEditor()
|
|||||||
cbe::MessageResponse ActorPrefabAssetEditor::sendMessage(MAYBE_UNUSED const MessageData &msgDat, MAYBE_UNUSED ECoreEdMessageType msgType)
|
cbe::MessageResponse ActorPrefabAssetEditor::sendMessage(MAYBE_UNUSED const MessageData &msgDat, MAYBE_UNUSED ECoreEdMessageType msgType)
|
||||||
{
|
{
|
||||||
MessageResponse response{ .state = MessageResponse::None };
|
MessageResponse response{ .state = MessageResponse::None };
|
||||||
|
const bool bDetailsWgIssuer = msgDat.issuerData == std::bit_cast<uint64>(detailsLayer.get());
|
||||||
|
const bool bViewportWgIssuer = msgDat.issuerData == std::bit_cast<uint64>(viewportLayer.get());
|
||||||
|
|
||||||
switch (msgType)
|
switch (msgType)
|
||||||
{
|
{
|
||||||
case cbe::CoreEdMessage_RefreshEd:
|
case cbe::CoreEdMessage_RefreshEd:
|
||||||
if (msgDat.issuerData != std::bit_cast<uint64>(detailsLayer.get()) && std::holds_alternative<MessageData::ObjectsList>(msgDat.msg))
|
{
|
||||||
|
if (std::holds_alternative<MessageData::ObjectsList>(msgDat.msg))
|
||||||
|
{
|
||||||
|
if (!bDetailsWgIssuer)
|
||||||
{
|
{
|
||||||
detailsLayer->refreshDetailsDrawers(std::get<MessageData::ObjectsList>(msgDat.msg));
|
detailsLayer->refreshDetailsDrawers(std::get<MessageData::ObjectsList>(msgDat.msg));
|
||||||
|
}
|
||||||
|
response.state = MessageResponse::Remove;
|
||||||
|
}
|
||||||
|
else if (std::holds_alternative<EEdRefreshType>(msgDat.msg))
|
||||||
|
{
|
||||||
|
switch (std::get<EEdRefreshType>(msgDat.msg))
|
||||||
|
{
|
||||||
|
case EEdRefreshType::VisualOnly:
|
||||||
|
if (!bDetailsWgIssuer)
|
||||||
|
{
|
||||||
|
detailsLayer->refreshDetailsDrawers({});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EEdRefreshType::FullReset:
|
||||||
|
if (!bDetailsWgIssuer)
|
||||||
|
{
|
||||||
|
detailsLayer->resetDetails();
|
||||||
|
}
|
||||||
|
if (!bViewportWgIssuer)
|
||||||
|
{
|
||||||
|
viewportLayer->resetViewport();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
response.state = MessageResponse::Remove;
|
||||||
|
}
|
||||||
|
else if (std::holds_alternative<EmptyType>(msgDat.msg))
|
||||||
|
{
|
||||||
|
refreshEditor();
|
||||||
response.state = MessageResponse::Remove;
|
response.state = MessageResponse::Remove;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case cbe::CoreEdMessage_SelectionChanged:
|
case cbe::CoreEdMessage_SelectionChanged:
|
||||||
{
|
{
|
||||||
const std::vector<WeakObjPtr<Object>> &newSelObjs = viewportLayer->getWorldViewport().getSelectedObjects();
|
ActorPrefab *editingPrefab = static_cast<cbe::ActorPrefab *>(getEditingAsset());
|
||||||
|
WeakObjPtr<TransformComponent> rootTfComp = editingPrefab->getActorTemplate()->getRootComponent();
|
||||||
|
|
||||||
|
std::vector<cbe::WeakObjectPtr> newSelObjs = viewportLayer->getWorldViewport().getSelectedObjects();
|
||||||
const std::unordered_set<WorldSelectionProxyRef> &newSelProxies = viewportLayer->getWorldViewport().getSelectedProxies();
|
const std::unordered_set<WorldSelectionProxyRef> &newSelProxies = viewportLayer->getWorldViewport().getSelectedProxies();
|
||||||
|
|
||||||
|
/* Erase the root component from selected objects list */
|
||||||
|
debugAssert(rootTfComp.isValid());
|
||||||
|
std::erase_if(
|
||||||
|
newSelObjs,
|
||||||
|
[rootTfComp](const cbe::WeakObjectPtr &selObjPtr)
|
||||||
|
{
|
||||||
|
/* For actor prefab we do not have to select actor so remove anything other than required components. */
|
||||||
|
Object *selObj = selObjPtr.get();
|
||||||
|
CBEClass selType = selObj->getType();
|
||||||
|
return rootTfComp == selObj
|
||||||
|
|| !(
|
||||||
|
selType == TransformComponent::staticType()
|
||||||
|
|| PropertyHelper::isChildOf(selType, TransformLeafComponent::staticType())
|
||||||
|
|| PropertyHelper::isChildOf(selType, LogicComponent::staticType())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
/* If nothing was selected. Select the actor we are editing */
|
/* If nothing was selected. Select the actor we are editing */
|
||||||
if (newSelProxies.empty() && newSelObjs.empty())
|
if (newSelProxies.empty() && newSelObjs.empty())
|
||||||
{
|
{
|
||||||
viewportLayer->getWorldViewport().selectFromObjects(static_cast<cbe::ActorPrefab *>(getEditingAsset())->getActorTemplate());
|
viewportLayer->getWorldViewport().selectFromObjects(editingPrefab->getActorTemplate());
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(ASAP) : Update selection.
|
|
||||||
detailsLayer->onSelectionChanged();
|
detailsLayer->onSelectionChanged();
|
||||||
viewportLayer->onSelectionChanged();
|
viewportLayer->onSelectionChanged();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::vector<cbe::Object *> finalSelObjs;
|
||||||
|
std::vector<WorldSelectionProxyRef> finalSelProxies;
|
||||||
|
finalSelObjs.reserve(newSelObjs.size());
|
||||||
|
finalSelProxies.reserve(newSelProxies.size());
|
||||||
|
for (const cbe::WeakObjectPtr &ptr : newSelObjs)
|
||||||
|
{
|
||||||
|
if (ptr)
|
||||||
|
{
|
||||||
|
finalSelObjs.emplace_back(ptr.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const WorldSelectionProxyRef &proxyRef : newSelProxies)
|
||||||
|
{
|
||||||
|
finalSelProxies.emplace_back(proxyRef);
|
||||||
|
}
|
||||||
|
viewportLayer->getWorldViewport().selectManual(finalSelObjs, finalSelProxies);
|
||||||
|
|
||||||
|
cbe::MessageData::SubSelectionData msg{
|
||||||
|
.objs = newSelObjs,
|
||||||
|
.proxies = newSelProxies,
|
||||||
|
};
|
||||||
|
pushMessage(
|
||||||
|
cbe::MessageData{
|
||||||
|
.msg = msg,
|
||||||
|
},
|
||||||
|
cbe::CoreEdMessage_SubSelectionChanged
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
response.state = MessageResponse::Remove;
|
response.state = MessageResponse::Remove;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case cbe::CoreEdMessage_SubSelectionChanged:
|
case cbe::CoreEdMessage_SubSelectionChanged:
|
||||||
// TODO(ASAP) : Invoke sub selection update logics
|
{
|
||||||
|
debugAssert(std::holds_alternative<cbe::MessageData::SubSelectionData>(msgDat.msg));
|
||||||
|
const cbe::MessageData::SubSelectionData &subSelDat = std::get<cbe::MessageData::SubSelectionData>(msgDat.msg);
|
||||||
|
if (!bViewportWgIssuer)
|
||||||
|
{
|
||||||
|
viewportLayer->onSubselectionChanged(subSelDat.objs, subSelDat.proxies);
|
||||||
|
}
|
||||||
|
if (!bDetailsWgIssuer)
|
||||||
|
{
|
||||||
|
detailsLayer->onSubselectionChanged(subSelDat.objs, subSelDat.proxies);
|
||||||
|
}
|
||||||
response.state = MessageResponse::Remove;
|
response.state = MessageResponse::Remove;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case cbe::CoreEdMessage_SelectionTransformed:
|
case cbe::CoreEdMessage_SelectionTransformed:
|
||||||
/* Must be skipped if the issuer is same. As always refreshing makes transacting hard. */
|
/* Must be skipped if the issuer is same. As always refreshing makes transacting hard. */
|
||||||
if (msgDat.issuerData != std::bit_cast<uint64>(viewportLayer.get()))
|
if (!bViewportWgIssuer)
|
||||||
{
|
{
|
||||||
viewportLayer->onSelectionTransformed();
|
viewportLayer->onSelectionTransformed();
|
||||||
}
|
}
|
||||||
if (msgDat.issuerData != std::bit_cast<uint64>(detailsLayer.get()))
|
if (!bDetailsWgIssuer)
|
||||||
{
|
{
|
||||||
detailsLayer->onSelectionTransformed();
|
detailsLayer->onSelectionTransformed();
|
||||||
}
|
}
|
||||||
@@ -170,13 +272,29 @@ cbe::MessageResponse ActorPrefabAssetEditor::sendMessage(MAYBE_UNUSED const Mess
|
|||||||
|
|
||||||
void ActorPrefabAssetEditor::refreshEditor() { detailsLayer->refreshDetailsDrawers({}); }
|
void ActorPrefabAssetEditor::refreshEditor() { detailsLayer->refreshDetailsDrawers({}); }
|
||||||
|
|
||||||
void ActorPrefabAssetEditor::onObjectsEdited(MAYBE_UNUSED ArrayView<Object *> editedObjs)
|
void ActorPrefabAssetEditor::onObjectsEdited(MAYBE_UNUSED ArrayView<WeakObjectPtr> editedObjs)
|
||||||
{
|
{
|
||||||
/* Since we need the transform gizmo updated as well on external edit of objects */
|
/* Since we need the transform gizmo updated as well on external edit of objects */
|
||||||
pushMessage(CoreEdMessage_SelectionTransformed);
|
pushMessage(CoreEdMessage_SelectionTransformed);
|
||||||
IEngineAssetEditor::onObjectsEdited(editedObjs);
|
IEngineAssetEditor::onObjectsEdited(editedObjs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ActorPrefabAssetEditor::drawMenuBar()
|
||||||
|
{
|
||||||
|
if (ImGui::BeginMenu("Edit"))
|
||||||
|
{
|
||||||
|
if (ImGui::BeginMenu("Change Parent"))
|
||||||
|
{
|
||||||
|
const std::variant newParent = static_cast<ActorPrefabAssetFactory *>(getOwningFactory())->drawParentPrefabSelector();
|
||||||
|
cbe::ignoreUnused(newParent);
|
||||||
|
// TODO(ASAP) : Update prefab parent.
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// ActorPrefabAssetFactory implementation
|
// ActorPrefabAssetFactory implementation
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
@@ -186,16 +304,87 @@ ActorPrefabAssetFactory::ActorPrefabAssetFactory()
|
|||||||
.assetClass = cbe::ActorPrefab::staticType(),
|
.assetClass = cbe::ActorPrefab::staticType(),
|
||||||
.assetCategory = "Prefabs",
|
.assetCategory = "Prefabs",
|
||||||
})
|
})
|
||||||
|
, parentPrefabCntx{ .baseClassType = cbe::ActorPrefab::staticType() }
|
||||||
{}
|
{}
|
||||||
|
|
||||||
ActorPrefabAssetFactory::~ActorPrefabAssetFactory() {}
|
ActorPrefabAssetFactory::~ActorPrefabAssetFactory() {}
|
||||||
|
|
||||||
Object *ActorPrefabAssetFactory::createAsset(const AssetCreateInfo &ci)
|
std::variant<NullType, CBEClass, String> ActorPrefabAssetFactory::drawParentPrefabSelector()
|
||||||
|
{
|
||||||
|
std::variant<NullType, CBEClass, String> retValue;
|
||||||
|
|
||||||
|
EdClassCacheList &actorClasses = gCBEditorEngine->getActorClassesCache();
|
||||||
|
|
||||||
|
if (!actorClasses.classList.empty() && ImGui::BeginMenu("Parent Class"))
|
||||||
|
{
|
||||||
|
if (CBEClass clazz = cbe::EditorWidgetsHelper::drawClassMenuList(actorClasses, wg_consts::MENUITEM_WIDTH, true))
|
||||||
|
{
|
||||||
|
retValue = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const AChar *parentPrefabMenu = "Parent Prefab";
|
||||||
|
const bool bParentPrefabOpen = cbe::ImGuiHelpers::isPopupOpen(cbe::ImGuiHelpers::getPopupId(parentPrefabMenu));
|
||||||
|
if (ImGui::BeginMenu(parentPrefabMenu))
|
||||||
|
{
|
||||||
|
if (!bParentPrefabOpen)
|
||||||
|
{
|
||||||
|
parentPrefabCntx.bListUptoDate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CranberryEngineApp *application = static_cast<CranberryEngineApp *>(IApplicationModule::get()->getApplication());
|
||||||
|
AssetManager *assetManager = application->getAssetManager();
|
||||||
|
debugAssert(assetManager != nullptr);
|
||||||
|
const std::optional selPrefab
|
||||||
|
= GenericFieldCustomizer::drawWidgetClassPointerCntxMenuList(parentPrefabCntx, *assetManager, 200.0f, false);
|
||||||
|
if (selPrefab)
|
||||||
|
{
|
||||||
|
retValue = selPrefab->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
return retValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object *createApFromClass(const AssetCreateInfo &ci, CBEClass parentClazz)
|
||||||
{
|
{
|
||||||
cbe::ActorPrefab *prefab
|
cbe::ActorPrefab *prefab
|
||||||
= cbe::create<cbe::ActorPrefab, StringID, String>(ci.assetName, ci.outer, ci.flags, Actor::staticType()->name, ci.assetName);
|
= cbe::create<cbe::ActorPrefab, StringID, String>(ci.assetName, ci.outer, ci.flags, parentClazz->name, ci.assetName);
|
||||||
return prefab;
|
return prefab;
|
||||||
}
|
}
|
||||||
|
static Object *createApFromPrefab(const AssetCreateInfo &ci, String prefabPath)
|
||||||
|
{
|
||||||
|
cbe::ActorPrefab *parentPrefab = cbe::getOrLoad<cbe::ActorPrefab>(prefabPath);
|
||||||
|
cbe::ActorPrefab *prefab
|
||||||
|
= cbe::create<cbe::ActorPrefab, cbe::ActorPrefab *, String>(ci.assetName, ci.outer, ci.flags, parentPrefab, ci.assetName);
|
||||||
|
return prefab;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object *ActorPrefabAssetFactory::createAsset(const AssetCreateInfo &ci) { return createApFromClass(ci, Actor::staticType()); }
|
||||||
|
cbe::AssetCreateCustomCallback ActorPrefabAssetFactory::drawCustomCreateMenus()
|
||||||
|
{
|
||||||
|
return std::visit(
|
||||||
|
[]<typename T>(const T &parent)
|
||||||
|
{
|
||||||
|
cbe::AssetCreateCustomCallback createCb;
|
||||||
|
if constexpr (std::same_as<CBEClass, T>)
|
||||||
|
{
|
||||||
|
createCb.bindStatic(&createApFromClass, parent);
|
||||||
|
}
|
||||||
|
else if constexpr (std::same_as<String, T>)
|
||||||
|
{
|
||||||
|
createCb.bindStatic(&createApFromPrefab, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return createCb;
|
||||||
|
},
|
||||||
|
drawParentPrefabSelector()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ICoreAssetEditorRef ActorPrefabAssetFactory::createAssetEditorInternal(const AssetEditorCreateInfo &ci)
|
ICoreAssetEditorRef ActorPrefabAssetFactory::createAssetEditorInternal(const AssetEditorCreateInfo &ci)
|
||||||
{
|
{
|
||||||
cbe::Package *pkg = cbe::cast<cbe::Package>(ci.asset->getOuterMost());
|
cbe::Package *pkg = cbe::cast<cbe::Package>(ci.asset->getOuterMost());
|
||||||
@@ -212,17 +401,18 @@ ICoreAssetEditorRef ActorPrefabAssetFactory::createAssetEditorInternal(const Ass
|
|||||||
/* Now add the actor prefab to world and copy it */
|
/* Now add the actor prefab to world and copy it */
|
||||||
ActorPrefab *orgPrefab = static_cast<ActorPrefab *>(ci.asset);
|
ActorPrefab *orgPrefab = static_cast<ActorPrefab *>(ci.asset);
|
||||||
ActorPrefab *edPrefab = nullptr;
|
ActorPrefab *edPrefab = nullptr;
|
||||||
|
/* Prefab cannot be transient since it has to be transacted */
|
||||||
if (orgPrefab->getParentPrefab() != nullptr)
|
if (orgPrefab->getParentPrefab() != nullptr)
|
||||||
{
|
{
|
||||||
edPrefab = ActorPrefab::prefabFromActor(EditorHelpers::addActorToWorld(
|
edPrefab = ActorPrefab::prefabFromActor(
|
||||||
world, orgPrefab->getParentPrefab(), orgPrefab->getActorTemplate()->getObjectData().name, cbe::ObjFlag_Transient
|
EditorHelpers::addActorToWorld(world, orgPrefab->getParentPrefab(), orgPrefab->getActorTemplate()->getObjectData().name, 0)
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
edPrefab = ActorPrefab::prefabFromActor(EditorHelpers::addActorToWorld(
|
edPrefab = ActorPrefab::prefabFromActor(
|
||||||
world, orgPrefab->getActorClass(), orgPrefab->getActorTemplate()->getObjectData().name, cbe::ObjFlag_Transient
|
EditorHelpers::addActorToWorld(world, orgPrefab->getActorClass(), orgPrefab->getActorTemplate()->getObjectData().name, 0)
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
edPrefab->copyFrom(orgPrefab);
|
edPrefab->copyFrom(orgPrefab);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date December 2025
|
* \date December 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -12,9 +12,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ICoreAssetEditor.hpp>
|
#include <ICoreAssetEditor.hpp>
|
||||||
|
#include <DetailCustomizers/GenericFieldCustomizer.hpp>
|
||||||
|
|
||||||
namespace cbe
|
namespace cbe
|
||||||
{
|
{
|
||||||
|
class ActorPrefab;
|
||||||
|
|
||||||
class ActorPrefabAssetFactory : public ICoreAssetFactory
|
class ActorPrefabAssetFactory : public ICoreAssetFactory
|
||||||
{
|
{
|
||||||
@@ -22,8 +24,13 @@ public:
|
|||||||
ActorPrefabAssetFactory();
|
ActorPrefabAssetFactory();
|
||||||
~ActorPrefabAssetFactory();
|
~ActorPrefabAssetFactory();
|
||||||
|
|
||||||
protected:
|
std::variant<NullType, CBEClass, String> drawParentPrefabSelector();
|
||||||
|
|
||||||
/* ICoreAssetFactory overrides */
|
/* ICoreAssetFactory overrides */
|
||||||
|
public:
|
||||||
|
AssetCreateCustomCallback drawCustomCreateMenus() final;
|
||||||
|
|
||||||
|
protected:
|
||||||
bool canCreateAsset() const final { return true; }
|
bool canCreateAsset() const final { return true; }
|
||||||
Object *createAsset(const AssetCreateInfo &ci) final;
|
Object *createAsset(const AssetCreateInfo &ci) final;
|
||||||
|
|
||||||
@@ -36,6 +43,8 @@ private:
|
|||||||
/* Necessary to keep the actor prefab list updated. */
|
/* Necessary to keep the actor prefab list updated. */
|
||||||
DelegateHandle packageScannedEventHnd;
|
DelegateHandle packageScannedEventHnd;
|
||||||
DelegateHandle packageDeletedEventHnd;
|
DelegateHandle packageDeletedEventHnd;
|
||||||
|
|
||||||
|
GenericFieldCustomizer::ClassObjectContext parentPrefabCntx;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace cbe
|
} // namespace cbe
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date September 2025
|
* \date September 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -75,7 +75,7 @@ void GenericAssetEditor::drawImGui()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenericAssetEditor::onObjectsEdited(ArrayView<Object *> editedObjs)
|
void GenericAssetEditor::onObjectsEdited(ArrayView<WeakObjectPtr> editedObjs)
|
||||||
{
|
{
|
||||||
#if DEBUG_VALIDATIONS_ENABLED
|
#if DEBUG_VALIDATIONS_ENABLED
|
||||||
auto editingAssetItr = std::find(editedObjs.cbegin(), editedObjs.cend(), editingAsset);
|
auto editingAssetItr = std::find(editedObjs.cbegin(), editedObjs.cend(), editingAsset);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date September 2025
|
* \date September 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -33,7 +33,7 @@ public:
|
|||||||
/* IEngineAssetEditor overrides */
|
/* IEngineAssetEditor overrides */
|
||||||
void buildEditorLayout(ImGuiID dockId) final;
|
void buildEditorLayout(ImGuiID dockId) final;
|
||||||
void drawImGui() final;
|
void drawImGui() final;
|
||||||
void onObjectsEdited(ArrayView<Object *> editedObjs) final;
|
void onObjectsEdited(ArrayView<WeakObjectPtr> editedObjs) final;
|
||||||
void refreshEditor() final;
|
void refreshEditor() final;
|
||||||
|
|
||||||
/* ICoreAssetEditor overrides */
|
/* ICoreAssetEditor overrides */
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date September 2025
|
* \date September 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -122,7 +122,7 @@ MessageResponse IEngineAssetEditor::sendMessage(MAYBE_UNUSED const MessageData &
|
|||||||
break;
|
break;
|
||||||
case cbe::CoreEdMessage_ObjsEdited:
|
case cbe::CoreEdMessage_ObjsEdited:
|
||||||
{
|
{
|
||||||
ArrayView<Object *> editedObjs;
|
ArrayView<WeakObjectPtr> editedObjs;
|
||||||
if (std::holds_alternative<MessageData::ObjectsList>(msgDat.msg))
|
if (std::holds_alternative<MessageData::ObjectsList>(msgDat.msg))
|
||||||
{
|
{
|
||||||
editedObjs = std::get<MessageData::ObjectsList>(msgDat.msg);
|
editedObjs = std::get<MessageData::ObjectsList>(msgDat.msg);
|
||||||
@@ -148,8 +148,15 @@ void IEngineAssetEditor::undo()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
debugAssert(!editedObjs.empty());
|
debugAssert(!editedObjs.empty());
|
||||||
onObjectsEdited(editedObjs);
|
std::vector<WeakObjectPtr> weakObjPtrs{ editedObjs.size() };
|
||||||
|
for (SizeT i = 0; i < editedObjs.size(); ++i)
|
||||||
|
{
|
||||||
|
weakObjPtrs[i] = editedObjs[i];
|
||||||
|
}
|
||||||
|
onObjectsEdited(weakObjPtrs);
|
||||||
|
}
|
||||||
|
|
||||||
cbe::NotificationInfo notifyInfo{ .name = STR_FORMAT("Undo: {}", std::move(str)), .durrInSeconds = 2 };
|
cbe::NotificationInfo notifyInfo{ .name = STR_FORMAT("Undo: {}", std::move(str)), .durrInSeconds = 2 };
|
||||||
gCBEditorEngine->addNotification(notifyInfo);
|
gCBEditorEngine->addNotification(notifyInfo);
|
||||||
@@ -165,8 +172,15 @@ void IEngineAssetEditor::redo()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
debugAssert(!editedObjs.empty());
|
debugAssert(!editedObjs.empty());
|
||||||
onObjectsEdited(editedObjs);
|
std::vector<WeakObjectPtr> weakObjPtrs{ editedObjs.size() };
|
||||||
|
for (SizeT i = 0; i < editedObjs.size(); ++i)
|
||||||
|
{
|
||||||
|
weakObjPtrs[i] = editedObjs[i];
|
||||||
|
}
|
||||||
|
onObjectsEdited(weakObjPtrs);
|
||||||
|
}
|
||||||
|
|
||||||
cbe::NotificationInfo notifyInfo{ .name = STR_FORMAT("Redo: {}", std::move(str)), .durrInSeconds = 2 };
|
cbe::NotificationInfo notifyInfo{ .name = STR_FORMAT("Redo: {}", std::move(str)), .durrInSeconds = 2 };
|
||||||
gCBEditorEngine->addNotification(notifyInfo);
|
gCBEditorEngine->addNotification(notifyInfo);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date September 2025
|
* \date September 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -50,7 +50,10 @@ public:
|
|||||||
/* Shortcuts must be handled here. */
|
/* Shortcuts must be handled here. */
|
||||||
virtual void editorShortcuts() {}
|
virtual void editorShortcuts() {}
|
||||||
/* Must be called if object gets edited outside the editor itself. For example undo/redo */
|
/* Must be called if object gets edited outside the editor itself. For example undo/redo */
|
||||||
virtual void onObjectsEdited(MAYBE_UNUSED ArrayView<Object *> editedObjs) { pushMessage(CoreEdMessage_RefreshEd); }
|
virtual void onObjectsEdited(MAYBE_UNUSED ArrayView<WeakObjectPtr> editedObjs)
|
||||||
|
{
|
||||||
|
pushMessage(cbe::MessageData{ .msg = std::vector<WeakObjectPtr>(editedObjs.cbegin(), editedObjs.cend()) }, CoreEdMessage_RefreshEd);
|
||||||
|
}
|
||||||
virtual void refreshEditor() {}
|
virtual void refreshEditor() {}
|
||||||
|
|
||||||
/* Interface ends */
|
/* Interface ends */
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date October 2025
|
* \date October 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -24,6 +24,7 @@ struct MaterialTextureEntry
|
|||||||
{
|
{
|
||||||
String paramName;
|
String paramName;
|
||||||
ObjectPath texPath;
|
ObjectPath texPath;
|
||||||
|
uint32 offset;
|
||||||
};
|
};
|
||||||
struct MaterialSaveTransaction
|
struct MaterialSaveTransaction
|
||||||
{
|
{
|
||||||
@@ -51,6 +52,7 @@ static void matInstToTransaction(MaterialSaveTransaction &outTransaction, Materi
|
|||||||
outTransaction.texture2dRefs[i] = MaterialTextureEntry{
|
outTransaction.texture2dRefs[i] = MaterialTextureEntry{
|
||||||
.paramName = matInst->texture2dRefs[i].paramName,
|
.paramName = matInst->texture2dRefs[i].paramName,
|
||||||
.texPath = ObjectPath(matInst->texture2dRefs[i].texture.get()),
|
.texPath = ObjectPath(matInst->texture2dRefs[i].texture.get()),
|
||||||
|
.offset = matInst->texture2dRefs[i].layoutOffset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,6 +67,7 @@ static void transactionToMatInst(MaterialInstance *matInst, const MaterialSaveTr
|
|||||||
matInst->texture2dRefs[i] = MaterialTexture2DParam{
|
matInst->texture2dRefs[i] = MaterialTexture2DParam{
|
||||||
.paramName = transaction.texture2dRefs[i].paramName,
|
.paramName = transaction.texture2dRefs[i].paramName,
|
||||||
.texture = cast<Texture2D>(transaction.texture2dRefs[i].texPath.getObject()),
|
.texture = cast<Texture2D>(transaction.texture2dRefs[i].texPath.getObject()),
|
||||||
|
.layoutOffset = transaction.texture2dRefs[i].offset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -15,9 +15,10 @@
|
|||||||
#include <Widgets/WgEditorImGuiLayer.h>
|
#include <Widgets/WgEditorImGuiLayer.h>
|
||||||
#include <IEditorCore.h>
|
#include <IEditorCore.h>
|
||||||
/* Importers */
|
/* Importers */
|
||||||
#include <StaticMeshImporter.hpp>
|
#include <Importers/StaticMeshImporter.hpp>
|
||||||
#include <AudioImporter.hpp>
|
#include <Importers/AssimpImporter.hpp>
|
||||||
#include <Texture2DImporter.hpp>
|
#include <Importers/AudioImporter.hpp>
|
||||||
|
#include <Importers/Texture2DImporter.hpp>
|
||||||
/* Viewport drawers */
|
/* Viewport drawers */
|
||||||
#include <ViewportDrawers/TransformComponentViewportDrawer.hpp>
|
#include <ViewportDrawers/TransformComponentViewportDrawer.hpp>
|
||||||
/* Details Customizers */
|
/* Details Customizers */
|
||||||
@@ -44,6 +45,7 @@ class CBEEditorModule final : public ICBEEditor
|
|||||||
private:
|
private:
|
||||||
/* Importers */
|
/* Importers */
|
||||||
ObjStaticMeshImporter smFromObjImporter;
|
ObjStaticMeshImporter smFromObjImporter;
|
||||||
|
AssimpImporter assimpImporter;
|
||||||
|
|
||||||
StbTexture2DImporter stbTextureImporter;
|
StbTexture2DImporter stbTextureImporter;
|
||||||
|
|
||||||
@@ -82,6 +84,7 @@ void CBEEditorModule::init()
|
|||||||
IEditorCore *editorCore = ModuleManager::get()->getOrLoadModulePtr<IEditorCore>(TCHAR("EditorCore"));
|
IEditorCore *editorCore = ModuleManager::get()->getOrLoadModulePtr<IEditorCore>(TCHAR("EditorCore"));
|
||||||
debugAssert(editorCore);
|
debugAssert(editorCore);
|
||||||
editorCore->registerAssetImporter(&smFromObjImporter);
|
editorCore->registerAssetImporter(&smFromObjImporter);
|
||||||
|
editorCore->registerAssetImporter(&assimpImporter);
|
||||||
editorCore->registerAssetImporter(&stbTextureImporter);
|
editorCore->registerAssetImporter(&stbTextureImporter);
|
||||||
editorCore->registerAssetImporter(&audioModuleImporter);
|
editorCore->registerAssetImporter(&audioModuleImporter);
|
||||||
|
|
||||||
@@ -99,6 +102,7 @@ void CBEEditorModule::release()
|
|||||||
if (IEditorCore *editorCore = IEditorCore::get())
|
if (IEditorCore *editorCore = IEditorCore::get())
|
||||||
{
|
{
|
||||||
editorCore->unregisterAssetImporter(&smFromObjImporter);
|
editorCore->unregisterAssetImporter(&smFromObjImporter);
|
||||||
|
editorCore->unregisterAssetImporter(&assimpImporter);
|
||||||
editorCore->unregisterAssetImporter(&stbTextureImporter);
|
editorCore->unregisterAssetImporter(&stbTextureImporter);
|
||||||
editorCore->unregisterAssetImporter(&audioModuleImporter);
|
editorCore->unregisterAssetImporter(&audioModuleImporter);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date July 2025
|
* \date July 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -186,8 +186,6 @@ void GenericFieldCustomizer::initSelectionDetails(ClassObjectContext &inOutCntx,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Jeslas) : Right now not considering the world actors or child of selected object's actor prefab.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericFieldCustomizer::DrawFieldArgs GenericFieldCustomizer::preDrawFieldWidget(const SelectionFieldCustomizationArgs &args)
|
GenericFieldCustomizer::DrawFieldArgs GenericFieldCustomizer::preDrawFieldWidget(const SelectionFieldCustomizationArgs &args)
|
||||||
@@ -2487,8 +2485,6 @@ GenericFieldCustomizer::EEditState GenericFieldCustomizer::drawWidgetbool(Fundam
|
|||||||
return editState;
|
return editState;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr float ASSET_SELECTOR_ICON_SIZE = 40;
|
|
||||||
constexpr float ASSET_SELECTOR_ICON_FONT_SCALE = 1.5f;
|
|
||||||
GenericFieldCustomizer::EEditState GenericFieldCustomizer::drawWidgetClassPointerCntx(ClassObjectContext &cntx)
|
GenericFieldCustomizer::EEditState GenericFieldCustomizer::drawWidgetClassPointerCntx(ClassObjectContext &cntx)
|
||||||
{
|
{
|
||||||
CranberryEngineApp *application = static_cast<CranberryEngineApp *>(IApplicationModule::get()->getApplication());
|
CranberryEngineApp *application = static_cast<CranberryEngineApp *>(IApplicationModule::get()->getApplication());
|
||||||
@@ -2512,7 +2508,7 @@ GenericFieldCustomizer::EEditState GenericFieldCustomizer::drawWidgetClassPointe
|
|||||||
ImGui::BeginGroup();
|
ImGui::BeginGroup();
|
||||||
const bool bSelectionValid = !cntx.objPath.getObjectName().empty();
|
const bool bSelectionValid = !cntx.objPath.getObjectName().empty();
|
||||||
/* First draw the Icon */
|
/* First draw the Icon */
|
||||||
const Rect iconRect{ ImGui::GetCursorScreenPos(), Vector2(ImGui::GetCursorScreenPos()) + Vector2(ASSET_SELECTOR_ICON_SIZE) };
|
const Rect iconRect{ ImGui::GetCursorScreenPos(), Vector2(ImGui::GetCursorScreenPos()) + Vector2(wg_consts::ASSET_SELECTOR_ICON_SIZE) };
|
||||||
std::string_view iconTxt = "NUL";
|
std::string_view iconTxt = "NUL";
|
||||||
Color iconBgColor{ ImGui::GetColorU32(ImGuiCol_FrameBg) };
|
Color iconBgColor{ ImGui::GetColorU32(ImGuiCol_FrameBg) };
|
||||||
if (bSelectionValid)
|
if (bSelectionValid)
|
||||||
@@ -2520,19 +2516,20 @@ GenericFieldCustomizer::EEditState GenericFieldCustomizer::drawWidgetClassPointe
|
|||||||
iconBgColor = cntx.selDetails.classColor;
|
iconBgColor = cntx.selDetails.classColor;
|
||||||
iconTxt = &cntx.selDetails.classInitials[0];
|
iconTxt = &cntx.selDetails.classInitials[0];
|
||||||
}
|
}
|
||||||
EditorWidgetsHelper::drawPackageIcon(iconRect, iconTxt, iconBgColor, ASSET_SELECTOR_ICON_FONT_SCALE);
|
EditorWidgetsHelper::drawPackageIcon(iconRect, iconTxt, iconBgColor, wg_consts::ASSET_SELECTOR_ICON_FONT_SCALE);
|
||||||
|
|
||||||
/* Used to do the selection focus in the first open frame. Do before any chance of popup being open. */
|
/* Used to do the selection focus in the first open frame. Do before any chance of popup being open. */
|
||||||
const bool bPopupOpenLastFrame = cbe::ImGuiHelpers::isPopupOpen(assetListPopupId);
|
const bool bPopupOpenLastFrame = cbe::ImGuiHelpers::isPopupOpen(assetListPopupId);
|
||||||
const bool bIconClicked = ImGui::Selectable(
|
const bool bIconClicked = ImGui::Selectable(
|
||||||
"###ObjectIcon", false, ImGuiSelectableFlags_AllowDoubleClick, ImVec2(ASSET_SELECTOR_ICON_SIZE, ASSET_SELECTOR_ICON_SIZE)
|
"###ObjectIcon", false, ImGuiSelectableFlags_AllowDoubleClick,
|
||||||
|
ImVec2(wg_consts::ASSET_SELECTOR_ICON_SIZE, wg_consts::ASSET_SELECTOR_ICON_SIZE)
|
||||||
);
|
);
|
||||||
const bool bIconDoubleClicked = bIconClicked && ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left);
|
const bool bIconDoubleClicked = bIconClicked && ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left);
|
||||||
if (bIconDoubleClicked)
|
if (bIconDoubleClicked)
|
||||||
{
|
{
|
||||||
if (bSelectionValid)
|
if (bSelectionValid)
|
||||||
{
|
{
|
||||||
gCBEditorEngine->openAssetEditor(cntx.objPath.getObject());
|
gCBEditorEngine->openAssetEditor(cntx.objPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (bIconClicked)
|
else if (bIconClicked)
|
||||||
@@ -2551,157 +2548,16 @@ GenericFieldCustomizer::EEditState GenericFieldCustomizer::drawWidgetClassPointe
|
|||||||
ImGui::SetNextItemWidth(popupItemWidth - (ImGuiHelpers::calcButtonSize(mat_icon::MOP).x * 3));
|
ImGui::SetNextItemWidth(popupItemWidth - (ImGuiHelpers::calcButtonSize(mat_icon::MOP).x * 3));
|
||||||
if (ImGui::BeginCombo(assetListPopup, comboPreview, ImGuiComboFlags_HeightLarge))
|
if (ImGui::BeginCombo(assetListPopup, comboPreview, ImGuiComboFlags_HeightLarge))
|
||||||
{
|
{
|
||||||
if (!cntx.bListUptoDate)
|
const std::optional newSel
|
||||||
|
= drawWidgetClassPointerCntxMenuList(cntx, *assetManager, popupItemWidth, !bPopupOpenLastFrame && bSelectionValid);
|
||||||
|
if (newSel)
|
||||||
{
|
{
|
||||||
generateObjectListForContext(cntx, *assetManager);
|
cntx.objPath = newSel->first.getChar();
|
||||||
}
|
cntx.selDetails = cntx.objsList[newSel->second];
|
||||||
|
|
||||||
const float filterStartPos = ImGui::GetCursorStartPos().y;
|
|
||||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
|
||||||
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip);
|
|
||||||
if (cbe::ImGuiHelpers::inputTextWithHint(
|
|
||||||
"###AssetFilterText", "Filter Asset", &cntx.filter,
|
|
||||||
ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_NoHorizontalScroll
|
|
||||||
))
|
|
||||||
{
|
|
||||||
/* Scroll back to filter every time filter is modified. */
|
|
||||||
ImGui::SetScrollFromPosY(filterStartPos, 0.0f);
|
|
||||||
|
|
||||||
const String filter = cntx.filter.toLowerCopy();
|
|
||||||
for (SizeT i = 0; i < cntx.objsList.size(); ++i)
|
|
||||||
{
|
|
||||||
const ObjectPackageDetail &objDetail = cntx.objsList[i];
|
|
||||||
|
|
||||||
const AssetManager::PackageAllocator::AllocHandle &allocHnd
|
|
||||||
= *reinterpret_cast<const AssetManager::PackageAllocator::AllocHandle *>(&objDetail.packageAllocHnd);
|
|
||||||
const AssetManager::AssetPackage *assetPack = assetManager->getAssetPackage(allocHnd);
|
|
||||||
cntx.filteredBits[i] = filter.empty() || assetPack->rootObjectPath.toLowerCopy().find(filter) != String::npos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Scroll to text filter the moment it becomes active. */
|
|
||||||
if (ImGui::IsItemActivated())
|
|
||||||
{
|
|
||||||
ImGui::SetScrollFromPosY(filterStartPos, 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImDrawList *drawList = ImGui::GetWindowDrawList();
|
|
||||||
const ImGuiStyle &imguiStyle = ImGui::GetStyle();
|
|
||||||
|
|
||||||
const Vector2 listStartPos = ImGui::GetCursorScreenPos();
|
|
||||||
const Vector2 selectableSize{ popupItemWidth,
|
|
||||||
Math::max(ASSET_SELECTOR_ICON_SIZE, 2 * ImGui::GetTextLineHeight()) + (2 * imguiStyle.FramePadding.y) };
|
|
||||||
|
|
||||||
ImGuiListClipper clipper;
|
|
||||||
clipper.Begin(static_cast<int32>(cntx.filteredBits.countOnes()), selectableSize.y);
|
|
||||||
|
|
||||||
/* Try to focus to the current selections */
|
|
||||||
if (!bPopupOpenLastFrame && bSelectionValid)
|
|
||||||
{
|
|
||||||
int64 lineIdx = -1;
|
|
||||||
for (uint64 entryIdx = 0, line = 0; entryIdx < cntx.objsList.size(); ++entryIdx)
|
|
||||||
{
|
|
||||||
if (!cntx.filteredBits[entryIdx])
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ObjectPackageDetail &objDetail = cntx.objsList[entryIdx];
|
|
||||||
const bool bContentSelected = cntx.selDetails.packageAllocHnd == objDetail.packageAllocHnd;
|
|
||||||
if (bContentSelected)
|
|
||||||
{
|
|
||||||
lineIdx = std::bit_cast<int64>(line);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
line++;
|
|
||||||
}
|
|
||||||
if (lineIdx >= 0)
|
|
||||||
{
|
|
||||||
const float lineStartPosY = listStartPos.y + (static_cast<float>(lineIdx) * selectableSize.y);
|
|
||||||
/* Pos must be set in window local space. */
|
|
||||||
ImGui::SetScrollFromPosY(lineStartPosY - ImGui::GetWindowPos().y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find the first filtered index */
|
|
||||||
uint64 entryIdx = 0;
|
|
||||||
uint64 setBitIdx = 0;
|
|
||||||
for (; entryIdx < cntx.objsList.size() && !cntx.filteredBits[entryIdx]; ++entryIdx)
|
|
||||||
{}
|
|
||||||
while (clipper.Step())
|
|
||||||
{
|
|
||||||
for (int32 lineIdx = clipper.DisplayStart; lineIdx < clipper.DisplayEnd; ++lineIdx)
|
|
||||||
{
|
|
||||||
/* Iterate until reaching setBitIdx that matches line idx. entryIdx will point to actual entry in objList */
|
|
||||||
for (; entryIdx < cntx.objsList.size() && setBitIdx < static_cast<uint64>(lineIdx); ++entryIdx)
|
|
||||||
{
|
|
||||||
setBitIdx += cntx.filteredBits[entryIdx] ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ObjectPackageDetail &objDetail = cntx.objsList[entryIdx];
|
|
||||||
const AssetManager::PackageAllocator::AllocHandle &allocHnd
|
|
||||||
= *reinterpret_cast<const AssetManager::PackageAllocator::AllocHandle *>(&objDetail.packageAllocHnd);
|
|
||||||
const AssetManager::AssetPackage *assetPack = assetManager->getAssetPackage(allocHnd);
|
|
||||||
|
|
||||||
const Vector2 contentStartPos = listStartPos + Vector2(0, (static_cast<float>(lineIdx) * selectableSize.y));
|
|
||||||
|
|
||||||
ImGui::PushID(std::format("{}.{}", entryIdx, objDetail.name).c_str());
|
|
||||||
ImGui::SetCursorScreenPos(contentStartPos);
|
|
||||||
|
|
||||||
const bool bContentSelected = cntx.selDetails.packageAllocHnd == objDetail.packageAllocHnd;
|
|
||||||
const bool bContentVisible = ImGui::IsRectVisible(selectableSize);
|
|
||||||
const bool bContentClicked = ImGui::Selectable("", bContentSelected, ImGuiSelectableFlags_None, selectableSize);
|
|
||||||
if (bContentSelected)
|
|
||||||
{
|
|
||||||
ImGui::SetItemDefaultFocus();
|
|
||||||
}
|
|
||||||
if (ImGui::BeginItemTooltip())
|
|
||||||
{
|
|
||||||
ImGuiHelpers::textUnformatted(assetPack->rootObjectPath);
|
|
||||||
ImGui::EndTooltip();
|
|
||||||
}
|
|
||||||
if (bContentVisible)
|
|
||||||
{
|
|
||||||
/* Draw Package icon, center align in Y */
|
|
||||||
const float iconAlignOffsetY = (selectableSize.y - (2 * imguiStyle.FramePadding.y) - ASSET_SELECTOR_ICON_SIZE) * 0.5f;
|
|
||||||
const Vector2 iconRectStartPos = contentStartPos + Vector2(imguiStyle.FramePadding) + Vector2(0, iconAlignOffsetY);
|
|
||||||
const Vector2 iconRectEndPos = iconRectStartPos + ASSET_SELECTOR_ICON_SIZE;
|
|
||||||
cbe::EditorWidgetsHelper::drawPackageIcon(
|
|
||||||
{ iconRectStartPos, iconRectEndPos }, objDetail.classInitials, objDetail.classColor, ASSET_SELECTOR_ICON_FONT_SCALE
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Center align text */
|
|
||||||
const float labelStartX = iconRectEndPos.x + imguiStyle.ItemInnerSpacing.x;
|
|
||||||
const float labelWidth = selectableSize.x - (labelStartX - contentStartPos.x) - imguiStyle.FramePadding.x;
|
|
||||||
const float labelEndX = labelStartX + labelWidth;
|
|
||||||
const float labelHeight
|
|
||||||
= ImGui::CalcTextSize(objDetail.name.c_str(), objDetail.name.c_str() + objDetail.name.length(), false, labelWidth).y;
|
|
||||||
const float labelAlignOffsetY = (selectableSize.y - (2 * imguiStyle.FramePadding.y) - labelHeight) * 0.5f;
|
|
||||||
const float labelStartY = contentStartPos.y + imguiStyle.FramePadding.y + labelAlignOffsetY;
|
|
||||||
const float labelEndY = labelStartY + labelHeight;
|
|
||||||
|
|
||||||
const ImVec4 clipRect{
|
|
||||||
labelStartX,
|
|
||||||
labelStartY,
|
|
||||||
labelEndX,
|
|
||||||
labelEndY,
|
|
||||||
};
|
|
||||||
drawList->AddText(
|
|
||||||
ImGui::GetFont(), ImGui::GetFontSize(), ImVec2(clipRect.x, clipRect.y), ImGui::GetColorU32(ImGuiCol_Text),
|
|
||||||
objDetail.name.c_str(), objDetail.name.c_str() + objDetail.name.length(), labelWidth, &clipRect
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (bContentClicked)
|
|
||||||
{
|
|
||||||
cntx.objPath = assetPack->rootObjectPath.getChar();
|
|
||||||
cntx.selDetails = objDetail;
|
|
||||||
debugAssertf(editState == Edit_None, "Two selection cannot be made in same frame!");
|
debugAssertf(editState == Edit_None, "Two selection cannot be made in same frame!");
|
||||||
editState = Edit_Committed;
|
editState = Edit_Committed;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopID();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clipper.End();
|
|
||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
ImGui::EndGroup();
|
ImGui::EndGroup();
|
||||||
@@ -2774,6 +2630,167 @@ GenericFieldCustomizer::EEditState GenericFieldCustomizer::drawWidgetClassPointe
|
|||||||
return editState;
|
return editState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::pair<String, uint64>> GenericFieldCustomizer::drawWidgetClassPointerCntxMenuList(
|
||||||
|
ClassObjectContext &cntx, AssetManager &assetManager, float popupItemWidth, bool bFocusSelDetails
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::optional<std::pair<String, uint64>> retVal;
|
||||||
|
if (!cntx.bListUptoDate)
|
||||||
|
{
|
||||||
|
generateObjectListForContext(cntx, assetManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float filterStartPos = ImGui::GetCursorStartPos().y;
|
||||||
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||||
|
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip);
|
||||||
|
if (cbe::ImGuiHelpers::inputTextWithHint(
|
||||||
|
"###AssetFilterText", "Filter Asset", &cntx.filter,
|
||||||
|
ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_NoHorizontalScroll
|
||||||
|
))
|
||||||
|
{
|
||||||
|
/* Scroll back to filter every time filter is modified. */
|
||||||
|
ImGui::SetScrollFromPosY(filterStartPos, 0.0f);
|
||||||
|
|
||||||
|
const String filter = cntx.filter.toLowerCopy();
|
||||||
|
for (SizeT i = 0; i < cntx.objsList.size(); ++i)
|
||||||
|
{
|
||||||
|
const ObjectPackageDetail &objDetail = cntx.objsList[i];
|
||||||
|
|
||||||
|
const AssetManager::PackageAllocator::AllocHandle &allocHnd
|
||||||
|
= *reinterpret_cast<const AssetManager::PackageAllocator::AllocHandle *>(&objDetail.packageAllocHnd);
|
||||||
|
const AssetManager::AssetPackage *assetPack = assetManager.getAssetPackage(allocHnd);
|
||||||
|
cntx.filteredBits[i] = filter.empty() || assetPack->rootObjectPath.toLowerCopy().find(filter) != String::npos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Scroll to text filter the moment it becomes active. */
|
||||||
|
if (ImGui::IsItemActivated())
|
||||||
|
{
|
||||||
|
ImGui::SetScrollFromPosY(filterStartPos, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImDrawList *drawList = ImGui::GetWindowDrawList();
|
||||||
|
const ImGuiStyle &imguiStyle = ImGui::GetStyle();
|
||||||
|
|
||||||
|
const Vector2 listStartPos = ImGui::GetCursorScreenPos();
|
||||||
|
const Vector2 selectableSize{
|
||||||
|
popupItemWidth,
|
||||||
|
Math::max(wg_consts::ASSET_SELECTOR_ICON_SIZE, 2 * ImGui::GetTextLineHeight()) + (2 * imguiStyle.FramePadding.y),
|
||||||
|
};
|
||||||
|
|
||||||
|
ImGuiListClipper clipper;
|
||||||
|
clipper.Begin(static_cast<int32>(cntx.filteredBits.countOnes()), selectableSize.y);
|
||||||
|
|
||||||
|
/* Try to focus to the current selections */
|
||||||
|
if (bFocusSelDetails)
|
||||||
|
{
|
||||||
|
int64 lineIdx = -1;
|
||||||
|
for (uint64 entryIdx = 0, line = 0; entryIdx < cntx.objsList.size(); ++entryIdx)
|
||||||
|
{
|
||||||
|
if (!cntx.filteredBits[entryIdx])
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ObjectPackageDetail &objDetail = cntx.objsList[entryIdx];
|
||||||
|
const bool bContentSelected = cntx.selDetails.packageAllocHnd == objDetail.packageAllocHnd;
|
||||||
|
if (bContentSelected)
|
||||||
|
{
|
||||||
|
lineIdx = std::bit_cast<int64>(line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
if (lineIdx >= 0)
|
||||||
|
{
|
||||||
|
const float lineStartPosY = listStartPos.y + (static_cast<float>(lineIdx) * selectableSize.y);
|
||||||
|
/* Pos must be set in window local space. */
|
||||||
|
ImGui::SetScrollFromPosY(lineStartPosY - ImGui::GetWindowPos().y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the first filtered index */
|
||||||
|
uint64 entryIdx = 0;
|
||||||
|
uint64 setBitIdx = 0;
|
||||||
|
for (; entryIdx < cntx.objsList.size() && !cntx.filteredBits[entryIdx]; ++entryIdx)
|
||||||
|
{}
|
||||||
|
while (clipper.Step())
|
||||||
|
{
|
||||||
|
for (int32 lineIdx = clipper.DisplayStart; lineIdx < clipper.DisplayEnd; ++lineIdx)
|
||||||
|
{
|
||||||
|
/* Iterate until reaching setBitIdx that matches line idx. entryIdx will point to actual entry in objList */
|
||||||
|
for (; entryIdx < cntx.objsList.size() && setBitIdx < static_cast<uint64>(lineIdx); ++entryIdx)
|
||||||
|
{
|
||||||
|
setBitIdx += cntx.filteredBits[entryIdx] ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ObjectPackageDetail &objDetail = cntx.objsList[entryIdx];
|
||||||
|
const AssetManager::PackageAllocator::AllocHandle &allocHnd
|
||||||
|
= *reinterpret_cast<const AssetManager::PackageAllocator::AllocHandle *>(&objDetail.packageAllocHnd);
|
||||||
|
const AssetManager::AssetPackage *assetPack = assetManager.getAssetPackage(allocHnd);
|
||||||
|
|
||||||
|
const Vector2 contentStartPos = listStartPos + Vector2(0, (static_cast<float>(lineIdx) * selectableSize.y));
|
||||||
|
|
||||||
|
ImGui::PushID(std::format("{}.{}", entryIdx, objDetail.name).c_str());
|
||||||
|
ImGui::SetCursorScreenPos(contentStartPos);
|
||||||
|
|
||||||
|
const bool bContentSelected = cntx.selDetails.packageAllocHnd == objDetail.packageAllocHnd;
|
||||||
|
const bool bContentVisible = ImGui::IsRectVisible(selectableSize);
|
||||||
|
const bool bContentClicked = ImGui::Selectable("", bContentSelected, ImGuiSelectableFlags_None, selectableSize);
|
||||||
|
if (bContentSelected)
|
||||||
|
{
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginItemTooltip())
|
||||||
|
{
|
||||||
|
ImGuiHelpers::textUnformatted(assetPack->rootObjectPath);
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
if (bContentVisible)
|
||||||
|
{
|
||||||
|
/* Draw Package icon, center align in Y */
|
||||||
|
const float iconAlignOffsetY
|
||||||
|
= (selectableSize.y - (2 * imguiStyle.FramePadding.y) - wg_consts::ASSET_SELECTOR_ICON_SIZE) * 0.5f;
|
||||||
|
const Vector2 iconRectStartPos = contentStartPos + Vector2(imguiStyle.FramePadding) + Vector2(0, iconAlignOffsetY);
|
||||||
|
const Vector2 iconRectEndPos = iconRectStartPos + wg_consts::ASSET_SELECTOR_ICON_SIZE;
|
||||||
|
cbe::EditorWidgetsHelper::drawPackageIcon(
|
||||||
|
{ iconRectStartPos, iconRectEndPos }, objDetail.classInitials, objDetail.classColor,
|
||||||
|
wg_consts::ASSET_SELECTOR_ICON_FONT_SCALE
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Center align text, some logic can be moved out of loop */
|
||||||
|
const float labelStartX = iconRectEndPos.x + imguiStyle.ItemInnerSpacing.x;
|
||||||
|
const float labelWidth = selectableSize.x - (labelStartX - contentStartPos.x) - imguiStyle.FramePadding.x;
|
||||||
|
const float labelEndX = labelStartX + labelWidth;
|
||||||
|
const float labelHeight
|
||||||
|
= ImGui::CalcTextSize(objDetail.name.c_str(), objDetail.name.c_str() + objDetail.name.length(), false, labelWidth).y;
|
||||||
|
const float labelAlignOffsetY = (selectableSize.y - (2 * imguiStyle.FramePadding.y) - labelHeight) * 0.5f;
|
||||||
|
const float labelStartY = contentStartPos.y + imguiStyle.FramePadding.y + labelAlignOffsetY;
|
||||||
|
const float labelEndY = labelStartY + labelHeight;
|
||||||
|
|
||||||
|
const ImVec4 clipRect{
|
||||||
|
labelStartX,
|
||||||
|
labelStartY,
|
||||||
|
labelEndX,
|
||||||
|
labelEndY,
|
||||||
|
};
|
||||||
|
drawList->AddText(
|
||||||
|
ImGui::GetFont(), ImGui::GetFontSize(), ImVec2(clipRect.x, clipRect.y), ImGui::GetColorU32(ImGuiCol_Text),
|
||||||
|
objDetail.name.c_str(), objDetail.name.c_str() + objDetail.name.length(), labelWidth, &clipRect
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (bContentClicked)
|
||||||
|
{
|
||||||
|
retVal.emplace(std::make_pair(assetPack->rootObjectPath, entryIdx));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clipper.End();
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
bool GenericFieldCustomizer::drawWidgetClassObjectPath(SelectionFieldCustomizationArgs args)
|
bool GenericFieldCustomizer::drawWidgetClassObjectPath(SelectionFieldCustomizationArgs args)
|
||||||
{
|
{
|
||||||
GenericFieldCustomizer *thisPtr = static_cast<GenericFieldCustomizer *>(args.customizer);
|
GenericFieldCustomizer *thisPtr = static_cast<GenericFieldCustomizer *>(args.customizer);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date July 2025
|
* \date July 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -12,10 +12,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ClassDetailsCustomizer.hpp>
|
#include <ClassDetailsCustomizer.hpp>
|
||||||
#include <bitset>
|
#include <Widgets/WgEditorConstants.hpp>
|
||||||
#include <Memory/ArenaAllocator.h>
|
#include <Memory/ArenaAllocator.h>
|
||||||
#include <StructDetailsCustomizer.hpp>
|
#include <StructDetailsCustomizer.hpp>
|
||||||
|
|
||||||
|
#include <bitset>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace cbe
|
namespace cbe
|
||||||
{
|
{
|
||||||
class Transaction;
|
class Transaction;
|
||||||
@@ -192,7 +195,7 @@ public:
|
|||||||
std::bitset<ELEM_COUNT> multipleValues;
|
std::bitset<ELEM_COUNT> multipleValues;
|
||||||
};
|
};
|
||||||
/* Max 3 letters */
|
/* Max 3 letters */
|
||||||
constexpr static const uint32 CLASS_INITIALS_COUNT = 3;
|
constexpr static const uint32 CLASS_INITIALS_COUNT = wg_consts::CLASS_INITIALS_COUNT;
|
||||||
struct ObjectPackageDetail
|
struct ObjectPackageDetail
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -201,7 +204,7 @@ public:
|
|||||||
/* Object pointer will be valid only if the package detail is either part of world or one of sub object of actor. */
|
/* Object pointer will be valid only if the package detail is either part of world or one of sub object of actor. */
|
||||||
WeakObjectPtr objPtr;
|
WeakObjectPtr objPtr;
|
||||||
|
|
||||||
TChar classInitials[CLASS_INITIALS_COUNT + 1] = {};
|
AChar classInitials[CLASS_INITIALS_COUNT + 1] = {};
|
||||||
Color classColor;
|
Color classColor;
|
||||||
};
|
};
|
||||||
/* Same context type for Raw pointer and ObjectPtr */
|
/* Same context type for Raw pointer and ObjectPtr */
|
||||||
@@ -408,6 +411,9 @@ public:
|
|||||||
EEditState drawWidgetClassRawPointer(FieldCustomizationContext &fieldCntx, DrawFieldArgs drawArgs);
|
EEditState drawWidgetClassRawPointer(FieldCustomizationContext &fieldCntx, DrawFieldArgs drawArgs);
|
||||||
EEditState drawWidgetClassObjPointer(FieldCustomizationContext &fieldCntx, DrawFieldArgs drawArgs);
|
EEditState drawWidgetClassObjPointer(FieldCustomizationContext &fieldCntx, DrawFieldArgs drawArgs);
|
||||||
static EEditState drawWidgetClassPointerCntx(ClassObjectContext &cntx);
|
static EEditState drawWidgetClassPointerCntx(ClassObjectContext &cntx);
|
||||||
|
/* When selected returns object path and index into objsList in context passed into */
|
||||||
|
static std::optional<std::pair<String, uint64>>
|
||||||
|
drawWidgetClassPointerCntxMenuList(ClassObjectContext &cntx, AssetManager &assetManager, float popupItemWidth, bool bFocusSelDetails);
|
||||||
|
|
||||||
EEditState drawWidgetEnum(FieldCustomizationContext &fieldCntx, DrawFieldArgs drawArgs);
|
EEditState drawWidgetEnum(FieldCustomizationContext &fieldCntx, DrawFieldArgs drawArgs);
|
||||||
static EEditState drawWidgetEnum(EnumContext &cntx);
|
static EEditState drawWidgetEnum(EnumContext &cntx);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date October 2025
|
* \date October 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -43,9 +43,12 @@ bool MaterialInstanceCustomizer::shouldCustomizeObjects(ArrayView<WeakObjectPtr>
|
|||||||
struct LocalLayoutStack
|
struct LocalLayoutStack
|
||||||
{
|
{
|
||||||
gal::GPUStructLayout dataLayout;
|
gal::GPUStructLayout dataLayout;
|
||||||
uint32 fieldIdx;
|
uint32 fieldIdx = 0;
|
||||||
/* Used by the inner struct array */
|
/* Used by the inner struct array */
|
||||||
uint32 arrayIdx;
|
uint32 arrayIdx = 0;
|
||||||
|
/* Always points to start offset of this struct. Not field offset. Field offset must be added to this.
|
||||||
|
* Used only to patch material texture refs offset. */
|
||||||
|
uint32 offset = 0;
|
||||||
};
|
};
|
||||||
static void popLocalLayoutsStack(std::vector<LocalLayoutStack> &inOutLayoutsStack)
|
static void popLocalLayoutsStack(std::vector<LocalLayoutStack> &inOutLayoutsStack)
|
||||||
{
|
{
|
||||||
@@ -69,6 +72,7 @@ static void popLocalLayoutsStack(std::vector<LocalLayoutStack> &inOutLayoutsStac
|
|||||||
{
|
{
|
||||||
/* Restart the stack. */
|
/* Restart the stack. */
|
||||||
stackEntry.fieldIdx = 0;
|
stackEntry.fieldIdx = 0;
|
||||||
|
stackEntry.offset += parentField.stride;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -97,6 +101,7 @@ void MaterialInstanceCustomizer::patchMaterialInstTexturesList(MaterialInstance
|
|||||||
const gal::GPUStructLayout &layout = layoutsStack.back().dataLayout;
|
const gal::GPUStructLayout &layout = layoutsStack.back().dataLayout;
|
||||||
const uint32 fieldIdx = layoutsStack.back().fieldIdx++;
|
const uint32 fieldIdx = layoutsStack.back().fieldIdx++;
|
||||||
const gal::GPUStructField &field = layout.fields[fieldIdx];
|
const gal::GPUStructField &field = layout.fields[fieldIdx];
|
||||||
|
const uint32 fieldAbsOffset = layoutsStack.back().offset + field.offset;
|
||||||
|
|
||||||
if (field.metaData.bTextureIdx)
|
if (field.metaData.bTextureIdx)
|
||||||
{
|
{
|
||||||
@@ -110,6 +115,7 @@ void MaterialInstanceCustomizer::patchMaterialInstTexturesList(MaterialInstance
|
|||||||
MaterialTexture2DParam{
|
MaterialTexture2DParam{
|
||||||
.paramName = field.name,
|
.paramName = field.name,
|
||||||
.texture = nullptr,
|
.texture = nullptr,
|
||||||
|
.layoutOffset = fieldAbsOffset + (field.stride * i),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -120,7 +126,11 @@ void MaterialInstanceCustomizer::patchMaterialInstTexturesList(MaterialInstance
|
|||||||
{
|
{
|
||||||
auto layoutItr = allStructLayouts.find(field.metaData.structTypeName);
|
auto layoutItr = allStructLayouts.find(field.metaData.structTypeName);
|
||||||
debugAssert(layoutItr != allStructLayouts.cend());
|
debugAssert(layoutItr != allStructLayouts.cend());
|
||||||
layoutsStack.emplace_back(layoutItr->second, 0);
|
layoutsStack.emplace_back(LocalLayoutStack{
|
||||||
|
.dataLayout = layoutItr->second,
|
||||||
|
.fieldIdx = 0,
|
||||||
|
.offset = fieldAbsOffset,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
popLocalLayoutsStack(layoutsStack);
|
popLocalLayoutsStack(layoutsStack);
|
||||||
@@ -711,6 +721,7 @@ void MaterialInstanceCustomizer::generateWidgetDrawEntries(SelectionClassCustomi
|
|||||||
gal::GPUStructLayout layout = layoutsStack.back().dataLayout;
|
gal::GPUStructLayout layout = layoutsStack.back().dataLayout;
|
||||||
const uint32 fieldIdx = layoutsStack.back().fieldIdx++;
|
const uint32 fieldIdx = layoutsStack.back().fieldIdx++;
|
||||||
const gal::GPUStructField &field = layout.fields[fieldIdx];
|
const gal::GPUStructField &field = layout.fields[fieldIdx];
|
||||||
|
const uint32 fieldAbsOffset = layoutsStack.back().offset + field.offset;
|
||||||
|
|
||||||
if (field.metaData.bTextureIdx)
|
if (field.metaData.bTextureIdx)
|
||||||
{
|
{
|
||||||
@@ -727,7 +738,11 @@ void MaterialInstanceCustomizer::generateWidgetDrawEntries(SelectionClassCustomi
|
|||||||
{
|
{
|
||||||
auto layoutItr = allStructLayouts.find(field.metaData.structTypeName);
|
auto layoutItr = allStructLayouts.find(field.metaData.structTypeName);
|
||||||
debugAssert(layoutItr != allStructLayouts.cend());
|
debugAssert(layoutItr != allStructLayouts.cend());
|
||||||
layoutsStack.emplace_back(layoutItr->second, 0, 0);
|
layoutsStack.emplace_back(LocalLayoutStack{
|
||||||
|
.dataLayout = layoutItr->second,
|
||||||
|
.fieldIdx = 0,
|
||||||
|
.offset = fieldAbsOffset,
|
||||||
|
});
|
||||||
|
|
||||||
WidgetEntry &entry = outInfo.widgetDrawEntries.emplace_back(WidgetEntry{
|
WidgetEntry &entry = outInfo.widgetDrawEntries.emplace_back(WidgetEntry{
|
||||||
.customData = (0ull << 32ull) | fieldIdx,
|
.customData = (0ull << 32ull) | fieldIdx,
|
||||||
@@ -781,6 +796,8 @@ void MaterialInstanceCustomizer::generateWidgetDrawEntries(SelectionClassCustomi
|
|||||||
{
|
{
|
||||||
/* Insert next array entry begin and restart the stack. */
|
/* Insert next array entry begin and restart the stack. */
|
||||||
stackEntry.fieldIdx = 0;
|
stackEntry.fieldIdx = 0;
|
||||||
|
stackEntry.offset += parentField.stride;
|
||||||
|
|
||||||
outInfo.widgetDrawEntries.emplace_back(WidgetEntry{
|
outInfo.widgetDrawEntries.emplace_back(WidgetEntry{
|
||||||
.customData = (static_cast<uint64>(stackEntry.arrayIdx) << 32ull) | parentFieldIdx,
|
.customData = (static_cast<uint64>(stackEntry.arrayIdx) << 32ull) | parentFieldIdx,
|
||||||
.name = parentField.name,
|
.name = parentField.name,
|
||||||
@@ -933,6 +950,7 @@ void MaterialInstanceCustomizer::fillCustomizationCntxFromMat(ClassCustomization
|
|||||||
const gal::GPUStructLayout &layout = layoutsStack.back().dataLayout;
|
const gal::GPUStructLayout &layout = layoutsStack.back().dataLayout;
|
||||||
const uint32 fieldIdx = layoutsStack.back().fieldIdx++;
|
const uint32 fieldIdx = layoutsStack.back().fieldIdx++;
|
||||||
const gal::GPUStructField &field = layout.fields[fieldIdx];
|
const gal::GPUStructField &field = layout.fields[fieldIdx];
|
||||||
|
const uint32 fieldAbsOffset = layoutsStack.back().offset + field.offset;
|
||||||
|
|
||||||
if (field.metaData.bTextureIdx)
|
if (field.metaData.bTextureIdx)
|
||||||
{
|
{
|
||||||
@@ -951,7 +969,11 @@ void MaterialInstanceCustomizer::fillCustomizationCntxFromMat(ClassCustomization
|
|||||||
{
|
{
|
||||||
auto layoutItr = allStructLayouts.find(field.metaData.structTypeName);
|
auto layoutItr = allStructLayouts.find(field.metaData.structTypeName);
|
||||||
debugAssert(layoutItr != allStructLayouts.cend());
|
debugAssert(layoutItr != allStructLayouts.cend());
|
||||||
layoutsStack.emplace_back(layoutItr->second, 0);
|
layoutsStack.emplace_back(LocalLayoutStack{
|
||||||
|
.dataLayout = layoutItr->second,
|
||||||
|
.fieldIdx = 0,
|
||||||
|
.offset = fieldAbsOffset,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
popLocalLayoutsStack(layoutsStack);
|
popLocalLayoutsStack(layoutsStack);
|
||||||
@@ -974,6 +996,7 @@ void MaterialInstanceCustomizer::setCustomizationCntxToMat(const ClassCustomizat
|
|||||||
const gal::GPUStructLayout &layout = layoutsStack.back().dataLayout;
|
const gal::GPUStructLayout &layout = layoutsStack.back().dataLayout;
|
||||||
const uint32 fieldIdx = layoutsStack.back().fieldIdx++;
|
const uint32 fieldIdx = layoutsStack.back().fieldIdx++;
|
||||||
const gal::GPUStructField &field = layout.fields[fieldIdx];
|
const gal::GPUStructField &field = layout.fields[fieldIdx];
|
||||||
|
const uint32 fieldAbsOffset = layoutsStack.back().offset + field.offset;
|
||||||
|
|
||||||
if (field.metaData.bTextureIdx)
|
if (field.metaData.bTextureIdx)
|
||||||
{
|
{
|
||||||
@@ -992,7 +1015,11 @@ void MaterialInstanceCustomizer::setCustomizationCntxToMat(const ClassCustomizat
|
|||||||
{
|
{
|
||||||
auto layoutItr = allStructLayouts.find(field.metaData.structTypeName);
|
auto layoutItr = allStructLayouts.find(field.metaData.structTypeName);
|
||||||
debugAssert(layoutItr != allStructLayouts.cend());
|
debugAssert(layoutItr != allStructLayouts.cend());
|
||||||
layoutsStack.emplace_back(layoutItr->second, 0);
|
layoutsStack.emplace_back(LocalLayoutStack{
|
||||||
|
.dataLayout = layoutItr->second,
|
||||||
|
.fieldIdx = 0,
|
||||||
|
.offset = fieldAbsOffset,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
popLocalLayoutsStack(layoutsStack);
|
popLocalLayoutsStack(layoutsStack);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date July 2025
|
* \date July 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -139,7 +139,7 @@ bool TransformComponentCustomizer::drawTransform3dWidget(SelectionClassCustomiza
|
|||||||
}
|
}
|
||||||
|
|
||||||
debugAssert(args.ownerEd != nullptr);
|
debugAssert(args.ownerEd != nullptr);
|
||||||
args.ownerEd->pushMessage(
|
args.ownerEd->pushMessageOverwrite(
|
||||||
cbe::MessageData{
|
cbe::MessageData{
|
||||||
.issuerData = std::bit_cast<uint64>(args.drawer),
|
.issuerData = std::bit_cast<uint64>(args.drawer),
|
||||||
.msg = EmptyType{},
|
.msg = EmptyType{},
|
||||||
|
|||||||
1381
Source/Editor/CBEEditor/Private/Importers/AssimpImporter.cpp
Normal file
1381
Source/Editor/CBEEditor/Private/Importers/AssimpImporter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
145
Source/Editor/CBEEditor/Private/Importers/AssimpImporter.hpp
Normal file
145
Source/Editor/CBEEditor/Private/Importers/AssimpImporter.hpp
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
/*!
|
||||||
|
* \file AssimpImporter.hpp
|
||||||
|
*
|
||||||
|
* \author Jeslas Pravin
|
||||||
|
* \date February 2026
|
||||||
|
* \copyright
|
||||||
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
|
* License can be read in LICENSE file at this repository's root
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AssetImporter.hpp>
|
||||||
|
#include <Math/CoreMathTypes.h>
|
||||||
|
|
||||||
|
#include "AssimpImporter.gen.hpp"
|
||||||
|
|
||||||
|
struct AssimpImporterContext;
|
||||||
|
|
||||||
|
namespace Assimp
|
||||||
|
{
|
||||||
|
class Importer;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AssimpTextureOptions
|
||||||
|
{
|
||||||
|
GENERATED_CODES()
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bFlipY = false;
|
||||||
|
|
||||||
|
} META_ANNOTATE(NoExport);
|
||||||
|
|
||||||
|
struct AssimpImportOptions
|
||||||
|
{
|
||||||
|
GENERATED_CODES()
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bImportAsScene = false;
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
float scale = 1.0f;
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bFromYUp = true;
|
||||||
|
|
||||||
|
/* If the mesh is already left handed */
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bIsLeftHanded = false;
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bFlipUvs = true;
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bFlipWindingOrder = true;
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bFixInwardsNormals = false;
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bSmoothNormals = false;
|
||||||
|
META_ANNOTATE()
|
||||||
|
float smoothingAngle = 35.0f;
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bRemoveDegenerates = false;
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bKeepAreaDegenerates = false;
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bMergeMeshes = false;
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bMergeMaterials = false;
|
||||||
|
|
||||||
|
/* Skips both texture and materials */
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bSkipMaterials = false;
|
||||||
|
|
||||||
|
/* Skips both texture and materials */
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bSkipSkeletons = false;
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bSkipNonMeshNodes = true;
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
AssimpTextureOptions textureOptions;
|
||||||
|
|
||||||
|
} META_ANNOTATE(NoExport);
|
||||||
|
|
||||||
|
struct AssimpPerMeshOptions
|
||||||
|
{
|
||||||
|
GENERATED_CODES()
|
||||||
|
|
||||||
|
/* Use heuristics to get this name which will end up being the name of the mesh. */
|
||||||
|
META_ANNOTATE()
|
||||||
|
String meshName;
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bSkipImport;
|
||||||
|
|
||||||
|
} META_ANNOTATE(NoExport);
|
||||||
|
|
||||||
|
struct AssimpContextOptions
|
||||||
|
{
|
||||||
|
GENERATED_CODES()
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
String sceneName;
|
||||||
|
|
||||||
|
META_ANNOTATE()
|
||||||
|
std::vector<AssimpPerMeshOptions> meshes;
|
||||||
|
|
||||||
|
} META_ANNOTATE(NoExport);
|
||||||
|
|
||||||
|
class AssimpImporter : public AssetImporterBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AssimpImporter();
|
||||||
|
MAKE_TYPE_NONCOPY_NONMOVE(AssimpImporter)
|
||||||
|
~AssimpImporter();
|
||||||
|
|
||||||
|
/* AssetImporterBase overrides */
|
||||||
|
public:
|
||||||
|
bool supportsImporting(ImportOption &inOutOptions) final;
|
||||||
|
ImportContext prepareContext(ImportOption &inOutOptions) final;
|
||||||
|
ImportResult importAssets(const ImportOption &importOptions, const ImportContext &cntx) const final;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void destructImportContext(void *cntx) final;
|
||||||
|
/* Override ends */
|
||||||
|
|
||||||
|
public:
|
||||||
|
AssimpImportOptions options;
|
||||||
|
|
||||||
|
LibHandle assimpLibHnd;
|
||||||
|
Assimp::Importer *assimpImporter;
|
||||||
|
/* For progress reporting */
|
||||||
|
uint32 ppStepsCount = 16;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void processAiScene(AssimpImporterContext *cntx, ImportOption &inOutOptions) const;
|
||||||
|
};
|
||||||
@@ -4,12 +4,12 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date March 2025
|
* \date March 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AudioImporter.hpp>
|
#include <Importers/AudioImporter.hpp>
|
||||||
#include <Classes/AudioSource.hpp>
|
#include <Classes/AudioSource.hpp>
|
||||||
#include <ObjectPathHelpers.h>
|
#include <ObjectPathHelpers.h>
|
||||||
#include <EditorHelpers.h>
|
#include <EditorHelpers.h>
|
||||||
@@ -31,6 +31,8 @@ bool AudioModuleDecodedImporter::supportsImporting(ImportOption &inOutOptions)
|
|||||||
inOutOptions.optionsStruct = &options;
|
inOutOptions.optionsStruct = &options;
|
||||||
inOutOptions.optionsStructType = AudioImportOptions::staticType();
|
inOutOptions.optionsStructType = AudioImportOptions::staticType();
|
||||||
|
|
||||||
|
inOutOptions.prepareProgressCount = 3;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -54,6 +56,10 @@ AssetImporterBase::ImportContext AudioModuleDecodedImporter::prepareContext(Impo
|
|||||||
audio_module_decoded::ImportContext *cntx = new audio_module_decoded::ImportContext();
|
audio_module_decoded::ImportContext *cntx = new audio_module_decoded::ImportContext();
|
||||||
AssetImporterBase::ImportContext ret{ cntx, ImportContextDtor{ this } };
|
AssetImporterBase::ImportContext ret{ cntx, ImportContextDtor{ this } };
|
||||||
|
|
||||||
|
if (inOutOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
inOutOptions.progressCb(1, TCHAR("Read file"));
|
||||||
|
}
|
||||||
std::vector<uint8> data;
|
std::vector<uint8> data;
|
||||||
if (!FileHelper::readBytes(data, inOutOptions.filePath))
|
if (!FileHelper::readBytes(data, inOutOptions.filePath))
|
||||||
{
|
{
|
||||||
@@ -80,6 +86,11 @@ AssetImporterBase::ImportContext AudioModuleDecodedImporter::prepareContext(Impo
|
|||||||
encoding = cbe::EAudioSrcEncoding::Vorbis;
|
encoding = cbe::EAudioSrcEncoding::Vorbis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inOutOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
inOutOptions.progressCb(1, TCHAR("Decode audio"));
|
||||||
|
}
|
||||||
|
|
||||||
ICbeAudioModule *audioModule = ICbeAudioModule::get();
|
ICbeAudioModule *audioModule = ICbeAudioModule::get();
|
||||||
const cbe::audio::DecodeInfo decodeInfo{ .sourceData = data, .encoding = encoding };
|
const cbe::audio::DecodeInfo decodeInfo{ .sourceData = data, .encoding = encoding };
|
||||||
cbe::audio::DecodedOutput decodedInfo;
|
cbe::audio::DecodedOutput decodedInfo;
|
||||||
@@ -107,6 +118,11 @@ AssetImporterBase::ImportContext AudioModuleDecodedImporter::prepareContext(Impo
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inOutOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
inOutOptions.progressCb(1, TCHAR("Setup Context"));
|
||||||
|
}
|
||||||
|
|
||||||
cntx->data = std::move(decodedInfo.pcmFrames.empty() ? data : decodedInfo.pcmFrames);
|
cntx->data = std::move(decodedInfo.pcmFrames.empty() ? data : decodedInfo.pcmFrames);
|
||||||
cntx->createInfo = {
|
cntx->createInfo = {
|
||||||
.encodedData = cntx->data,
|
.encodedData = cntx->data,
|
||||||
@@ -123,6 +139,7 @@ AssetImporterBase::ImportContext AudioModuleDecodedImporter::prepareContext(Impo
|
|||||||
};
|
};
|
||||||
inOutOptions.cntxOptionsStruct = &cntx->cntxOptions;
|
inOutOptions.cntxOptionsStruct = &cntx->cntxOptions;
|
||||||
inOutOptions.cntxOptionsStructType = audio_module_decoded::ContextOptions::staticType();
|
inOutOptions.cntxOptionsStructType = audio_module_decoded::ContextOptions::staticType();
|
||||||
|
inOutOptions.importProgressCount = 1;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -137,6 +154,11 @@ AssetImporterBase::ImportResult AudioModuleDecodedImporter::importAssets(const I
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (importOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
importOptions.progressCb(1, TCHAR("Importing"));
|
||||||
|
}
|
||||||
|
|
||||||
/* Now create the package and the asset */
|
/* Now create the package and the asset */
|
||||||
String packageRelPath = importOptions.relativeDirPath + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR;
|
String packageRelPath = importOptions.relativeDirPath + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR;
|
||||||
packageRelPath.append(importOptions.engName);
|
packageRelPath.append(importOptions.engName);
|
||||||
@@ -151,58 +173,3 @@ AssetImporterBase::ImportResult AudioModuleDecodedImporter::importAssets(const I
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AudioModuleDecodedImporter::destructImportContext(void *cntx) { delete std::bit_cast<audio_module_decoded::ImportContext *>(cntx); }
|
void AudioModuleDecodedImporter::destructImportContext(void *cntx) { delete std::bit_cast<audio_module_decoded::ImportContext *>(cntx); }
|
||||||
|
|
||||||
std::optional<std::vector<cbe::Object *>> AudioModuleDecodedImporter::tryImporting(const ImportOption &importOptions) const
|
|
||||||
{
|
|
||||||
std::vector<uint8> data;
|
|
||||||
if (FileHelper::readBytes(data, importOptions.filePath))
|
|
||||||
{
|
|
||||||
const String extension = importOptions.fileExt;
|
|
||||||
cbe::EAudioSrcEncoding encoding = cbe::EAudioSrcEncoding::Unknown;
|
|
||||||
if (extension.isEqual(TCHAR("WAV"), false))
|
|
||||||
{
|
|
||||||
encoding = cbe::EAudioSrcEncoding::Wav;
|
|
||||||
}
|
|
||||||
else if (extension.isEqual(TCHAR("MP3"), false))
|
|
||||||
{
|
|
||||||
encoding = cbe::EAudioSrcEncoding::Mp3;
|
|
||||||
}
|
|
||||||
else if (extension.isEqual(TCHAR("FLAC"), false))
|
|
||||||
{
|
|
||||||
encoding = cbe::EAudioSrcEncoding::Flac;
|
|
||||||
}
|
|
||||||
else if (extension.isEqual(TCHAR("OGG"), false))
|
|
||||||
{
|
|
||||||
encoding = cbe::EAudioSrcEncoding::Vorbis;
|
|
||||||
}
|
|
||||||
|
|
||||||
ICbeAudioModule *audioModule = ICbeAudioModule::get();
|
|
||||||
const cbe::audio::DecodeInfo decodeInfo{ .sourceData = data, .encoding = encoding };
|
|
||||||
const cbe::audio::DecodedOutput decodedInfo = audioModule->decodeAudioInfo(decodeInfo);
|
|
||||||
if (decodedInfo.dataFormat == cbe::audio::ESampleDataFormat::Invalid)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
cbe::AudioSourceCreateInfo createInfo{
|
|
||||||
.encodedData = std::move(data),
|
|
||||||
.framesCount = decodedInfo.framesCount,
|
|
||||||
.sampleRate = decodedInfo.sampleRate,
|
|
||||||
.dataFormat = decodedInfo.dataFormat,
|
|
||||||
.encoding = encoding,
|
|
||||||
.numOfChannels = decodedInfo.numChannels,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Now create the package and the asset */
|
|
||||||
String packageRelPath = importOptions.relativeDirPath + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR;
|
|
||||||
packageRelPath.append(importOptions.engName);
|
|
||||||
|
|
||||||
EditorHelpers::makePackageUnique(packageRelPath);
|
|
||||||
|
|
||||||
std::vector<cbe::Object *> audioSrcs{
|
|
||||||
EditorHelpers::createAudioSource(packageRelPath, importOptions.importContentPath, importOptions.engName, std::move(createInfo))
|
|
||||||
};
|
|
||||||
return audioSrcs;
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date March 2025
|
* \date March 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -58,8 +58,6 @@ public:
|
|||||||
ImportContext prepareContext(ImportOption &inOutOptions) final;
|
ImportContext prepareContext(ImportOption &inOutOptions) final;
|
||||||
ImportResult importAssets(const ImportOption &importOptions, const ImportContext &cntx) const final;
|
ImportResult importAssets(const ImportOption &importOptions, const ImportContext &cntx) const final;
|
||||||
|
|
||||||
std::optional<std::vector<cbe::Object *>> tryImporting(const ImportOption &importOptions) const override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void destructImportContext(void *cntx) final;
|
void destructImportContext(void *cntx) final;
|
||||||
/* Override ends */
|
/* Override ends */
|
||||||
@@ -4,12 +4,12 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <StaticMeshImporter.hpp>
|
#include <Importers/StaticMeshImporter.hpp>
|
||||||
#include <Types/Platform/LFS/PathFunctions.h>
|
#include <Types/Platform/LFS/PathFunctions.h>
|
||||||
#include <CBEPackage.hpp>
|
#include <CBEPackage.hpp>
|
||||||
#include <CBEObjectHelpers.h>
|
#include <CBEObjectHelpers.h>
|
||||||
@@ -62,16 +62,16 @@ static void printErrors(uint32 errorCount, EImportErrorCodes errorCode)
|
|||||||
switch (errorCode)
|
switch (errorCode)
|
||||||
{
|
{
|
||||||
case DegenerateTextureCoords:
|
case DegenerateTextureCoords:
|
||||||
LOG_WARN("ObjStaticMeshImporter", "Incorrect texture coordinate, using world x, y as tangents[{}]", errorCount);
|
CBE_LOG_WARN("ObjStaticMeshImporter", "Incorrect texture coordinate, using world x, y as tangents[{}]", errorCount);
|
||||||
break;
|
break;
|
||||||
case DegenerateNormal:
|
case DegenerateNormal:
|
||||||
LOG_WARN("ObjStaticMeshImporter", "Degenerate normals, Tangents might be invalid. Expect visual artifacts[{}]", errorCount);
|
CBE_LOG_WARN("ObjStaticMeshImporter", "Degenerate normals, Tangents might be invalid. Expect visual artifacts[{}]", errorCount);
|
||||||
break;
|
break;
|
||||||
case DegenerateTriangle:
|
case DegenerateTriangle:
|
||||||
LOG_WARN("ObjStaticMeshImporter", "Degenerate triangles found and they are removed[{}]", errorCount);
|
CBE_LOG_WARN("ObjStaticMeshImporter", "Degenerate triangles found and they are removed[{}]", errorCount);
|
||||||
break;
|
break;
|
||||||
case InvalidFace:
|
case InvalidFace:
|
||||||
LOG_WARN("ObjStaticMeshImporter", "Invalid face/index data found and they are removed[{}]", errorCount);
|
CBE_LOG_WARN("ObjStaticMeshImporter", "Invalid face/index data found and they are removed[{}]", errorCount);
|
||||||
break;
|
break;
|
||||||
case ErrorsCount:
|
case ErrorsCount:
|
||||||
default:
|
default:
|
||||||
@@ -203,11 +203,13 @@ static void calcTangent(
|
|||||||
|
|
||||||
static void transformVertices(tinyobj::attrib_t &attrib, const StaticMeshImportOptions &options)
|
static void transformVertices(tinyobj::attrib_t &attrib, const StaticMeshImportOptions &options)
|
||||||
{
|
{
|
||||||
|
const uint64 vertEndIdx = attrib.vertices.size() / 3;
|
||||||
|
const uint64 normEndIdx = attrib.normals.size() / 3;
|
||||||
if (options.bFromYUp)
|
if (options.bFromYUp)
|
||||||
{
|
{
|
||||||
|
/* Rotating 90.0 is equivalent to doing v.y = -v.z and v.z = v.y followed by negating to flip along Y Axis to left hand space. */
|
||||||
static const Quat ROTATION_Y2Z_UP = Quat(90.f, Vector3::FWD);
|
static const Quat ROTATION_Y2Z_UP = Quat(90.f, Vector3::FWD);
|
||||||
|
|
||||||
const uint64 vertEndIdx = attrib.vertices.size() / 3;
|
|
||||||
for (uint64 vertIdx = 0; vertIdx != vertEndIdx; ++vertIdx)
|
for (uint64 vertIdx = 0; vertIdx != vertEndIdx; ++vertIdx)
|
||||||
{
|
{
|
||||||
const uint64 vertXIdx = vertIdx * 3;
|
const uint64 vertXIdx = vertIdx * 3;
|
||||||
@@ -218,7 +220,6 @@ static void transformVertices(tinyobj::attrib_t &attrib, const StaticMeshImportO
|
|||||||
attrib.vertices[vertXIdx + 2] = v.z;
|
attrib.vertices[vertXIdx + 2] = v.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint64 normEndIdx = attrib.normals.size() / 3;
|
|
||||||
for (uint64 normIdx = 0; normIdx != normEndIdx; ++normIdx)
|
for (uint64 normIdx = 0; normIdx != normEndIdx; ++normIdx)
|
||||||
{
|
{
|
||||||
const uint64 normXIdx = normIdx * 3;
|
const uint64 normXIdx = normIdx * 3;
|
||||||
@@ -230,6 +231,21 @@ static void transformVertices(tinyobj::attrib_t &attrib, const StaticMeshImportO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!options.bIsLeftHanded)
|
||||||
|
{
|
||||||
|
/* Left Handedness: Flip along Y */
|
||||||
|
for (uint64 vertIdx = 0; vertIdx != vertEndIdx; ++vertIdx)
|
||||||
|
{
|
||||||
|
const uint64 vertXIdx = vertIdx * 3;
|
||||||
|
attrib.vertices[vertXIdx + 1] *= -1.f;
|
||||||
|
}
|
||||||
|
for (uint64 normIdx = 0; normIdx != normEndIdx; ++normIdx)
|
||||||
|
{
|
||||||
|
const uint64 normXIdx = normIdx * 3;
|
||||||
|
attrib.normals[normXIdx + 1] *= -1.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!Math::isEqual(options.scale, 1.0f))
|
if (!Math::isEqual(options.scale, 1.0f))
|
||||||
{
|
{
|
||||||
for (float &elem : attrib.vertices)
|
for (float &elem : attrib.vertices)
|
||||||
@@ -238,6 +254,14 @@ static void transformVertices(tinyobj::attrib_t &attrib, const StaticMeshImportO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static void transformIndices(ArrayRange<tinyobj::index_t> idxs, const StaticMeshImportOptions &options)
|
||||||
|
{
|
||||||
|
if (!options.bIsLeftHanded)
|
||||||
|
{
|
||||||
|
/* Left Handedness: Reverse the winding */
|
||||||
|
std::reverse(idxs.begin(), idxs.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void fillVertexInfo(ImporterSMVertex &vertexData, const tinyobj::attrib_t &attrib, const tinyobj::index_t &index)
|
static void fillVertexInfo(ImporterSMVertex &vertexData, const tinyobj::attrib_t &attrib, const tinyobj::index_t &index)
|
||||||
{
|
{
|
||||||
@@ -245,13 +269,15 @@ static void fillVertexInfo(ImporterSMVertex &vertexData, const tinyobj::attrib_t
|
|||||||
Vector3 normal = {};
|
Vector3 normal = {};
|
||||||
if (index.texcoord_index != -1)
|
if (index.texcoord_index != -1)
|
||||||
{
|
{
|
||||||
// Inverting Y since UV origin is at left bottom of image and Graphics API's UV origin is at left top
|
uvCoord = { attrib.texcoords[(index.texcoord_index * 2u) + 0u], attrib.texcoords[(index.texcoord_index * 2u) + 1u] };
|
||||||
uvCoord = { attrib.texcoords[(index.texcoord_index * 2u) + 0u], (1.0f - attrib.texcoords[(index.texcoord_index * 2u) + 1u]) };
|
|
||||||
}
|
}
|
||||||
if (index.normal_index != -1)
|
if (index.normal_index != -1)
|
||||||
{
|
{
|
||||||
normal = { attrib.normals[(index.normal_index * 3u) + 0u], attrib.normals[(index.normal_index * 3u) + 1u],
|
normal = {
|
||||||
attrib.normals[(index.normal_index * 3u) + 2u] };
|
attrib.normals[(index.normal_index * 3u) + 0u],
|
||||||
|
attrib.normals[(index.normal_index * 3u) + 1u],
|
||||||
|
attrib.normals[(index.normal_index * 3u) + 2u],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
uvCoord = Math::clamp(uvCoord, Vector2::ZERO, Vector2::ONE);
|
uvCoord = Math::clamp(uvCoord, Vector2::ZERO, Vector2::ONE);
|
||||||
@@ -355,9 +381,12 @@ static void load(
|
|||||||
{
|
{
|
||||||
debugAssert(FACE_MAX_VERTS == 3 && FACE_MAX_VERTS == mesh.mesh.num_face_vertices[faceIdx]);
|
debugAssert(FACE_MAX_VERTS == 3 && FACE_MAX_VERTS == mesh.mesh.num_face_vertices[faceIdx]);
|
||||||
|
|
||||||
const std::array<tinyobj::index_t, FACE_MAX_VERTS> idxs
|
std::array<tinyobj::index_t, FACE_MAX_VERTS> idxs = {
|
||||||
= { mesh.mesh.indices[(faceIdx * FACE_MAX_VERTS) + 0u], mesh.mesh.indices[(faceIdx * FACE_MAX_VERTS) + 1u],
|
mesh.mesh.indices[(faceIdx * FACE_MAX_VERTS) + 0u],
|
||||||
mesh.mesh.indices[(faceIdx * FACE_MAX_VERTS) + 2u] };
|
mesh.mesh.indices[(faceIdx * FACE_MAX_VERTS) + 1u],
|
||||||
|
mesh.mesh.indices[(faceIdx * FACE_MAX_VERTS) + 2u],
|
||||||
|
};
|
||||||
|
transformIndices(idxs, outImportData.options);
|
||||||
if (idxs[0].vertex_index == -1 || idxs[1].vertex_index == -1 || idxs[2].vertex_index == -1)
|
if (idxs[0].vertex_index == -1 || idxs[1].vertex_index == -1 || idxs[2].vertex_index == -1)
|
||||||
{
|
{
|
||||||
outImportData.errorsCounter[EImportErrorCodes::InvalidFace]++;
|
outImportData.errorsCounter[EImportErrorCodes::InvalidFace]++;
|
||||||
@@ -481,9 +510,12 @@ static void smoothAndLoad(
|
|||||||
{
|
{
|
||||||
debugAssert(FACE_MAX_VERTS == 3 && FACE_MAX_VERTS == mesh.mesh.num_face_vertices[faceIdx]);
|
debugAssert(FACE_MAX_VERTS == 3 && FACE_MAX_VERTS == mesh.mesh.num_face_vertices[faceIdx]);
|
||||||
|
|
||||||
const std::array<tinyobj::index_t, FACE_MAX_VERTS> idxs
|
std::array<tinyobj::index_t, FACE_MAX_VERTS> idxs = {
|
||||||
= { mesh.mesh.indices[(faceIdx * FACE_MAX_VERTS) + 0u], mesh.mesh.indices[(faceIdx * FACE_MAX_VERTS) + 1u],
|
mesh.mesh.indices[(faceIdx * FACE_MAX_VERTS) + 0u],
|
||||||
mesh.mesh.indices[(faceIdx * FACE_MAX_VERTS) + 2u] };
|
mesh.mesh.indices[(faceIdx * FACE_MAX_VERTS) + 1u],
|
||||||
|
mesh.mesh.indices[(faceIdx * FACE_MAX_VERTS) + 2u],
|
||||||
|
};
|
||||||
|
transformIndices(idxs, outImportData.options);
|
||||||
if (idxs[0].vertex_index == -1 || idxs[1].vertex_index == -1 || idxs[2].vertex_index == -1)
|
if (idxs[0].vertex_index == -1 || idxs[1].vertex_index == -1 || idxs[2].vertex_index == -1)
|
||||||
{
|
{
|
||||||
outImportData.errorsCounter[EImportErrorCodes::InvalidFace]++;
|
outImportData.errorsCounter[EImportErrorCodes::InvalidFace]++;
|
||||||
@@ -759,6 +791,7 @@ bool ObjStaticMeshImporter::supportsImporting(ImportOption &inOutOptions)
|
|||||||
{
|
{
|
||||||
inOutOptions.optionsStruct = &options;
|
inOutOptions.optionsStruct = &options;
|
||||||
inOutOptions.optionsStructType = StaticMeshImportOptions::staticType();
|
inOutOptions.optionsStructType = StaticMeshImportOptions::staticType();
|
||||||
|
inOutOptions.prepareProgressCount = 6;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -770,6 +803,11 @@ AssetImporterBase::ImportContext ObjStaticMeshImporter::prepareContext(ImportOpt
|
|||||||
obj_sm::ImportContext *cntx = new obj_sm::ImportContext();
|
obj_sm::ImportContext *cntx = new obj_sm::ImportContext();
|
||||||
ImportContext ret{ cntx, ImportContextDtor{ this } };
|
ImportContext ret{ cntx, ImportContextDtor{ this } };
|
||||||
|
|
||||||
|
if (inOutOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
inOutOptions.progressCb(1, TCHAR("Load Obj"));
|
||||||
|
}
|
||||||
|
|
||||||
tinyobj::attrib_t attrib;
|
tinyobj::attrib_t attrib;
|
||||||
std::vector<tinyobj::shape_t> meshes;
|
std::vector<tinyobj::shape_t> meshes;
|
||||||
std::vector<tinyobj::material_t> materials;
|
std::vector<tinyobj::material_t> materials;
|
||||||
@@ -782,7 +820,7 @@ AssetImporterBase::ImportContext ObjStaticMeshImporter::prepareContext(ImportOpt
|
|||||||
);
|
);
|
||||||
if (!warning.empty())
|
if (!warning.empty())
|
||||||
{
|
{
|
||||||
LOG_WARN("ObjStaticMeshImporter", "Tiny obj loader {}", UTF8_TO_TCHAR(warning));
|
CBE_LOG_WARN("ObjStaticMeshImporter", "Tiny obj loader {}", UTF8_TO_TCHAR(warning));
|
||||||
}
|
}
|
||||||
if (!error.empty())
|
if (!error.empty())
|
||||||
{
|
{
|
||||||
@@ -801,8 +839,16 @@ AssetImporterBase::ImportContext ObjStaticMeshImporter::prepareContext(ImportOpt
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inOutOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
inOutOptions.progressCb(1, TCHAR("Transform vertices"));
|
||||||
|
}
|
||||||
obj_sm::transformVertices(attrib, options);
|
obj_sm::transformVertices(attrib, options);
|
||||||
|
|
||||||
|
if (inOutOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
inOutOptions.progressCb(1, TCHAR("Load to intermediate"));
|
||||||
|
}
|
||||||
obj_sm::IntermediateImportData meshIntermediate;
|
obj_sm::IntermediateImportData meshIntermediate;
|
||||||
meshIntermediate.options = options;
|
meshIntermediate.options = options;
|
||||||
CBEMemory::memZero(&meshIntermediate.errorsCounter, sizeof(meshIntermediate.errorsCounter));
|
CBEMemory::memZero(&meshIntermediate.errorsCounter, sizeof(meshIntermediate.errorsCounter));
|
||||||
@@ -810,12 +856,13 @@ AssetImporterBase::ImportContext ObjStaticMeshImporter::prepareContext(ImportOpt
|
|||||||
{
|
{
|
||||||
const bool hasSmoothing = obj_sm::hasSmoothedNormals(mesh);
|
const bool hasSmoothing = obj_sm::hasSmoothedNormals(mesh);
|
||||||
|
|
||||||
if (options.bLoadSmoothed && !hasSmoothing)
|
if (options.bSmoothNormals && !hasSmoothing)
|
||||||
{
|
{
|
||||||
obj_sm::smoothAndLoad(meshIntermediate, mesh, attrib, materials);
|
obj_sm::smoothAndLoad(meshIntermediate, mesh, attrib, materials);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
obj_sm::load(meshIntermediate, mesh, attrib, materials);
|
obj_sm::load(meshIntermediate, mesh, attrib, materials);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -824,6 +871,11 @@ AssetImporterBase::ImportContext ObjStaticMeshImporter::prepareContext(ImportOpt
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inOutOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
inOutOptions.progressCb(1, TCHAR("Normalize vertices"));
|
||||||
|
}
|
||||||
// Normalizing all the vertex normals
|
// Normalizing all the vertex normals
|
||||||
for (ImporterSMVertex &vertex : meshIntermediate.vertices)
|
for (ImporterSMVertex &vertex : meshIntermediate.vertices)
|
||||||
{
|
{
|
||||||
@@ -841,7 +893,7 @@ AssetImporterBase::ImportContext ObjStaticMeshImporter::prepareContext(ImportOpt
|
|||||||
}
|
}
|
||||||
if (bHadAnyErrors)
|
if (bHadAnyErrors)
|
||||||
{
|
{
|
||||||
LOG_WARN("ObjStaticMeshImporter", "Errors when loading mesh {}", inOutOptions.filePath);
|
CBE_LOG_WARN("ObjStaticMeshImporter", "Errors when loading mesh {}", inOutOptions.filePath);
|
||||||
for (uint32 i = 0; i != obj_sm::ErrorsCount; ++i)
|
for (uint32 i = 0; i != obj_sm::ErrorsCount; ++i)
|
||||||
{
|
{
|
||||||
if (meshIntermediate.errorsCounter[i] > 0)
|
if (meshIntermediate.errorsCounter[i] > 0)
|
||||||
@@ -851,6 +903,10 @@ AssetImporterBase::ImportContext ObjStaticMeshImporter::prepareContext(ImportOpt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inOutOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
inOutOptions.progressCb(1, TCHAR("Gather CreateInfos"));
|
||||||
|
}
|
||||||
/* Split each mesh and it's vertices */
|
/* Split each mesh and it's vertices */
|
||||||
std::unordered_map<String, obj_sm::CreateData> createInfoSMs;
|
std::unordered_map<String, obj_sm::CreateData> createInfoSMs;
|
||||||
createInfoSMs.reserve(meshIntermediate.loadedMeshes.size());
|
createInfoSMs.reserve(meshIntermediate.loadedMeshes.size());
|
||||||
@@ -906,6 +962,10 @@ AssetImporterBase::ImportContext ObjStaticMeshImporter::prepareContext(ImportOpt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inOutOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
inOutOptions.progressCb(1, TCHAR("Setup Context"));
|
||||||
|
}
|
||||||
/* Fill the context information and context options */
|
/* Fill the context information and context options */
|
||||||
cntx->sms.reserve(createInfoSMs.size());
|
cntx->sms.reserve(createInfoSMs.size());
|
||||||
cntx->contextOpts.importingMeshes.reserve(createInfoSMs.size());
|
cntx->contextOpts.importingMeshes.reserve(createInfoSMs.size());
|
||||||
@@ -919,6 +979,9 @@ AssetImporterBase::ImportContext ObjStaticMeshImporter::prepareContext(ImportOpt
|
|||||||
}
|
}
|
||||||
inOutOptions.cntxOptionsStruct = &cntx->contextOpts;
|
inOutOptions.cntxOptionsStruct = &cntx->contextOpts;
|
||||||
inOutOptions.cntxOptionsStructType = obj_sm::ContextOptions::staticType();
|
inOutOptions.cntxOptionsStructType = obj_sm::ContextOptions::staticType();
|
||||||
|
inOutOptions.importProgressCount = static_cast<uint32>(createInfoSMs.size());
|
||||||
|
/* +1 scene creation */
|
||||||
|
inOutOptions.importProgressCount += 1;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,6 +1021,11 @@ AssetImporterBase::ImportResult ObjStaticMeshImporter::importAssets(const Import
|
|||||||
|
|
||||||
const String &meshName = cntxPtr->contextOpts.importingMeshes[i].meshName;
|
const String &meshName = cntxPtr->contextOpts.importingMeshes[i].meshName;
|
||||||
|
|
||||||
|
if (importOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
importOptions.progressCb(1, STR_FORMAT("Create mesh {}", meshName));
|
||||||
|
}
|
||||||
|
|
||||||
String packageName = packageRelDir + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR + meshName;
|
String packageName = packageRelDir + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR + meshName;
|
||||||
importedObjs.emplace_back(createStaticMesh(packageName, meshName, std::move(cntxPtr->sms[i].smCi)));
|
importedObjs.emplace_back(createStaticMesh(packageName, meshName, std::move(cntxPtr->sms[i].smCi)));
|
||||||
Transform3D actorTf;
|
Transform3D actorTf;
|
||||||
@@ -976,6 +1044,11 @@ AssetImporterBase::ImportResult ObjStaticMeshImporter::importAssets(const Import
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (importOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
importOptions.progressCb(1, STR_FORMAT("Create mesh {}", i));
|
||||||
|
}
|
||||||
|
|
||||||
importedObjs.emplace_back(createStaticMesh(packageName, fileName, std::move(cntxPtr->sms[i].smCi)));
|
importedObjs.emplace_back(createStaticMesh(packageName, fileName, std::move(cntxPtr->sms[i].smCi)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -983,6 +1056,11 @@ AssetImporterBase::ImportResult ObjStaticMeshImporter::importAssets(const Import
|
|||||||
|
|
||||||
if (options.bImportAsScene && !importedObjs.empty())
|
if (options.bImportAsScene && !importedObjs.empty())
|
||||||
{
|
{
|
||||||
|
if (importOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
importOptions.progressCb(1, TCHAR("Create world"));
|
||||||
|
}
|
||||||
|
|
||||||
const String packageName = importOptions.relativeDirPath + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR + fileName;
|
const String packageName = importOptions.relativeDirPath + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR + fileName;
|
||||||
cbe::Package *worldPackage = cbe::Package::createPackage(packageName, importOptions.importContentPath, false);
|
cbe::Package *worldPackage = cbe::Package::createPackage(packageName, importOptions.importContentPath, false);
|
||||||
debugAssert(worldPackage);
|
debugAssert(worldPackage);
|
||||||
@@ -999,200 +1077,15 @@ AssetImporterBase::ImportResult ObjStaticMeshImporter::importAssets(const Import
|
|||||||
|
|
||||||
importedObjs.insert(importedObjs.begin(), world);
|
importedObjs.insert(importedObjs.begin(), world);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (importOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
importOptions.progressCb(1, TCHAR(""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ImportSuccess{ .objs = std::move(importedObjs) };
|
return ImportSuccess{ .objs = std::move(importedObjs) };
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjStaticMeshImporter::destructImportContext(void *cntx) { delete std::bit_cast<obj_sm::ImportContext *>(cntx); }
|
void ObjStaticMeshImporter::destructImportContext(void *cntx) { delete std::bit_cast<obj_sm::ImportContext *>(cntx); }
|
||||||
|
|
||||||
std::optional<std::vector<cbe::Object *>> ObjStaticMeshImporter::tryImporting(const ImportOption &importOptions) const
|
|
||||||
{
|
|
||||||
tinyobj::attrib_t attrib;
|
|
||||||
std::vector<tinyobj::shape_t> meshes;
|
|
||||||
std::vector<tinyobj::material_t> materials;
|
|
||||||
|
|
||||||
std::string warning;
|
|
||||||
std::string error;
|
|
||||||
const String fileDir = importOptions.fileDirectory;
|
|
||||||
const bool bIsSuccessful = tinyobj::LoadObj(
|
|
||||||
&attrib, &meshes, &materials, &warning, &error, TCHAR_TO_ANSI(importOptions.filePath).data(), TCHAR_TO_ANSI(fileDir).data()
|
|
||||||
);
|
|
||||||
if (!warning.empty())
|
|
||||||
{
|
|
||||||
LOG_WARN("ObjStaticMeshImporter", "Tiny obj loader {}", UTF8_TO_TCHAR(warning));
|
|
||||||
}
|
|
||||||
if (!error.empty())
|
|
||||||
{
|
|
||||||
LOG_ERROR("ObjStaticMeshImporter", "Tiny obj loader {}", UTF8_TO_TCHAR(error));
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (!bIsSuccessful)
|
|
||||||
{
|
|
||||||
LOG_ERROR("ObjStaticMeshImporter", "Loading {} with ObjStaticMeshImporter failed!", importOptions.filePath);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (meshes.empty())
|
|
||||||
{
|
|
||||||
LOG_WARN("ObjStaticMeshImporter", "No mesh found while loading {} with ObjStaticMeshImporter!", importOptions.filePath);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
obj_sm::transformVertices(attrib, options);
|
|
||||||
|
|
||||||
obj_sm::IntermediateImportData meshIntermediate;
|
|
||||||
meshIntermediate.options = options;
|
|
||||||
CBEMemory::memZero(&meshIntermediate.errorsCounter, sizeof(meshIntermediate.errorsCounter));
|
|
||||||
for (const tinyobj::shape_t &mesh : meshes)
|
|
||||||
{
|
|
||||||
const bool hasSmoothing = obj_sm::hasSmoothedNormals(mesh);
|
|
||||||
|
|
||||||
if (options.bLoadSmoothed && !hasSmoothing)
|
|
||||||
{
|
|
||||||
obj_sm::smoothAndLoad(meshIntermediate, mesh, attrib, materials);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
obj_sm::load(meshIntermediate, mesh, attrib, materials);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(options.bImportAllMesh || options.bImportAsScene))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Normalizing all the vertex normals
|
|
||||||
for (ImporterSMVertex &vertex : meshIntermediate.vertices)
|
|
||||||
{
|
|
||||||
vertex.normal = vertex.normal.normalized();
|
|
||||||
}
|
|
||||||
// Print errors
|
|
||||||
bool bHadAnyErrors = false;
|
|
||||||
for (uint32 i = 0; i != obj_sm::ErrorsCount; ++i)
|
|
||||||
{
|
|
||||||
if (meshIntermediate.errorsCounter[i] > 0)
|
|
||||||
{
|
|
||||||
bHadAnyErrors = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bHadAnyErrors)
|
|
||||||
{
|
|
||||||
LOG_WARN("ObjStaticMeshImporter", "Errors when loading mesh {}", importOptions.filePath);
|
|
||||||
for (uint32 i = 0; i != obj_sm::ErrorsCount; ++i)
|
|
||||||
{
|
|
||||||
if (meshIntermediate.errorsCounter[i] > 0)
|
|
||||||
{
|
|
||||||
obj_sm::printErrors(meshIntermediate.errorsCounter[i], obj_sm::EImportErrorCodes(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Split each mesh and it's vertices */
|
|
||||||
std::unordered_map<String, obj_sm::CreateData> createInfoSMs;
|
|
||||||
createInfoSMs.reserve(meshIntermediate.loadedMeshes.size());
|
|
||||||
for (std::pair<const String, obj_sm::PerMeshData> &meshIntermData : meshIntermediate.loadedMeshes)
|
|
||||||
{
|
|
||||||
obj_sm::CreateData &createData = createInfoSMs[PropertyHelper::getValidSymbolName(meshIntermData.first)];
|
|
||||||
cbe::SMCreateInfo &createInfo = createData.smCi;
|
|
||||||
/* Offset the origin of each mesh to match the bounds center. */
|
|
||||||
createData.shiftedOrigin = meshIntermData.second.bound.center();
|
|
||||||
/* Shift all vertices and bounds */
|
|
||||||
if (createData.shiftedOrigin.sqrSize() > 0.0f)
|
|
||||||
{
|
|
||||||
meshIntermData.second.bound.maxBound -= createData.shiftedOrigin;
|
|
||||||
meshIntermData.second.bound.minBound -= createData.shiftedOrigin;
|
|
||||||
|
|
||||||
for (uint32 vertIdx = meshIntermData.second.verticesRange.minBound; vertIdx < meshIntermData.second.verticesRange.maxBound;
|
|
||||||
++vertIdx)
|
|
||||||
{
|
|
||||||
meshIntermediate.vertices[vertIdx].position -= createData.shiftedOrigin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createInfo.meshBatches = std::move(meshIntermData.second.meshBatches);
|
|
||||||
createInfo.bounds = meshIntermData.second.bound;
|
|
||||||
createInfo.tbnVerts = std::move(meshIntermData.second.tbnVerts);
|
|
||||||
|
|
||||||
/* Assign indices and vertices per mesh after converting intermediate vertices in to per mesh vertices */
|
|
||||||
createInfo.indices.reserve(meshIntermData.second.indices.size());
|
|
||||||
std::unordered_map<uint32, uint32> vertIntermIdxToMeshIdx;
|
|
||||||
for (const uint32 vertIdx : meshIntermData.second.indices)
|
|
||||||
{
|
|
||||||
auto meshVertIdxItr = vertIntermIdxToMeshIdx.find(vertIdx);
|
|
||||||
if (meshVertIdxItr == vertIntermIdxToMeshIdx.cend())
|
|
||||||
{
|
|
||||||
const uint32 index = static_cast<uint32>(createInfo.vertsS0.size());
|
|
||||||
vertIntermIdxToMeshIdx[vertIdx] = index;
|
|
||||||
|
|
||||||
createInfo.indices.emplace_back(index);
|
|
||||||
|
|
||||||
GalStaticMeshStream0 vert0;
|
|
||||||
GalStaticMeshStream1 vert1;
|
|
||||||
std::get<"position">(vert0) = meshIntermediate.vertices[vertIdx].position;
|
|
||||||
std::get<"normal">(vert1) = meshIntermediate.vertices[vertIdx].normal;
|
|
||||||
std::get<"tangent">(vert1) = meshIntermediate.vertices[vertIdx].tangent;
|
|
||||||
std::get<"texCoord">(vert1) = meshIntermediate.vertices[vertIdx].texCoord;
|
|
||||||
createInfo.vertsS0.emplace_back(vert0);
|
|
||||||
createInfo.vertsS1.emplace_back(vert1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
createInfo.indices.emplace_back(meshVertIdxItr->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<cbe::Object *> importedObjs;
|
|
||||||
/* Will be used only if importing as scene. */
|
|
||||||
std::vector<Transform3D> importedActorTfs;
|
|
||||||
|
|
||||||
auto createStaticMesh = [&importOptions](String &packageName, const String &meshName, cbe::SMCreateInfo &&createInfo) -> cbe::StaticMesh *
|
|
||||||
{
|
|
||||||
EditorHelpers::makePackageUnique(packageName);
|
|
||||||
|
|
||||||
return EditorHelpers::createStaticMesh(packageName, importOptions.importContentPath, meshName, std::move(createInfo));
|
|
||||||
};
|
|
||||||
const String fileName = importOptions.engName;
|
|
||||||
if (options.bImportAsScene || options.bImportAllMesh)
|
|
||||||
{
|
|
||||||
importedObjs.reserve(createInfoSMs.size());
|
|
||||||
importedActorTfs.reserve(createInfoSMs.size());
|
|
||||||
|
|
||||||
const String packageRelDir = importOptions.relativeDirPath + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR + fileName;
|
|
||||||
for (std::pair<const String, obj_sm::CreateData> &smCI : createInfoSMs)
|
|
||||||
{
|
|
||||||
String packageName = packageRelDir + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR + smCI.first;
|
|
||||||
importedObjs.emplace_back(createStaticMesh(packageName, smCI.first, std::move(smCI.second.smCi)));
|
|
||||||
Transform3D actorTf;
|
|
||||||
actorTf.setTranslation(smCI.second.shiftedOrigin);
|
|
||||||
importedActorTfs.emplace_back(actorTf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
debugAssert(createInfoSMs.size() == 1);
|
|
||||||
String packageName = importOptions.relativeDirPath + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR + fileName;
|
|
||||||
|
|
||||||
importedObjs.emplace_back(createStaticMesh(packageName, fileName, std::move(createInfoSMs.begin()->second.smCi)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.bImportAsScene && !importedObjs.empty())
|
|
||||||
{
|
|
||||||
const String packageName = importOptions.relativeDirPath + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR + fileName;
|
|
||||||
cbe::Package *worldPackage = cbe::Package::createPackage(packageName, importOptions.importContentPath, false);
|
|
||||||
debugAssert(worldPackage);
|
|
||||||
cbe::markPackageDirty(worldPackage);
|
|
||||||
cbe::INTERNAL_ObjectCoreAccessors::addFlags(worldPackage, cbe::EObjectFlagBits::ObjFlag_PackageLoaded);
|
|
||||||
|
|
||||||
std::vector<cbe::StaticMesh *> staticMeshes;
|
|
||||||
staticMeshes.resize(importedObjs.size());
|
|
||||||
CBEMemory::memCopy(staticMeshes.data(), importedObjs.data(), sizeof(cbe::StaticMesh *) * importedObjs.size());
|
|
||||||
|
|
||||||
cbe::World *world = cbe::create<cbe::World>(fileName, worldPackage, cbe::EObjectFlagBits::ObjFlag_PackageLoaded);
|
|
||||||
cbe::Actor *rootActor = EditorHelpers::addStaticMeshesToWorld(staticMeshes, importedActorTfs, world, fileName + TCHAR("Root"));
|
|
||||||
debugAssert(world && rootActor);
|
|
||||||
|
|
||||||
importedObjs.insert(importedObjs.begin(), world);
|
|
||||||
}
|
|
||||||
return importedObjs;
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -35,7 +35,11 @@ struct StaticMeshImportOptions
|
|||||||
bool bImportAllMesh = false;
|
bool bImportAllMesh = false;
|
||||||
|
|
||||||
META_ANNOTATE()
|
META_ANNOTATE()
|
||||||
bool bLoadSmoothed = false;
|
bool bSmoothNormals = false;
|
||||||
|
|
||||||
|
/* If the mesh is already left handed */
|
||||||
|
META_ANNOTATE()
|
||||||
|
bool bIsLeftHanded = false;
|
||||||
|
|
||||||
META_ANNOTATE()
|
META_ANNOTATE()
|
||||||
float smoothingAngle = 35.0f;
|
float smoothingAngle = 35.0f;
|
||||||
@@ -83,8 +87,6 @@ public:
|
|||||||
ImportContext prepareContext(ImportOption &inOutOptions) final;
|
ImportContext prepareContext(ImportOption &inOutOptions) final;
|
||||||
ImportResult importAssets(const ImportOption &importOptions, const ImportContext &cntx) const final;
|
ImportResult importAssets(const ImportOption &importOptions, const ImportContext &cntx) const final;
|
||||||
|
|
||||||
std::optional<std::vector<cbe::Object *>> tryImporting(const ImportOption &importOptions) const override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void destructImportContext(void *cntx) final;
|
void destructImportContext(void *cntx) final;
|
||||||
/* Override ends */
|
/* Override ends */
|
||||||
@@ -4,12 +4,12 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date June 2025
|
* \date June 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Texture2DImporter.hpp>
|
#include <Importers/Texture2DImporter.hpp>
|
||||||
#include <Types/Platform/LFS/File/FileHelper.h>
|
#include <Types/Platform/LFS/File/FileHelper.h>
|
||||||
#include <Logger/Logger.h>
|
#include <Logger/Logger.h>
|
||||||
#include <ThirdParties/StbWrapper.hpp>
|
#include <ThirdParties/StbWrapper.hpp>
|
||||||
@@ -83,7 +83,7 @@ static bool isNormalTexture(const uint8 *texels, UShort2 textureDimension, uint8
|
|||||||
if (Math::abs(rgMaxLum - 127.5f) < 17.5f && blueMaxLum > 200)
|
if (Math::abs(rgMaxLum - 127.5f) < 17.5f && blueMaxLum > 200)
|
||||||
{
|
{
|
||||||
isNormal = true;
|
isNormal = true;
|
||||||
LOG_VERBOSE(
|
CBE_LOG_VERBOSE(
|
||||||
"Texture2DImporter",
|
"Texture2DImporter",
|
||||||
"Texture {} with Max Red Green lum {} Max RG weight {:0.3}, Max Blue lum {} Max B weight {:0.3} is determined as normal texture",
|
"Texture {} with Max Red Green lum {} Max RG weight {:0.3}, Max Blue lum {} Max B weight {:0.3} is determined as normal texture",
|
||||||
fileName, rgMaxLum, rgMaxWeight, blueMaxLum, blueMaxWeight
|
fileName, rgMaxLum, rgMaxWeight, blueMaxLum, blueMaxWeight
|
||||||
@@ -93,7 +93,7 @@ static bool isNormalTexture(const uint8 *texels, UShort2 textureDimension, uint8
|
|||||||
if (!isNormal && fileName.ends_with(TCHAR("_N")))
|
if (!isNormal && fileName.ends_with(TCHAR("_N")))
|
||||||
{
|
{
|
||||||
isNormal = true;
|
isNormal = true;
|
||||||
LOG_DEBUG(
|
CBE_LOG_DEBUG(
|
||||||
"Texture2DImporter", "Texture {} is determined as normal texture based on suffix _N, Please rename texture if not intended",
|
"Texture2DImporter", "Texture {} is determined as normal texture based on suffix _N, Please rename texture if not intended",
|
||||||
fileName
|
fileName
|
||||||
);
|
);
|
||||||
@@ -111,6 +111,7 @@ bool StbTexture2DImporter::supportsImporting(ImportOption &inOutOptions)
|
|||||||
{
|
{
|
||||||
inOutOptions.optionsStruct = &options;
|
inOutOptions.optionsStruct = &options;
|
||||||
inOutOptions.optionsStructType = Texture2DImportOptions::staticType();
|
inOutOptions.optionsStructType = Texture2DImportOptions::staticType();
|
||||||
|
inOutOptions.prepareProgressCount = 3;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -139,6 +140,10 @@ AssetImporterBase::ImportContext StbTexture2DImporter::prepareContext(ImportOpti
|
|||||||
stb_texture2d::ImportContext *cntx = new stb_texture2d::ImportContext();
|
stb_texture2d::ImportContext *cntx = new stb_texture2d::ImportContext();
|
||||||
AssetImporterBase::ImportContext ret{ cntx, ImportContextDtor{ this } };
|
AssetImporterBase::ImportContext ret{ cntx, ImportContextDtor{ this } };
|
||||||
|
|
||||||
|
if (inOutOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
inOutOptions.progressCb(1, TCHAR("Read file"));
|
||||||
|
}
|
||||||
std::vector<uint8> data;
|
std::vector<uint8> data;
|
||||||
if (!FileHelper::readBytes(data, inOutOptions.filePath))
|
if (!FileHelper::readBytes(data, inOutOptions.filePath))
|
||||||
{
|
{
|
||||||
@@ -146,6 +151,11 @@ AssetImporterBase::ImportContext StbTexture2DImporter::prepareContext(ImportOpti
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inOutOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
inOutOptions.progressCb(1, TCHAR("Import image"));
|
||||||
|
}
|
||||||
|
|
||||||
int32 channelsCount = 0;
|
int32 channelsCount = 0;
|
||||||
int32 dimX = 0;
|
int32 dimX = 0;
|
||||||
int32 dimY = 0;
|
int32 dimY = 0;
|
||||||
@@ -163,6 +173,11 @@ AssetImporterBase::ImportContext StbTexture2DImporter::prepareContext(ImportOpti
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inOutOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
inOutOptions.progressCb(1, TCHAR("Setup Context"));
|
||||||
|
}
|
||||||
|
|
||||||
cntx->channelsCount = channelsCount;
|
cntx->channelsCount = channelsCount;
|
||||||
cntx->dim = { static_cast<uint16>(dimX), static_cast<uint16>(dimY) };
|
cntx->dim = { static_cast<uint16>(dimX), static_cast<uint16>(dimY) };
|
||||||
cntx->data = texelData;
|
cntx->data = texelData;
|
||||||
@@ -172,6 +187,7 @@ AssetImporterBase::ImportContext StbTexture2DImporter::prepareContext(ImportOpti
|
|||||||
|
|
||||||
inOutOptions.cntxOptionsStruct = &cntx->contextOpts;
|
inOutOptions.cntxOptionsStruct = &cntx->contextOpts;
|
||||||
inOutOptions.cntxOptionsStructType = stb_texture2d::ContextOptions::staticType();
|
inOutOptions.cntxOptionsStructType = stb_texture2d::ContextOptions::staticType();
|
||||||
|
inOutOptions.importProgressCount = 2;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -186,6 +202,11 @@ AssetImporterBase::ImportResult StbTexture2DImporter::importAssets(const ImportO
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (importOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
importOptions.progressCb(1, TCHAR("Copy data"));
|
||||||
|
}
|
||||||
|
|
||||||
cbe::Texture2DCreateInfo createInfo;
|
cbe::Texture2DCreateInfo createInfo;
|
||||||
createInfo.dim = Math::clamp(Short2(cntxPtr->contextOpts.dimensionX, cntxPtr->contextOpts.dimensionY), Short2(1), cntxPtr->dim);
|
createInfo.dim = Math::clamp(Short2(cntxPtr->contextOpts.dimensionX, cntxPtr->contextOpts.dimensionY), Short2(1), cntxPtr->dim);
|
||||||
createInfo.numOfChannels = stb_texture2d::CHANNEL_NUM;
|
createInfo.numOfChannels = stb_texture2d::CHANNEL_NUM;
|
||||||
@@ -212,6 +233,11 @@ AssetImporterBase::ImportResult StbTexture2DImporter::importAssets(const ImportO
|
|||||||
|
|
||||||
stb::deallocStbBuffer(cntxPtr->data);
|
stb::deallocStbBuffer(cntxPtr->data);
|
||||||
|
|
||||||
|
if (importOptions.progressCb.isBound())
|
||||||
|
{
|
||||||
|
importOptions.progressCb(1, TCHAR("Create texture"));
|
||||||
|
}
|
||||||
|
|
||||||
/* Now create the package and the asset */
|
/* Now create the package and the asset */
|
||||||
String packageRelPath = importOptions.relativeDirPath + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR;
|
String packageRelPath = importOptions.relativeDirPath + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR;
|
||||||
packageRelPath.append(importOptions.engName);
|
packageRelPath.append(importOptions.engName);
|
||||||
@@ -225,66 +251,3 @@ AssetImporterBase::ImportResult StbTexture2DImporter::importAssets(const ImportO
|
|||||||
}
|
}
|
||||||
|
|
||||||
void StbTexture2DImporter::destructImportContext(void *cntx) { delete std::bit_cast<stb_texture2d::ImportContext *>(cntx); }
|
void StbTexture2DImporter::destructImportContext(void *cntx) { delete std::bit_cast<stb_texture2d::ImportContext *>(cntx); }
|
||||||
|
|
||||||
std::optional<std::vector<cbe::Object *>> StbTexture2DImporter::tryImporting(const ImportOption &importOptions) const
|
|
||||||
{
|
|
||||||
std::vector<uint8> data;
|
|
||||||
if (FileHelper::readBytes(data, importOptions.filePath))
|
|
||||||
{
|
|
||||||
int32 channelsCount;
|
|
||||||
int32 dimX;
|
|
||||||
int32 dimY;
|
|
||||||
|
|
||||||
if (options.bFlipY)
|
|
||||||
{
|
|
||||||
stb::setLoadVerticalFlipped(true);
|
|
||||||
}
|
|
||||||
uint8 *texelData = stb::loadFromMemory(data, &dimX, &dimY, &channelsCount, stb_texture2d::CHANNEL_NUM);
|
|
||||||
stb::setLoadVerticalFlipped(false);
|
|
||||||
|
|
||||||
if (texelData == nullptr)
|
|
||||||
{
|
|
||||||
LOG_ERROR("Texture2DImporter", "Failed loading image[{}] - {}", importOptions.filePath, UTF8_TO_TCHAR(stb::lastFailure()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cbe::Texture2DCreateInfo createInfo;
|
|
||||||
createInfo.dim = { static_cast<uint16>(dimX), static_cast<uint16>(dimY) };
|
|
||||||
createInfo.numOfChannels = stb_texture2d::CHANNEL_NUM;
|
|
||||||
createInfo.bIsNormal = isNormalTexture(texelData, createInfo.dim, stb_texture2d::CHANNEL_NUM, importOptions.engName);
|
|
||||||
|
|
||||||
/* TODO(Jeslas) : Generate mips */
|
|
||||||
createInfo.dataPerMip.resize(1);
|
|
||||||
const int32 pixelsCount = dimY * dimX;
|
|
||||||
createInfo.dataPerMip[0].resize(pixelsCount * stb_texture2d::CHANNEL_NUM);
|
|
||||||
memcpy(createInfo.dataPerMip[0].data(), texelData, pixelsCount * stb_texture2d::CHANNEL_NUM);
|
|
||||||
|
|
||||||
/* If normal we are inverting x value to account for flip of texture in u channel along tangent axis */
|
|
||||||
// TODO(Jeslas) : not exactly sure why I did that verify before committing.
|
|
||||||
// The normals without this inversion seems to be better though.
|
|
||||||
// if (createInfo.bIsNormal)
|
|
||||||
//{
|
|
||||||
// for (int32 i = 0; i < pixelsCount; ++i)
|
|
||||||
// {
|
|
||||||
// const SizeT redCompIdx = (i * stb_texture2d::CHANNEL_NUM) + 0;
|
|
||||||
// createInfo.dataPerMip[0][redCompIdx]
|
|
||||||
// = static_cast<uint8>(Math::clamp(255 - createInfo.dataPerMip[0][redCompIdx], 0, 255));
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
stb::deallocStbBuffer(texelData);
|
|
||||||
|
|
||||||
/* Now create the package and the asset */
|
|
||||||
String packageRelPath = importOptions.relativeDirPath + ObjectPathHelper::OBJECT_OBJECT_SEPARATOR;
|
|
||||||
packageRelPath.append(importOptions.engName);
|
|
||||||
|
|
||||||
EditorHelpers::makePackageUnique(packageRelPath);
|
|
||||||
|
|
||||||
std::vector<cbe::Object *> textures{
|
|
||||||
EditorHelpers::createTexture2D(packageRelPath, importOptions.importContentPath, importOptions.engName, std::move(createInfo))
|
|
||||||
};
|
|
||||||
return textures;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date June 2025
|
* \date June 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -57,8 +57,6 @@ public:
|
|||||||
ImportContext prepareContext(ImportOption &inOutOptions) final;
|
ImportContext prepareContext(ImportOption &inOutOptions) final;
|
||||||
ImportResult importAssets(const ImportOption &importOptions, const ImportContext &cntx) const final;
|
ImportResult importAssets(const ImportOption &importOptions, const ImportContext &cntx) const final;
|
||||||
|
|
||||||
std::optional<std::vector<cbe::Object *>> tryImporting(const ImportOption &importOptions) const final;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void destructImportContext(void *cntx) final;
|
void destructImportContext(void *cntx) final;
|
||||||
/* Overrides ends */
|
/* Overrides ends */
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*!
|
||||||
|
* \file EditorDragPayloads.cpp
|
||||||
|
*
|
||||||
|
* \author Subity
|
||||||
|
* \date January 2026
|
||||||
|
* \copyright
|
||||||
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
|
* License can be read in LICENSE file at this repository's root
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Payloads/EditorDragPayloads.hpp>
|
||||||
|
#include <Components/ComponentBase.hpp>
|
||||||
|
|
||||||
|
namespace cbe
|
||||||
|
{
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// ComponentDragPayload Implementation
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
ComponentDragPayload::ComponentDragPayload(Object *obj, uint64 userData)
|
||||||
|
: objPtr(obj)
|
||||||
|
, uData(userData)
|
||||||
|
, bLogicComp(PropertyHelper::isChildOf(obj->getType(), cbe::LogicComponent::staticType()))
|
||||||
|
{}
|
||||||
|
|
||||||
|
String ComponentDragPayload::getTitle() const
|
||||||
|
{
|
||||||
|
return STR_FORMAT("Component: {}", objPtr.isValid() ? objPtr->getObjectData().name : TCHAR("NULL"));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cbe
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/*!
|
||||||
|
* \file EditorDragPayloads.hpp
|
||||||
|
*
|
||||||
|
* \author Subity
|
||||||
|
* \date January 2026
|
||||||
|
* \copyright
|
||||||
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
|
* License can be read in LICENSE file at this repository's root
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Widgets/DragPayload.hpp>
|
||||||
|
#include <ObjectPtrs.h>
|
||||||
|
|
||||||
|
namespace cbe
|
||||||
|
{
|
||||||
|
class ComponentDragPayload : public IRttiDragPayload
|
||||||
|
{
|
||||||
|
CBE_SIMPLE_RTTI_CLASS(ComponentDragPayload, IRttiDragPayload)
|
||||||
|
|
||||||
|
public:
|
||||||
|
ComponentDragPayload(Object *obj, uint64 userData);
|
||||||
|
|
||||||
|
Object *getObject() const { return objPtr.get(); }
|
||||||
|
bool isLogicComponent() const { return bLogicComp; }
|
||||||
|
uint64 getUserData() const { return uData; }
|
||||||
|
|
||||||
|
/* DragPayload overrides */
|
||||||
|
|
||||||
|
protected:
|
||||||
|
String getTitle() const override;
|
||||||
|
|
||||||
|
/* Overrides ends */
|
||||||
|
private:
|
||||||
|
WeakObjectPtr objPtr;
|
||||||
|
uint64 uData;
|
||||||
|
bool bLogicComp;
|
||||||
|
};
|
||||||
|
} // namespace cbe
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date December 2025
|
* \date December 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -58,9 +58,15 @@ ViewportDrawResponse TrasformComponentViewportDrawer::drawObjContext(DrawInput i
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransformComponentDrawContext &cntx = static_cast<TransformComponentDrawContext &>(input.objCntx);
|
TransformComponentDrawContext &cntx = static_cast<TransformComponentDrawContext &>(input.objCntx);
|
||||||
|
cbe::TransformComponent *tfComponent = static_cast<cbe::TransformComponent *>(cntx.getObject());
|
||||||
|
if (tfComponent == nullptr)
|
||||||
|
{
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
input.imDd.beginSelectionProxy(&cntx);
|
input.imDd.beginSelectionProxy(&cntx);
|
||||||
|
|
||||||
Transform3D tf = static_cast<cbe::TransformComponent *>(cntx.getObject())->getWorldTransform();
|
Transform3D tf = tfComponent->getWorldTransform();
|
||||||
tf.setScale(tf.getScale() * TF_SPHERE_SCALE);
|
tf.setScale(tf.getScale() * TF_SPHERE_SCALE);
|
||||||
input.imDd.drawMeshes(
|
input.imDd.drawMeshes(
|
||||||
cbe::WorldImDrawContext::MeshDrawInfo{
|
cbe::WorldImDrawContext::MeshDrawInfo{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -23,8 +23,8 @@ WgConsoleStringBuffer::~WgConsoleStringBuffer()
|
|||||||
|
|
||||||
WgConsoleImGuiLayer::WgConsoleImGuiLayer()
|
WgConsoleImGuiLayer::WgConsoleImGuiLayer()
|
||||||
{
|
{
|
||||||
callbackHndl = Logger::bindPacketListener(
|
callbackHndl = cbe::Logger::bindPacketListener(
|
||||||
[this](const String &strBuffer, const std::vector<Logger::LogMsgPacket> &packets)
|
[this](const String &strBuffer, const std::vector<cbe::Logger::LogMsgPacket> &packets)
|
||||||
{
|
{
|
||||||
onMsgPacketsReceived(strBuffer, packets);
|
onMsgPacketsReceived(strBuffer, packets);
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ WgConsoleImGuiLayer::WgConsoleImGuiLayer()
|
|||||||
}
|
}
|
||||||
WgConsoleImGuiLayer::~WgConsoleImGuiLayer()
|
WgConsoleImGuiLayer::~WgConsoleImGuiLayer()
|
||||||
{
|
{
|
||||||
Logger::unbindPacketListener(callbackHndl);
|
cbe::Logger::unbindPacketListener(callbackHndl);
|
||||||
callbackHndl = {};
|
callbackHndl = {};
|
||||||
|
|
||||||
packets.clear();
|
packets.clear();
|
||||||
@@ -77,7 +77,7 @@ void WgConsoleImGuiLayer::drawImGui()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
copat::JobSystemFuncAwaiter
|
copat::JobSystemFuncAwaiter
|
||||||
WgConsoleImGuiLayer::onMsgPacketsReceived(const String &inStrBuffer, const std::vector<Logger::LogMsgPacket> &inPackets)
|
WgConsoleImGuiLayer::onMsgPacketsReceived(const String &inStrBuffer, const std::vector<cbe::Logger::LogMsgPacket> &inPackets)
|
||||||
{
|
{
|
||||||
if (inPackets.empty())
|
if (inPackets.empty())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2024
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -51,7 +51,7 @@ private:
|
|||||||
struct WgConsolePacket
|
struct WgConsolePacket
|
||||||
{
|
{
|
||||||
ReferenceCountPtr<WgConsoleStringBuffer> strBuffer;
|
ReferenceCountPtr<WgConsoleStringBuffer> strBuffer;
|
||||||
Logger::LogMsgPacket logPacket;
|
cbe::Logger::LogMsgPacket logPacket;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WgConsoleImGuiLayer : public IImGuiLayer
|
class WgConsoleImGuiLayer : public IImGuiLayer
|
||||||
@@ -73,10 +73,10 @@ private:
|
|||||||
SizeT packetsHead = 0;
|
SizeT packetsHead = 0;
|
||||||
|
|
||||||
uint32 maxLogHistory = 100;
|
uint32 maxLogHistory = 100;
|
||||||
Logger::ELogSeverityFlags logSeverityFilter = Logger::Log | Logger::Warning | Logger::Error;
|
cbe::Logger::ELogSeverityFlags logSeverityFilter = cbe::Logger::Log | cbe::Logger::Warning | cbe::Logger::Error;
|
||||||
bool bAutoscroll = true;
|
bool bAutoscroll = true;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
copat::JobSystemFuncAwaiter onMsgPacketsReceived(const String &inStrBuffer, const std::vector<Logger::LogMsgPacket> &inPackets);
|
copat::JobSystemFuncAwaiter onMsgPacketsReceived(const String &inStrBuffer, const std::vector<cbe::Logger::LogMsgPacket> &inPackets);
|
||||||
void changeMaxLogHistory(uint32 maxHistory);
|
void changeMaxLogHistory(uint32 maxHistory);
|
||||||
};
|
};
|
||||||
@@ -4,29 +4,31 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <Widgets/WgContentsImGuiLayer.h>
|
#include <Widgets/WgContentsImGuiLayer.h>
|
||||||
|
#include <CbeApplication.h>
|
||||||
#include <CBEPackage.hpp>
|
#include <CBEPackage.hpp>
|
||||||
|
#include <CoreObjectGC.h>
|
||||||
#include <Wg/EngineDragPayloads.hpp>
|
#include <Wg/EngineDragPayloads.hpp>
|
||||||
#include <Serialization/ArrayArchiveStream.h>
|
#include <Serialization/ArrayArchiveStream.h>
|
||||||
#include <Serialization/PackageLoader.h>
|
#include <Serialization/PackageLoader.h>
|
||||||
#include <Serialization/PackageSaver.h>
|
#include <Serialization/PackageSaver.h>
|
||||||
|
#include <Widgets/EditorWidgetsHelper.hpp>
|
||||||
|
#include <Widgets/WgEditorConstants.hpp>
|
||||||
|
#include <Widgets/WgEditorImGuiLayer.h>
|
||||||
#include <Widgets/ImGui/CbeImGui.hpp>
|
#include <Widgets/ImGui/CbeImGui.hpp>
|
||||||
#include <Types/Platform/LFS/File/FileHelper.h>
|
#include <Types/Platform/LFS/File/FileHelper.h>
|
||||||
#include <Types/Platform/LFS/PathFunctions.h>
|
#include <Types/Platform/LFS/PathFunctions.h>
|
||||||
#include <Types/Platform/LFS/Paths.h>
|
#include <Types/Platform/LFS/Paths.h>
|
||||||
#include <Types/Platform/LFS/PlatformLFS.h>
|
#include <Types/Platform/LFS/PlatformLFS.h>
|
||||||
#include <CranberryEngineApp.h>
|
|
||||||
#include <IApplicationModule.h>
|
#include <IApplicationModule.h>
|
||||||
#include <CBEAssetManager.hpp>
|
#include <CBEAssetManager.hpp>
|
||||||
#include <Widgets/EditorWidgetsHelper.hpp>
|
|
||||||
#include <ObjectPathHelpers.h>
|
#include <ObjectPathHelpers.h>
|
||||||
#include <Property/PropertyHelper.h>
|
#include <Property/PropertyHelper.h>
|
||||||
#include <Widgets/WgEditorConstants.hpp>
|
|
||||||
#include <Classes/ActorPrefab.hpp>
|
#include <Classes/ActorPrefab.hpp>
|
||||||
#include <Classes/World.hpp>
|
#include <Classes/World.hpp>
|
||||||
#include <Classes/EngineBase.hpp>
|
#include <Classes/EngineBase.hpp>
|
||||||
@@ -46,8 +48,6 @@ constexpr ImGuiWindowFlags CWD_CONTENTS_WND_FLAGS = ImGuiWindowFlags_NoTitleBar
|
|||||||
|
|
||||||
constexpr const TChar *WG_CONTENT_LOG_CATEGORY = "WgContents";
|
constexpr const TChar *WG_CONTENT_LOG_CATEGORY = "WgContents";
|
||||||
|
|
||||||
constexpr Vector2 HMODAL_POPUP_SIZE = { 500, 200 };
|
|
||||||
constexpr Vector2 VMODAL_POPUP_SIZE = { 300, 500 };
|
|
||||||
constexpr const AChar *IMPORT_MODAL_POPUP = "Import Assets";
|
constexpr const AChar *IMPORT_MODAL_POPUP = "Import Assets";
|
||||||
constexpr const AChar *CREATE_MODAL_POPUP = "New Asset";
|
constexpr const AChar *CREATE_MODAL_POPUP = "New Asset";
|
||||||
constexpr const AChar *DELETE_ASSETS_MODAL_POPUP = "Delete Assets";
|
constexpr const AChar *DELETE_ASSETS_MODAL_POPUP = "Delete Assets";
|
||||||
@@ -69,8 +69,7 @@ static void deleteAssetPackage(StringView rootObjectPath, StringView packagePath
|
|||||||
}
|
}
|
||||||
static void openAssetEditorFromAssetPack(const cbe::AssetManager::AssetPackage *assetPack)
|
static void openAssetEditorFromAssetPack(const cbe::AssetManager::AssetPackage *assetPack)
|
||||||
{
|
{
|
||||||
cbe::Object *asset = cbe::getOrLoad(assetPack->rootObjectPath, assetPack->rootObjectClass);
|
cbe::gCBEditorEngine->openAssetEditor(assetPack->rootObjectPath, assetPack->rootObjectClass);
|
||||||
cbe::gCBEditorEngine->openAssetEditor(asset);
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Approach each delete, copy, move actions from transaction point.
|
* Approach each delete, copy, move actions from transaction point.
|
||||||
@@ -96,7 +95,7 @@ public:
|
|||||||
{
|
{
|
||||||
/* Remove the backup directory */
|
/* Remove the backup directory */
|
||||||
const bool bDeleted = FileHelper::deleteDir(backupDir);
|
const bool bDeleted = FileHelper::deleteDir(backupDir);
|
||||||
LOG_ERROR_C(!bDeleted, cbe::Transaction::LOG_CATEGORY, "Failed to delete transaction directory {}", backupDir);
|
CBE_LOG_ERROR_C(!bDeleted, cbe::Transaction::LOG_CATEGORY, "Failed to delete transaction directory {}", backupDir);
|
||||||
backupDir.clear();
|
backupDir.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,7 +123,7 @@ public:
|
|||||||
const bool bSucceeded = FileHelper::copyFile(
|
const bool bSucceeded = FileHelper::copyFile(
|
||||||
PathFunctions::combinePath(backupDir, contentRelPkgPath), PathFunctions::combinePath(contentDir, contentRelPkgPath)
|
PathFunctions::combinePath(backupDir, contentRelPkgPath), PathFunctions::combinePath(contentDir, contentRelPkgPath)
|
||||||
);
|
);
|
||||||
LOG_ERROR_C(
|
CBE_LOG_ERROR_C(
|
||||||
!bSucceeded, cbe::Transaction::LOG_CATEGORY, "Failed to copy package {} to content dir {}", pkg.packagePath, contentDir
|
!bSucceeded, cbe::Transaction::LOG_CATEGORY, "Failed to copy package {} to content dir {}", pkg.packagePath, contentDir
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -174,7 +173,7 @@ public:
|
|||||||
{
|
{
|
||||||
/* Remove the backup directory */
|
/* Remove the backup directory */
|
||||||
const bool bDeleted = FileHelper::deleteDir(backupDir);
|
const bool bDeleted = FileHelper::deleteDir(backupDir);
|
||||||
LOG_ERROR_C(!bDeleted, cbe::Transaction::LOG_CATEGORY, "Failed to delete transaction directory {}", backupDir);
|
CBE_LOG_ERROR_C(!bDeleted, cbe::Transaction::LOG_CATEGORY, "Failed to delete transaction directory {}", backupDir);
|
||||||
backupDir.clear();
|
backupDir.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,7 +209,7 @@ public:
|
|||||||
FileHelper::makeDir(destDir);
|
FileHelper::makeDir(destDir);
|
||||||
}
|
}
|
||||||
const bool bCopied = FileHelper::copyFile(srcPath, destPath);
|
const bool bCopied = FileHelper::copyFile(srcPath, destPath);
|
||||||
LOG_ERROR_C(!bCopied, cbe::Transaction::LOG_CATEGORY, "Failed to copy package {} to file {}", srcPath, destPath);
|
CBE_LOG_ERROR_C(!bCopied, cbe::Transaction::LOG_CATEGORY, "Failed to copy package {} to file {}", srcPath, destPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load one package to refresh the package manager */
|
/* Load one package to refresh the package manager */
|
||||||
@@ -271,7 +270,7 @@ public:
|
|||||||
{
|
{
|
||||||
/* Remove the backup directory */
|
/* Remove the backup directory */
|
||||||
const bool bDeleted = FileHelper::deleteDir(backupDir);
|
const bool bDeleted = FileHelper::deleteDir(backupDir);
|
||||||
LOG_ERROR_C(!bDeleted, cbe::Transaction::LOG_CATEGORY, "Failed to delete transaction directory {}", backupDir);
|
CBE_LOG_ERROR_C(!bDeleted, cbe::Transaction::LOG_CATEGORY, "Failed to delete transaction directory {}", backupDir);
|
||||||
backupDir.clear();
|
backupDir.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -353,7 +352,7 @@ private:
|
|||||||
FileHelper::makeDir(destDir);
|
FileHelper::makeDir(destDir);
|
||||||
}
|
}
|
||||||
const bool bCopied = FileHelper::copyFile(srcPath, destPath);
|
const bool bCopied = FileHelper::copyFile(srcPath, destPath);
|
||||||
LOG_ERROR_C(!bCopied, cbe::Transaction::LOG_CATEGORY, "Failed to copy package {} to file {}", srcPath, destPath);
|
CBE_LOG_ERROR_C(!bCopied, cbe::Transaction::LOG_CATEGORY, "Failed to copy package {} to file {}", srcPath, destPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load one package to refresh the package manager */
|
/* Load one package to refresh the package manager */
|
||||||
@@ -377,8 +376,7 @@ void WgContentsImGuiLayer::drawImGui()
|
|||||||
{
|
{
|
||||||
if (assetManager == nullptr)
|
if (assetManager == nullptr)
|
||||||
{
|
{
|
||||||
CranberryEngineApp *application = static_cast<CranberryEngineApp *>(IApplicationModule::get()->getApplication());
|
assetManager = cbe::gCBEditorEngine->getAssetManager();
|
||||||
assetManager = application->getAssetManager();
|
|
||||||
}
|
}
|
||||||
if (assetManager == nullptr)
|
if (assetManager == nullptr)
|
||||||
{
|
{
|
||||||
@@ -389,6 +387,9 @@ void WgContentsImGuiLayer::drawImGui()
|
|||||||
|
|
||||||
const ImGuiWindowFlags wndFlags = ImGuiWindowFlags_NoFocusOnAppearing; ///< To not mess with docking parent focus
|
const ImGuiWindowFlags wndFlags = ImGuiWindowFlags_NoFocusOnAppearing; ///< To not mess with docking parent focus
|
||||||
const cbe::ImGuiScopedWindow window{ cbe::wg_consts::WND_CONTENTS_ID, wndFlags };
|
const cbe::ImGuiScopedWindow window{ cbe::wg_consts::WND_CONTENTS_ID, wndFlags };
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
if (window)
|
if (window)
|
||||||
{
|
{
|
||||||
if (ImGui::BeginChild(
|
if (ImGui::BeginChild(
|
||||||
@@ -428,8 +429,6 @@ void WgContentsImGuiLayer::drawImGui()
|
|||||||
drawCwdContents();
|
drawCwdContents();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopStyleVar();
|
|
||||||
|
|
||||||
drawImportAssetsModal();
|
drawImportAssetsModal();
|
||||||
drawDeleteAssetsModal();
|
drawDeleteAssetsModal();
|
||||||
drawCreateAssetModal();
|
drawCreateAssetModal();
|
||||||
@@ -1033,16 +1032,20 @@ void WgContentsImGuiLayer::drawAssetContextMenus(const CwdContent &content)
|
|||||||
drawMultiSelectContextMenus();
|
drawMultiSelectContextMenus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#if DEBUG_VALIDATIONS_ENABLED
|
|
||||||
{
|
{
|
||||||
void *selItr = nullptr;
|
void *selItr = nullptr;
|
||||||
ImGuiID id;
|
ImGuiID id;
|
||||||
const bool bHasSel = selections.GetNextSelectedItem(&selItr, &id);
|
const bool bHasSel = selections.GetNextSelectedItem(&selItr, &id);
|
||||||
const uint32 cwdContentIdx = imGuiIdToCwdContentIdx(id);
|
const uint32 cwdContentIdx = imGuiIdToCwdContentIdx(id);
|
||||||
debugAssert(bHasSel && cwdContentIdx < cwdContents.size());
|
debugAssert(bHasSel && cwdContentIdx < cwdContents.size());
|
||||||
debugAssertf(content.nodeIdx == cwdContents[cwdContentIdx].nodeIdx, "Context menu must open only over current selection!");
|
if (content.nodeIdx != cwdContents[cwdContentIdx].nodeIdx)
|
||||||
|
{
|
||||||
|
/* Right mouse button released on top of folder without selecting it. Probably stray mouse drag release. */
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ImGui::MenuItem(CBE_MAT_ICON_EDIT_SQUARE " Open"))
|
if (ImGui::MenuItem(CBE_MAT_ICON_EDIT_SQUARE " Open"))
|
||||||
{
|
{
|
||||||
@@ -1080,7 +1083,6 @@ void WgContentsImGuiLayer::drawFolderContextMenus(TreeNodeIdx dirFolderTreeIdx)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG_VALIDATIONS_ENABLED
|
|
||||||
if (selections.Size == 1)
|
if (selections.Size == 1)
|
||||||
{
|
{
|
||||||
void *selItr = nullptr;
|
void *selItr = nullptr;
|
||||||
@@ -1088,9 +1090,13 @@ void WgContentsImGuiLayer::drawFolderContextMenus(TreeNodeIdx dirFolderTreeIdx)
|
|||||||
const bool bHasSel = selections.GetNextSelectedItem(&selItr, &id);
|
const bool bHasSel = selections.GetNextSelectedItem(&selItr, &id);
|
||||||
const uint32 cwdContentIdx = imGuiIdToCwdContentIdx(id);
|
const uint32 cwdContentIdx = imGuiIdToCwdContentIdx(id);
|
||||||
debugAssert(bHasSel && cwdContentIdx < cwdContents.size());
|
debugAssert(bHasSel && cwdContentIdx < cwdContents.size());
|
||||||
debugAssertf(dirFolderTreeIdx == cwdContents[cwdContentIdx].nodeIdx, "Context menu must open only over current selection!");
|
if (dirFolderTreeIdx != cwdContents[cwdContentIdx].nodeIdx)
|
||||||
|
{
|
||||||
|
/* Right mouse button released on top of folder without selecting it. Probably stray mouse drag release. */
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
const AChar *createAssetLabel = CBE_MAT_ICON_LARGE_ADD " Create";
|
const AChar *createAssetLabel = CBE_MAT_ICON_LARGE_ADD " Create";
|
||||||
const bool bCreateAssetOpenLastFrame = cbe::ImGuiHelpers::isPopupOpen(cbe::ImGuiHelpers::getPopupId(createAssetLabel));
|
const bool bCreateAssetOpenLastFrame = cbe::ImGuiHelpers::isPopupOpen(cbe::ImGuiHelpers::getPopupId(createAssetLabel));
|
||||||
@@ -1313,13 +1319,18 @@ void WgContentsImGuiLayer::drawImportAssetsModal()
|
|||||||
ImportAssetData &impData = *importData;
|
ImportAssetData &impData = *importData;
|
||||||
const uint32 pathIdx = impData.pathIdx;
|
const uint32 pathIdx = impData.pathIdx;
|
||||||
|
|
||||||
|
/* Necessary to try to reopen this pop up after async processing is done. Since progress bar closes this one. */
|
||||||
|
if (!ImGui::IsPopupOpen(IMPORT_MODAL_POPUP) && !impData.bProcessing)
|
||||||
|
{
|
||||||
|
ImGui::OpenPopup(IMPORT_MODAL_POPUP);
|
||||||
|
}
|
||||||
|
|
||||||
bool bPopupOpen = true;
|
bool bPopupOpen = true;
|
||||||
/* To allow matching content size every time it appears */
|
/* To allow matching content size every time it appears */
|
||||||
ImGui::SetNextWindowSize(ImVec2{}, ImGuiCond_Appearing);
|
ImGui::SetNextWindowSize(ImVec2{}, ImGuiCond_Appearing);
|
||||||
ImGui::SetNextWindowSizeConstraints(VMODAL_POPUP_SIZE, Vector2{ std::numeric_limits<float>::max() });
|
ImGui::SetNextWindowSizeConstraints(cbe::wg_consts::VMODAL_POPUP_SIZE, Vector2{ std::numeric_limits<float>::max() });
|
||||||
if (ImGui::BeginPopupModal(IMPORT_MODAL_POPUP, &bPopupOpen, ImGuiWindowFlags_None))
|
if (ImGui::BeginPopupModal(IMPORT_MODAL_POPUP, &bPopupOpen, ImGuiWindowFlags_None))
|
||||||
{
|
{
|
||||||
bool bImportNextPath = false;
|
|
||||||
if (impData.importer == nullptr)
|
if (impData.importer == nullptr)
|
||||||
{
|
{
|
||||||
/* It is okay we take view directly from paths instead of import option's filePath as both live until import is done. */
|
/* It is okay we take view directly from paths instead of import option's filePath as both live until import is done. */
|
||||||
@@ -1357,7 +1368,7 @@ void WgContentsImGuiLayer::drawImportAssetsModal()
|
|||||||
/* Still null? Skip to next path */
|
/* Still null? Skip to next path */
|
||||||
if (impData.importer == nullptr)
|
if (impData.importer == nullptr)
|
||||||
{
|
{
|
||||||
bImportNextPath = true;
|
impData.bImportNext = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1377,6 +1388,8 @@ void WgContentsImGuiLayer::drawImportAssetsModal()
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(impData.bProcessing);
|
||||||
|
|
||||||
ImGui::SetNextItemShortcut(ImGuiKey_Escape);
|
ImGui::SetNextItemShortcut(ImGuiKey_Escape);
|
||||||
if (ImGui::Button("Cancel"))
|
if (ImGui::Button("Cancel"))
|
||||||
{
|
{
|
||||||
@@ -1391,32 +1404,109 @@ void WgContentsImGuiLayer::drawImportAssetsModal()
|
|||||||
/* If importer and context is valid try importing else go to next path. */
|
/* If importer and context is valid try importing else go to next path. */
|
||||||
if (impData.cntx)
|
if (impData.cntx)
|
||||||
{
|
{
|
||||||
|
debugAssert(impData.importOption.importProgressCount > 0);
|
||||||
|
|
||||||
|
/* +1 for completion in main thread */
|
||||||
|
const cbe::ProgressTrackerHnd progTracker = cbe::gCBEditorEngine->addProgressTracker(cbe::ProgressTrackerInfo{
|
||||||
|
.name = TCHAR("Import"),
|
||||||
|
.stepsCount = impData.importOption.importProgressCount + 1,
|
||||||
|
});
|
||||||
|
impData.importOption.progressCb.bindLambda(
|
||||||
|
[progTracker](uint32 stepsCount, StringView desc)
|
||||||
|
{
|
||||||
|
cbe::gCBEditorEngine->progressProgressTracker(progTracker, stepsCount, desc);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
auto importAssets = [](copat::JobSystem &js, WgContentsImGuiLayer *thisLayer,
|
||||||
|
cbe::ProgressTrackerHnd progTracker) -> copat::JobSystemFuncAwaiter
|
||||||
|
{
|
||||||
|
CoreObjectGC &gc = ICoreObjectsModule::get()->getGC();
|
||||||
|
/* To not clear the loaded objects while Async loading */
|
||||||
|
gc.pauseGc();
|
||||||
|
|
||||||
|
co_await copat::SwitchJobSystemThreadAwaiter{ js, copat::EJobThreadType::WorkerThreads };
|
||||||
|
|
||||||
|
debugAssert(thisLayer->importData);
|
||||||
|
ImportAssetData &impData = *thisLayer->importData;
|
||||||
|
|
||||||
|
/* Perform the import */
|
||||||
const AssetImporterBase::ImportResult result = impData.importer->importAssets(impData.importOption, impData.cntx);
|
const AssetImporterBase::ImportResult result = impData.importer->importAssets(impData.importOption, impData.cntx);
|
||||||
|
|
||||||
|
/* Sync with main thread to start saving */
|
||||||
|
co_await copat::SwitchJobSystemThreadAwaiter{ *copat::JobSystem::get(), copat::EJobThreadType::MainThread };
|
||||||
|
|
||||||
|
cbe::gCBEditorEngine->progressProgressTracker(progTracker, 1, {});
|
||||||
|
cbe::ProgressTrackerHnd savingProgTracker = 0;
|
||||||
|
|
||||||
if (std::holds_alternative<AssetImporterBase::ImportSuccess>(result))
|
if (std::holds_alternative<AssetImporterBase::ImportSuccess>(result))
|
||||||
{
|
{
|
||||||
const AssetImporterBase::ImportSuccess &success = std::get<AssetImporterBase::ImportSuccess>(result);
|
const AssetImporterBase::ImportSuccess &success = std::get<AssetImporterBase::ImportSuccess>(result);
|
||||||
|
|
||||||
|
/* +1 to finish at main thread and refresh content browser */
|
||||||
|
savingProgTracker = cbe::gCBEditorEngine->addProgressTracker(cbe::ProgressTrackerInfo{
|
||||||
|
.name = TCHAR("Saving"),
|
||||||
|
.stepsCount = static_cast<uint32>(success.objs.size()) + 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Save in worker thread */
|
||||||
|
co_await copat::SwitchJobSystemThreadAwaiter{ js, copat::EJobThreadType::WorkerThreads };
|
||||||
|
|
||||||
for (cbe::Object *obj : success.objs)
|
for (cbe::Object *obj : success.objs)
|
||||||
{
|
{
|
||||||
LOG(WG_CONTENT_LOG_CATEGORY, "Imported {}", obj->getObjectData().path);
|
CBE_LOG(WG_CONTENT_LOG_CATEGORY, "Imported and Saved {}", obj->getObjectData().path);
|
||||||
|
|
||||||
|
cbe::gCBEditorEngine->progressProgressTracker(
|
||||||
|
savingProgTracker, 1, STR_FORMAT("Saving {}", obj->getObjectData().name)
|
||||||
|
);
|
||||||
|
|
||||||
cbe::save(obj);
|
cbe::save(obj);
|
||||||
|
|
||||||
|
co_await copat::YieldAwaiter{};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Refresh if imported successfully. */
|
/* Switch back to main thread */
|
||||||
refreshCwd();
|
co_await copat::SwitchJobSystemThreadAwaiter{ *copat::JobSystem::get(), copat::EJobThreadType::MainThread };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_ERROR(WG_CONTENT_LOG_CATEGORY, "Importing {} failed with following errors", impData.importOption.filePath);
|
CBE_LOG_ERROR(
|
||||||
|
WG_CONTENT_LOG_CATEGORY, "Importing {} failed with following errors", impData.importOption.filePath
|
||||||
|
);
|
||||||
const AssetImporterBase::ImportError &errs = std::get<AssetImporterBase::ImportError>(result);
|
const AssetImporterBase::ImportError &errs = std::get<AssetImporterBase::ImportError>(result);
|
||||||
for (SizeT i = 0; i < errs.errors.size(); ++i)
|
for (SizeT i = 0; i < errs.errors.size(); ++i)
|
||||||
{
|
{
|
||||||
LOG_ERROR(WG_CONTENT_LOG_CATEGORY, "Import error {}: {}", i, errs.errors[i]);
|
CBE_LOG_ERROR(WG_CONTENT_LOG_CATEGORY, "Import error {}: {}", i, errs.errors[i]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (std::holds_alternative<AssetImporterBase::ImportSuccess>(result))
|
||||||
|
{
|
||||||
|
cbe::gCBEditorEngine->progressProgressTracker(savingProgTracker, 1, TCHAR("Refresh content browser"));
|
||||||
|
/* Refresh if imported successfully. */
|
||||||
|
thisLayer->refreshCwd();
|
||||||
|
}
|
||||||
|
|
||||||
|
impData.bProcessing = false;
|
||||||
/* Go to next path */
|
/* Go to next path */
|
||||||
bImportNextPath = true;
|
impData.bImportNext = true;
|
||||||
|
impData.importOption.progressCb.unbind();
|
||||||
|
|
||||||
|
/* Cancel just in case prepare fails. */
|
||||||
|
cbe::gCBEditorEngine->cancelProgressTracker(progTracker);
|
||||||
|
cbe::gCBEditorEngine->cancelProgressTracker(savingProgTracker);
|
||||||
|
|
||||||
|
gc.resumeGc();
|
||||||
|
};
|
||||||
|
|
||||||
|
impData.bProcessing = true;
|
||||||
|
importAssets(*copat::JobSystem::get(), this, progTracker);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Go to next path */
|
||||||
|
impData.bImportNext = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1424,6 +1514,25 @@ void WgContentsImGuiLayer::drawImportAssetsModal()
|
|||||||
if (ImGui::Button("Prepare"))
|
if (ImGui::Button("Prepare"))
|
||||||
{
|
{
|
||||||
debugAssert(impData.importer != nullptr);
|
debugAssert(impData.importer != nullptr);
|
||||||
|
debugAssert(impData.importOption.prepareProgressCount > 0);
|
||||||
|
|
||||||
|
const cbe::ProgressTrackerHnd progTracker = cbe::gCBEditorEngine->addProgressTracker(cbe::ProgressTrackerInfo{
|
||||||
|
.name = TCHAR("Prepare Import"),
|
||||||
|
.stepsCount = impData.importOption.prepareProgressCount + 1,
|
||||||
|
});
|
||||||
|
impData.importOption.progressCb.bindLambda(
|
||||||
|
[progTracker](uint32 stepsCount, StringView desc)
|
||||||
|
{
|
||||||
|
cbe::gCBEditorEngine->progressProgressTracker(progTracker, stepsCount, desc);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
auto prepareCntx
|
||||||
|
= [](copat::JobSystem &js, ImportAssetData &impData, cbe::ProgressTrackerHnd progTracker) -> copat::JobSystemFuncAwaiter
|
||||||
|
{
|
||||||
|
co_await copat::SwitchJobSystemThreadAwaiter{ js, copat::EJobThreadType::WorkerThreads };
|
||||||
|
|
||||||
|
/* Prepare the context */
|
||||||
impData.cntx = impData.importer->prepareContext(impData.importOption);
|
impData.cntx = impData.importer->prepareContext(impData.importOption);
|
||||||
if (impData.importOption.cntxOptionsStruct != nullptr)
|
if (impData.importOption.cntxOptionsStruct != nullptr)
|
||||||
{
|
{
|
||||||
@@ -1431,10 +1540,27 @@ void WgContentsImGuiLayer::drawImportAssetsModal()
|
|||||||
impData.importOption.cntxOptionsStructType, impData.importOption.cntxOptionsStruct
|
impData.importOption.cntxOptionsStructType, impData.importOption.cntxOptionsStruct
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
co_await copat::SwitchJobSystemThreadAwaiter{ *copat::JobSystem::get(), copat::EJobThreadType::MainThread };
|
||||||
|
|
||||||
impData.bPrepared = true;
|
impData.bPrepared = true;
|
||||||
|
impData.bProcessing = false;
|
||||||
|
impData.importOption.progressCb.unbind();
|
||||||
|
|
||||||
|
/* Do final progress */
|
||||||
|
cbe::gCBEditorEngine->progressProgressTracker(progTracker, 1, {});
|
||||||
|
|
||||||
|
/* Cancel just in case prepare fails. */
|
||||||
|
cbe::gCBEditorEngine->cancelProgressTracker(progTracker);
|
||||||
|
};
|
||||||
|
|
||||||
|
impData.bProcessing = true;
|
||||||
|
prepareCntx(*copat::JobSystem::get(), impData, progTracker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (cbe::EditorWidgetsHelper::beginPropertiesTable("", 150.f))
|
if (cbe::EditorWidgetsHelper::beginPropertiesTable("", 150.f))
|
||||||
{
|
{
|
||||||
@@ -1451,7 +1577,7 @@ void WgContentsImGuiLayer::drawImportAssetsModal()
|
|||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bImportNextPath)
|
if (impData.bImportNext)
|
||||||
{
|
{
|
||||||
impData.pathIdx++;
|
impData.pathIdx++;
|
||||||
/* Reset the data cache for a file import */
|
/* Reset the data cache for a file import */
|
||||||
@@ -1461,6 +1587,8 @@ void WgContentsImGuiLayer::drawImportAssetsModal()
|
|||||||
impData.cntxOptionsDrawer.reset();
|
impData.cntxOptionsDrawer.reset();
|
||||||
impData.cntx.reset();
|
impData.cntx.reset();
|
||||||
impData.bPrepared = false;
|
impData.bPrepared = false;
|
||||||
|
impData.bProcessing = false;
|
||||||
|
impData.bImportNext = false;
|
||||||
if (impData.pathIdx >= impData.fromPaths.size())
|
if (impData.pathIdx >= impData.fromPaths.size())
|
||||||
{
|
{
|
||||||
/* Close the pop up now. */
|
/* Close the pop up now. */
|
||||||
@@ -1475,7 +1603,8 @@ void WgContentsImGuiLayer::drawImportAssetsModal()
|
|||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bPopupOpen)
|
/* If processing, probably the progress bar made this popup to close. */
|
||||||
|
if (!bPopupOpen && !impData.bProcessing)
|
||||||
{
|
{
|
||||||
importData.reset();
|
importData.reset();
|
||||||
}
|
}
|
||||||
@@ -1771,9 +1900,19 @@ void WgContentsImGuiLayer::issueMoveAssets(MoveAssetsInfo &&moveInf)
|
|||||||
movedDummyTransaction.apply(dummyCollector);
|
movedDummyTransaction.apply(dummyCollector);
|
||||||
for (uint32 i = 0; i < moveInf.movingAssets.size(); ++i)
|
for (uint32 i = 0; i < moveInf.movingAssets.size(); ++i)
|
||||||
{
|
{
|
||||||
|
cbe::Object *orgObj = orgRootObjs[i];
|
||||||
cbe::Object *movedObj = cbe::getOrLoad(movedDummyTransaction.pkgs[i].rootObjectPath, nullptr);
|
cbe::Object *movedObj = cbe::getOrLoad(movedDummyTransaction.pkgs[i].rootObjectPath, nullptr);
|
||||||
debugAssert(movedObj != nullptr);
|
debugAssert(movedObj != nullptr);
|
||||||
replacements[orgRootObjs[i]] = movedObj;
|
replacements[orgObj] = movedObj;
|
||||||
|
|
||||||
|
/* Since asset sub objects will also be modified we need to replace them as well. */
|
||||||
|
std::vector<cbe::Object *> childrenObjs;
|
||||||
|
objectsDb.getSubobjects(childrenObjs, orgObj->getDbIdx());
|
||||||
|
for (cbe::Object *child : childrenObjs)
|
||||||
|
{
|
||||||
|
const String childSubPath = ObjectPathHelper::computeObjectPath(child, orgObj);
|
||||||
|
replacements[child] = cbe::get(ObjectPathHelper::getFullPath(childSubPath, movedObj));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cbe::TransactionRef thisTransaction;
|
cbe::TransactionRef thisTransaction;
|
||||||
@@ -1800,7 +1939,7 @@ void WgContentsImGuiLayer::issueMoveAssets(MoveAssetsInfo &&moveInf)
|
|||||||
{
|
{
|
||||||
thisTransaction->captureObjectBaseline(referrer);
|
thisTransaction->captureObjectBaseline(referrer);
|
||||||
std::vector<cbe::Object *> children;
|
std::vector<cbe::Object *> children;
|
||||||
objectsDb.getChildren(children, referrer->getDbIdx());
|
objectsDb.getSubobjects(children, referrer->getDbIdx());
|
||||||
for (cbe::Object *child : children)
|
for (cbe::Object *child : children)
|
||||||
{
|
{
|
||||||
thisTransaction->captureObjectBaseline(child);
|
thisTransaction->captureObjectBaseline(child);
|
||||||
@@ -1863,7 +2002,7 @@ void WgContentsImGuiLayer::drawRenameModal()
|
|||||||
|
|
||||||
/* To allow matching content size every time if appears */
|
/* To allow matching content size every time if appears */
|
||||||
ImGui::SetNextWindowSize(ImVec2{}, ImGuiCond_Appearing);
|
ImGui::SetNextWindowSize(ImVec2{}, ImGuiCond_Appearing);
|
||||||
ImGui::SetNextWindowSizeConstraints(HMODAL_POPUP_SIZE, Vector2{ std::numeric_limits<float>::max() });
|
ImGui::SetNextWindowSizeConstraints(cbe::wg_consts::HMODAL_POPUP_SIZE, Vector2{ std::numeric_limits<float>::max() });
|
||||||
if (ImGui::BeginPopupModal(RENAME_ASSET_MODAL_POPUP, &bPopupOpen))
|
if (ImGui::BeginPopupModal(RENAME_ASSET_MODAL_POPUP, &bPopupOpen))
|
||||||
{
|
{
|
||||||
const std::string_view descText = renameData.content.bFolder ? "Rename folder " : "Rename asset ";
|
const std::string_view descText = renameData.content.bFolder ? "Rename folder " : "Rename asset ";
|
||||||
@@ -2083,7 +2222,7 @@ void WgContentsImGuiLayer::drawMoveModal()
|
|||||||
|
|
||||||
/* To allow matching content size every time if appears */
|
/* To allow matching content size every time if appears */
|
||||||
ImGui::SetNextWindowSize(ImVec2{}, ImGuiCond_Appearing);
|
ImGui::SetNextWindowSize(ImVec2{}, ImGuiCond_Appearing);
|
||||||
ImGui::SetNextWindowSizeConstraints(HMODAL_POPUP_SIZE, Vector2{ std::numeric_limits<float>::max() });
|
ImGui::SetNextWindowSizeConstraints(cbe::wg_consts::HMODAL_POPUP_SIZE, Vector2{ std::numeric_limits<float>::max() });
|
||||||
if (ImGui::BeginPopupModal(MOVE_ASSETS_MODAL_POPUP, &bPopupOpen))
|
if (ImGui::BeginPopupModal(MOVE_ASSETS_MODAL_POPUP, &bPopupOpen))
|
||||||
{
|
{
|
||||||
cbe::ImGuiHelpers::textUnformatted(std::format("Moving {} assets", moveData.movingAssets.size()));
|
cbe::ImGuiHelpers::textUnformatted(std::format("Moving {} assets", moveData.movingAssets.size()));
|
||||||
@@ -2256,7 +2395,7 @@ void WgContentsImGuiLayer::openDeleteAssetsModal(bool bDeleteFolder)
|
|||||||
std::vector<CwdContent> &referrerContents = deleteAssetData.deletingAssetsReferrers.emplace_back();
|
std::vector<CwdContent> &referrerContents = deleteAssetData.deletingAssetsReferrers.emplace_back();
|
||||||
|
|
||||||
std::vector<cbe::AssetManager::PackageAllocator::AllocHandle> referrers;
|
std::vector<cbe::AssetManager::PackageAllocator::AllocHandle> referrers;
|
||||||
assetManager->getPackageReferrers(referrers, treeNodeIdx);
|
assetManager->getPackageReferrersFromRefTree(referrers, treeNodeIdx);
|
||||||
for (const cbe::AssetManager::PackageAllocator::AllocHandle &hnd : referrers)
|
for (const cbe::AssetManager::PackageAllocator::AllocHandle &hnd : referrers)
|
||||||
{
|
{
|
||||||
referrerContents.emplace_back(fillPackageContent(assetManager->getAssetPackage(hnd)->folderTreeIdx));
|
referrerContents.emplace_back(fillPackageContent(assetManager->getAssetPackage(hnd)->folderTreeIdx));
|
||||||
@@ -2281,6 +2420,7 @@ void WgContentsImGuiLayer::drawDeleteAssetsModal()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CoreObjectsDB &objectsDb = ICoreObjectsModule::get()->objectsDB();
|
||||||
DeleteAssetData &deleteAssetsData = std::get<DeleteAssetData>(assetEditData);
|
DeleteAssetData &deleteAssetsData = std::get<DeleteAssetData>(assetEditData);
|
||||||
|
|
||||||
bool bIssueDelete = false;
|
bool bIssueDelete = false;
|
||||||
@@ -2288,7 +2428,7 @@ void WgContentsImGuiLayer::drawDeleteAssetsModal()
|
|||||||
|
|
||||||
/* To allow matching content size every time if appears */
|
/* To allow matching content size every time if appears */
|
||||||
ImGui::SetNextWindowSize(ImVec2{}, ImGuiCond_Appearing);
|
ImGui::SetNextWindowSize(ImVec2{}, ImGuiCond_Appearing);
|
||||||
ImGui::SetNextWindowSizeConstraints(HMODAL_POPUP_SIZE, Vector2{ std::numeric_limits<float>::max() });
|
ImGui::SetNextWindowSizeConstraints(cbe::wg_consts::HMODAL_POPUP_SIZE, Vector2{ std::numeric_limits<float>::max() });
|
||||||
if (ImGui::BeginPopupModal(DELETE_ASSETS_MODAL_POPUP, &bPopupOpen))
|
if (ImGui::BeginPopupModal(DELETE_ASSETS_MODAL_POPUP, &bPopupOpen))
|
||||||
{
|
{
|
||||||
ImGui::TextUnformatted(std::format("Delete following {} Assets?", deleteAssetsData.deletingAssets.size()).c_str());
|
ImGui::TextUnformatted(std::format("Delete following {} Assets?", deleteAssetsData.deletingAssets.size()).c_str());
|
||||||
@@ -2393,6 +2533,20 @@ void WgContentsImGuiLayer::drawDeleteAssetsModal()
|
|||||||
cbe::Object *obj = cbe::getOrLoad(pkg->rootObjectPath, nullptr);
|
cbe::Object *obj = cbe::getOrLoad(pkg->rootObjectPath, nullptr);
|
||||||
cbe::Object *replacement = replacementCntx.objPath.isValid() ? replacementCntx.objPath.getObject() : nullptr;
|
cbe::Object *replacement = replacementCntx.objPath.isValid() ? replacementCntx.objPath.getObject() : nullptr;
|
||||||
replacements[obj] = replacement;
|
replacements[obj] = replacement;
|
||||||
|
|
||||||
|
/* Since asset sub objects will also be modified we need to replace them as well. */
|
||||||
|
std::vector<cbe::Object *> childrenObjs;
|
||||||
|
objectsDb.getSubobjects(childrenObjs, obj->getDbIdx());
|
||||||
|
for (cbe::Object *child : childrenObjs)
|
||||||
|
{
|
||||||
|
if (replacement == nullptr)
|
||||||
|
{
|
||||||
|
replacements[child] = nullptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const String childSubPath = ObjectPathHelper::computeObjectPath(child, obj);
|
||||||
|
replacements[child] = cbe::get(ObjectPathHelper::getFullPath(childSubPath, replacement));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* Generate unique referrer objects list */
|
/* Generate unique referrer objects list */
|
||||||
for (uint32 i = 0; i < deleteAssetsData.deletingAssets.size(); ++i)
|
for (uint32 i = 0; i < deleteAssetsData.deletingAssets.size(); ++i)
|
||||||
@@ -2478,12 +2632,11 @@ void WgContentsImGuiLayer::drawDeleteAssetsModal()
|
|||||||
/* Issue save transaction that must be reverted on undelete */
|
/* Issue save transaction that must be reverted on undelete */
|
||||||
thisTransaction->addCustomAction<cbe::SaveAssetsAction>(nullptr, referrers, cbe::SaveAssetsAction::AtRevert);
|
thisTransaction->addCustomAction<cbe::SaveAssetsAction>(nullptr, referrers, cbe::SaveAssetsAction::AtRevert);
|
||||||
|
|
||||||
const CoreObjectsDB &objectsDb = ICoreObjectsModule::get()->objectsDB();
|
|
||||||
for (cbe::Object *referrer : referrers)
|
for (cbe::Object *referrer : referrers)
|
||||||
{
|
{
|
||||||
thisTransaction->captureObjectBaseline(referrer);
|
thisTransaction->captureObjectBaseline(referrer);
|
||||||
std::vector<cbe::Object *> children;
|
std::vector<cbe::Object *> children;
|
||||||
objectsDb.getChildren(children, referrer->getDbIdx());
|
objectsDb.getSubobjects(children, referrer->getDbIdx());
|
||||||
for (cbe::Object *child : children)
|
for (cbe::Object *child : children)
|
||||||
{
|
{
|
||||||
thisTransaction->captureObjectBaseline(child);
|
thisTransaction->captureObjectBaseline(child);
|
||||||
@@ -2608,7 +2761,7 @@ void WgContentsImGuiLayer::cutAssets(bool bCutFolder)
|
|||||||
std::vector<CwdContent> &referrerContents = moveAssetData.movingAssetsReferrers.emplace_back();
|
std::vector<CwdContent> &referrerContents = moveAssetData.movingAssetsReferrers.emplace_back();
|
||||||
|
|
||||||
std::vector<cbe::AssetManager::PackageAllocator::AllocHandle> referrers;
|
std::vector<cbe::AssetManager::PackageAllocator::AllocHandle> referrers;
|
||||||
assetManager->getPackageReferrers(referrers, treeNodeIdx);
|
assetManager->getPackageReferrersFromRefTree(referrers, treeNodeIdx);
|
||||||
for (const cbe::AssetManager::PackageAllocator::AllocHandle &hnd : referrers)
|
for (const cbe::AssetManager::PackageAllocator::AllocHandle &hnd : referrers)
|
||||||
{
|
{
|
||||||
referrerContents.emplace_back(fillPackageContent(assetManager->getAssetPackage(hnd)->folderTreeIdx));
|
referrerContents.emplace_back(fillPackageContent(assetManager->getAssetPackage(hnd)->folderTreeIdx));
|
||||||
@@ -2893,7 +3046,7 @@ void WgContentsImGuiLayer::openRenameAssetOrFolderModal(TreeNodeIdx folderTreeId
|
|||||||
);
|
);
|
||||||
|
|
||||||
std::vector<cbe::AssetManager::PackageAllocator::AllocHandle> referrers;
|
std::vector<cbe::AssetManager::PackageAllocator::AllocHandle> referrers;
|
||||||
assetManager->getPackageReferrers(referrers, folderTreeIdx);
|
assetManager->getPackageReferrersFromRefTree(referrers, folderTreeIdx);
|
||||||
|
|
||||||
std::vector<CwdContent> &outReferrers = renameData.referrers.emplace_back();
|
std::vector<CwdContent> &outReferrers = renameData.referrers.emplace_back();
|
||||||
outReferrers.reserve(referrers.size());
|
outReferrers.reserve(referrers.size());
|
||||||
@@ -2922,7 +3075,7 @@ void WgContentsImGuiLayer::openRenameAssetOrFolderModal(TreeNodeIdx folderTreeId
|
|||||||
renameData.folderAssets.emplace_back(fillPackageContent(childIdx));
|
renameData.folderAssets.emplace_back(fillPackageContent(childIdx));
|
||||||
|
|
||||||
std::vector<cbe::AssetManager::PackageAllocator::AllocHandle> referrers;
|
std::vector<cbe::AssetManager::PackageAllocator::AllocHandle> referrers;
|
||||||
assetManager->getPackageReferrers(referrers, childIdx);
|
assetManager->getPackageReferrersFromRefTree(referrers, childIdx);
|
||||||
std::vector<CwdContent> &outReferrers = renameData.referrers.emplace_back();
|
std::vector<CwdContent> &outReferrers = renameData.referrers.emplace_back();
|
||||||
outReferrers.reserve(referrers.size());
|
outReferrers.reserve(referrers.size());
|
||||||
for (cbe::AssetManager::PackageAllocator::AllocHandle pkgAllocHnd : referrers)
|
for (cbe::AssetManager::PackageAllocator::AllocHandle pkgAllocHnd : referrers)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -172,7 +172,11 @@ private:
|
|||||||
/* Importing path index. We do one index at a time */
|
/* Importing path index. We do one index at a time */
|
||||||
uint32 pathIdx = 0;
|
uint32 pathIdx = 0;
|
||||||
/* When context is prepared. Gets filled after importer is prepared */
|
/* When context is prepared. Gets filled after importer is prepared */
|
||||||
bool bPrepared = false;
|
bool bPrepared:1 = false;
|
||||||
|
/* When a step is being async processed. */
|
||||||
|
bool bProcessing:1 = false;
|
||||||
|
/* If current import is done and next import is requested */
|
||||||
|
bool bImportNext:1 = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -22,16 +22,29 @@
|
|||||||
|
|
||||||
struct WorldViewport;
|
struct WorldViewport;
|
||||||
|
|
||||||
|
namespace cbe
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class EDetailsFlags : uint8
|
||||||
|
{
|
||||||
|
None = 0, ///< None
|
||||||
|
SendCompSelMsgs, ///< If want to send component selection change messages to editor.
|
||||||
|
};
|
||||||
|
MAKE_ENUM_BIT_OPS(EDetailsFlags)
|
||||||
|
|
||||||
struct WgDetailsCreateInfo
|
struct WgDetailsCreateInfo
|
||||||
{
|
{
|
||||||
const cbe::ICoreAssetEditorRef &edRoot;
|
const cbe::ICoreAssetEditorRef &edRoot;
|
||||||
std::string detailWndName;
|
std::string detailWndName;
|
||||||
|
|
||||||
|
EDetailsFlags flags = EDetailsFlags::None;
|
||||||
};
|
};
|
||||||
|
} // namespace cbe
|
||||||
|
|
||||||
class WgDetailsImGuiLayer : public IImGuiLayer
|
class WgDetailsImGuiLayer : public IImGuiLayer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WgDetailsImGuiLayer(WgDetailsCreateInfo ci);
|
WgDetailsImGuiLayer(cbe::WgDetailsCreateInfo ci);
|
||||||
~WgDetailsImGuiLayer();
|
~WgDetailsImGuiLayer();
|
||||||
/* IImGuiLayer overrides */
|
/* IImGuiLayer overrides */
|
||||||
|
|
||||||
@@ -39,10 +52,17 @@ public:
|
|||||||
|
|
||||||
/* Overrides ends */
|
/* Overrides ends */
|
||||||
void setWorldViewport(cbe::World *world, WorldViewport *viewport);
|
void setWorldViewport(cbe::World *world, WorldViewport *viewport);
|
||||||
|
void resetDetails()
|
||||||
|
{
|
||||||
|
/* redo this when sub-selection gets added */
|
||||||
|
onSelectionChanged();
|
||||||
|
}
|
||||||
void onSelectionChanged();
|
void onSelectionChanged();
|
||||||
void onSelectionTransformed() { rebuildTransformCompWidgetDrawer(); }
|
void onSelectionTransformed() { rebuildTransformCompWidgetDrawer(); }
|
||||||
|
void
|
||||||
|
onSubselectionChanged(ArrayView<cbe::WeakObjectPtr> selectedObjs, const std::unordered_set<cbe::WorldSelectionProxyRef> &selectedProxies);
|
||||||
/* If none of the actor selected is one of the object provided in does not refreshes */
|
/* If none of the actor selected is one of the object provided in does not refreshes */
|
||||||
void refreshDetailsDrawers(ArrayView<cbe::Object *> objsModified);
|
void refreshDetailsDrawers(ArrayView<cbe::WeakObjectPtr> objsModified);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static_assert(!IsTCharWide::value, "Wide character support is not optimized here! It will work but performs bad");
|
static_assert(!IsTCharWide::value, "Wide character support is not optimized here! It will work but performs bad");
|
||||||
@@ -73,7 +93,7 @@ private:
|
|||||||
enum ESelectionVariantIndex
|
enum ESelectionVariantIndex
|
||||||
{
|
{
|
||||||
Selection_Single,
|
Selection_Single,
|
||||||
Selection_Multi
|
Selection_Multi,
|
||||||
};
|
};
|
||||||
using TransformCompNodeData = std::variant<TransformPerCompNodeData, std::vector<TransformPerCompNodeData>>;
|
using TransformCompNodeData = std::variant<TransformPerCompNodeData, std::vector<TransformPerCompNodeData>>;
|
||||||
using LogicCompData = std::variant<LogicPerCompData, std::vector<LogicPerCompData>>;
|
using LogicCompData = std::variant<LogicPerCompData, std::vector<LogicPerCompData>>;
|
||||||
@@ -81,6 +101,48 @@ private:
|
|||||||
using TfCompTreeTypeList = TL::CreateFrom_t<TransformCompNodeData, TransformCompCommonNodeData>;
|
using TfCompTreeTypeList = TL::CreateFrom_t<TransformCompNodeData, TransformCompCommonNodeData>;
|
||||||
using FlatTfCompTree = FlatTree<TfCompTreeTypeList, uint32>;
|
using FlatTfCompTree = FlatTree<TfCompTreeTypeList, uint32>;
|
||||||
|
|
||||||
|
struct ComponentListRef
|
||||||
|
{
|
||||||
|
cbe::WeakObjectPtr comp;
|
||||||
|
|
||||||
|
/* Will be node index for tf and leaf component.
|
||||||
|
* Will be logic component index for logic component. */
|
||||||
|
uint32 idx;
|
||||||
|
|
||||||
|
bool bLeaf:1;
|
||||||
|
bool bLogic:1;
|
||||||
|
};
|
||||||
|
struct AddComponentData
|
||||||
|
{
|
||||||
|
String compName;
|
||||||
|
std::vector<cbe::ObjectPath> selCompPerActor;
|
||||||
|
CBEClass componentClazz;
|
||||||
|
StringView transactionText;
|
||||||
|
bool bIssueTransaction;
|
||||||
|
bool bValidName;
|
||||||
|
};
|
||||||
|
struct RemoveComponentData
|
||||||
|
{
|
||||||
|
std::string compName;
|
||||||
|
ComponentListRef compRef;
|
||||||
|
bool bIssueTransaction;
|
||||||
|
};
|
||||||
|
struct RenameComponentData
|
||||||
|
{
|
||||||
|
std::string compName;
|
||||||
|
std::string newName;
|
||||||
|
ComponentListRef compRef;
|
||||||
|
bool bIssueTransaction;
|
||||||
|
};
|
||||||
|
struct ChangeRootComponentData
|
||||||
|
{
|
||||||
|
std::string compName;
|
||||||
|
ComponentListRef compRef;
|
||||||
|
bool bIssueTransaction;
|
||||||
|
};
|
||||||
|
using ComponentManipulationData
|
||||||
|
= std::variant<NullType, AddComponentData, RemoveComponentData, RenameComponentData, ChangeRootComponentData>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isMultiSelection() const { return selectedActors.size() > 1; }
|
bool isMultiSelection() const { return selectedActors.size() > 1; }
|
||||||
void buildTransformCompTree();
|
void buildTransformCompTree();
|
||||||
@@ -98,6 +160,16 @@ private:
|
|||||||
void drawTfNodeDetails(FlatTfCompTree::NodeIdx nodeIdx);
|
void drawTfNodeDetails(FlatTfCompTree::NodeIdx nodeIdx);
|
||||||
void drawLogicCompDetails(uint32 logicCompIdx);
|
void drawLogicCompDetails(uint32 logicCompIdx);
|
||||||
|
|
||||||
|
void drawAddComponentMenu();
|
||||||
|
void drawCompContextMenu(const cbe::ObjectPath &objPath, CBEClass clazz, uint32 idx, bool bLogicComp, bool bLeafComp);
|
||||||
|
void drawAddComponentModal();
|
||||||
|
void drawRemoveComponentModal();
|
||||||
|
void drawRenameComponentModal();
|
||||||
|
void drawChangeRootComponentModal();
|
||||||
|
|
||||||
|
void drawCompDragSource(const cbe::ObjectPath &objPath, uint32 idx);
|
||||||
|
void drawTfCompTreeDragTarget(const cbe::ObjectPath &objPath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
constexpr static const AChar *MULTIPLE_SELECTION_TXT = "Multiple";
|
constexpr static const AChar *MULTIPLE_SELECTION_TXT = "Multiple";
|
||||||
constexpr static const float COMP_LIST_FRAME_SIZE = 0.2f;
|
constexpr static const float COMP_LIST_FRAME_SIZE = 0.2f;
|
||||||
@@ -124,6 +196,8 @@ private:
|
|||||||
FlatTfCompTree::NodeIdx selectedTfNodeIdx = FlatTfCompTree::InvalidIdx;
|
FlatTfCompTree::NodeIdx selectedTfNodeIdx = FlatTfCompTree::InvalidIdx;
|
||||||
|
|
||||||
String filterName;
|
String filterName;
|
||||||
|
ComponentManipulationData compEditData;
|
||||||
|
|
||||||
std::string detailsWndName;
|
std::string detailsWndName;
|
||||||
|
cbe::EDetailsFlags detailsFlags;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -13,6 +13,8 @@
|
|||||||
#include <Widgets/WgEditorConstants.hpp>
|
#include <Widgets/WgEditorConstants.hpp>
|
||||||
#include <Widgets/WgViewportImGuiLayer.h>
|
#include <Widgets/WgViewportImGuiLayer.h>
|
||||||
#include <Widgets/WgDetailsImGuiLayer.h>
|
#include <Widgets/WgDetailsImGuiLayer.h>
|
||||||
|
#include <ICoreObjectsModule.h>
|
||||||
|
#include <CoreObjectGC.h>
|
||||||
#include <CbeApplication.h>
|
#include <CbeApplication.h>
|
||||||
#include <EditorHelpers.h>
|
#include <EditorHelpers.h>
|
||||||
#include <IApplicationModule.h>
|
#include <IApplicationModule.h>
|
||||||
@@ -29,6 +31,9 @@
|
|||||||
#include <AssetEditors/IEngineAssetEditor.hpp>
|
#include <AssetEditors/IEngineAssetEditor.hpp>
|
||||||
#include <Widgets/EditorWidgetsHelper.hpp>
|
#include <Widgets/EditorWidgetsHelper.hpp>
|
||||||
|
|
||||||
|
constexpr const AChar *PROGRESS_TRACKER_MODAL_POPUP = "###ProgressTracker";
|
||||||
|
static const TickRep PROGRESS_DOT_ANIM_TICKS = Time::fromSeconds(1.0f);
|
||||||
|
|
||||||
WgEditorImGuiLayer::WgEditorImGuiLayer(const WgEditorCreateInfo &ci)
|
WgEditorImGuiLayer::WgEditorImGuiLayer(const WgEditorCreateInfo &ci)
|
||||||
: ownerRoot(ci.rootEd.get())
|
: ownerRoot(ci.rootEd.get())
|
||||||
, wnd(ci.windowWidget)
|
, wnd(ci.windowWidget)
|
||||||
@@ -41,9 +46,9 @@ WgEditorImGuiLayer::WgEditorImGuiLayer(const WgEditorCreateInfo &ci)
|
|||||||
|
|
||||||
void WgEditorImGuiLayer::drawImGui()
|
void WgEditorImGuiLayer::drawImGui()
|
||||||
{
|
{
|
||||||
if (bShowDemo)
|
if (demoData.bShowImGuiDemo)
|
||||||
{
|
{
|
||||||
ImGui::ShowDemoWindow(&bShowDemo);
|
ImGui::ShowDemoWindow(&demoData.bShowImGuiDemo);
|
||||||
}
|
}
|
||||||
if (bShowStyleEditor)
|
if (bShowStyleEditor)
|
||||||
{
|
{
|
||||||
@@ -51,6 +56,23 @@ void WgEditorImGuiLayer::drawImGui()
|
|||||||
ImGui::ShowStyleEditor();
|
ImGui::ShowStyleEditor();
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
if (bShowImGuiMetricWnd)
|
||||||
|
{
|
||||||
|
ImGui::ShowMetricsWindow(&bShowImGuiMetricWnd);
|
||||||
|
}
|
||||||
|
if (bShowDebugLog)
|
||||||
|
{
|
||||||
|
ImGui::ShowDebugLogWindow(&bShowDebugLog);
|
||||||
|
}
|
||||||
|
if (bShowImGuiIdStack)
|
||||||
|
{
|
||||||
|
ImGui::ShowIDStackToolWindow(&bShowImGuiIdStack);
|
||||||
|
}
|
||||||
|
if (demoData.bShowEngineWgDemo)
|
||||||
|
{
|
||||||
|
drawEngineWidgetDemo();
|
||||||
|
}
|
||||||
|
|
||||||
/* Needed to manually set and reset the value of window menu button icon */
|
/* Needed to manually set and reset the value of window menu button icon */
|
||||||
ImGuiStyle &style = ImGui::GetStyle();
|
ImGuiStyle &style = ImGui::GetStyle();
|
||||||
|
|
||||||
@@ -124,24 +146,138 @@ void WgEditorImGuiLayer::removeMenuExtender(const TChar *menuName, DelegateHandl
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WgEditorImGuiLayer::addNotification(const cbe::NotificationInfo &info)
|
void WgEditorImGuiLayer::addNotification(const cbe::NotificationInfo &info)
|
||||||
|
{
|
||||||
|
const uint64 allocId = addStickyNotification(info);
|
||||||
|
notifyAlloc.getAllocAt(allocId)->bSticky = false;
|
||||||
|
}
|
||||||
|
uint64 WgEditorImGuiLayer::addStickyNotification(const cbe::NotificationInfo &info)
|
||||||
{
|
{
|
||||||
if (currNotifyCount >= maxNotifies)
|
if (currNotifyCount >= maxNotifies)
|
||||||
{
|
{
|
||||||
/* Remove the oldest */
|
/* Remove the oldest */
|
||||||
NotifyDrawInfo *tailNotification = LinkedListHelpers::tail(notifyHead);
|
NotifyDrawInfo *rmNotification = LinkedListHelpers::tail(notifyHead);
|
||||||
LinkedListHelpers::remove(¬ifyHead, tailNotification, nullptr);
|
/* Find earliest non sticky notification */
|
||||||
tailNotification->~NotifyDrawInfo();
|
rmNotification = LinkedListHelpers::rfind(
|
||||||
notifyAlloc.free(tailNotification);
|
notifyHead, rmNotification,
|
||||||
currNotifyCount--;
|
[](const NotifyDrawInfo ¬ification)
|
||||||
|
{
|
||||||
|
return !notification.bSticky;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rmNotification != nullptr)
|
||||||
|
{
|
||||||
|
remNotification(rmNotification);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NotifyDrawInfo *notification = new (notifyAlloc.getAllocAt(notifyAlloc.allocate())) NotifyDrawInfo{
|
const uint64 allocIdx = notifyAlloc.allocate<uint64>();
|
||||||
|
NotifyDrawInfo *notification = new (notifyAlloc.getAllocAt(allocIdx)) NotifyDrawInfo{
|
||||||
.name = std::string{ TCHAR_TO_UTF8(info.name) },
|
.name = std::string{ TCHAR_TO_UTF8(info.name) },
|
||||||
.endAt = Time::fromSeconds(info.durrInSeconds) + Time::timeNow(),
|
.endAt = Time::fromSeconds(info.durrInSeconds) + Time::timeNow(),
|
||||||
.cb = std::move(info.callback),
|
.cb = std::move(info.callback),
|
||||||
|
.bSticky = true,
|
||||||
};
|
};
|
||||||
LinkedListHelpers::pushHead(¬ifyHead, notification);
|
LinkedListHelpers::pushHead(¬ifyHead, notification);
|
||||||
currNotifyCount++;
|
currNotifyCount++;
|
||||||
|
|
||||||
|
return allocIdx;
|
||||||
|
}
|
||||||
|
void WgEditorImGuiLayer::removeStickyNotification(uint64 stickyId)
|
||||||
|
{
|
||||||
|
if (!notifyAlloc.isValid(stickyId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NotifyDrawInfo *notification = notifyAlloc.getAllocAt(stickyId);
|
||||||
|
remNotification(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
cbe::ProgressTrackerHnd WgEditorImGuiLayer::addProgressTracker(const cbe::ProgressTrackerInfo &info)
|
||||||
|
{
|
||||||
|
const cbe::ProgressTrackerHnd hnd = progTrackerAlloc.allocate<cbe::ProgressTrackerHnd>();
|
||||||
|
|
||||||
|
ProgressDrawInfo &progTracker = *new (progTrackerAlloc.getAllocAt(hnd)) ProgressDrawInfo{
|
||||||
|
.maxSteps = info.stepsCount,
|
||||||
|
.stepDesc = "",
|
||||||
|
.jobName = std::string{ TCHAR_TO_UTF8(info.name) },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (info.stepsCount == 0)
|
||||||
|
{
|
||||||
|
progTracker.maxSteps = std::numeric_limits<uint16>::max();
|
||||||
|
progTracker.bContinuous = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.bBackground)
|
||||||
|
{
|
||||||
|
cbe::NotificationInfo notifyInfo{ .name = info.name };
|
||||||
|
notifyInfo.callback.bindObject(this, &WgEditorImGuiLayer::drawBackgroundProgressTracker, hnd);
|
||||||
|
progTracker.stickyId = addStickyNotification(notifyInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkedListHelpers::pushHead(&progTrackerHead, &progTracker);
|
||||||
|
|
||||||
|
return hnd;
|
||||||
|
}
|
||||||
|
void WgEditorImGuiLayer::progressProgressTracker(cbe::ProgressTrackerHnd hnd, uint32 stepsCount, StringView stepDesc)
|
||||||
|
{
|
||||||
|
if (!progTrackerAlloc.isValid(hnd))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressDrawInfo &progTracker = *progTrackerAlloc.getAllocAt(hnd);
|
||||||
|
if (!stepDesc.empty())
|
||||||
|
{
|
||||||
|
progTracker.stepDesc = TCHAR_TO_UTF8(stepDesc);
|
||||||
|
}
|
||||||
|
debugAssertf(
|
||||||
|
progTracker.maxSteps >= (progTracker.currProgress.load(std::memory_order::relaxed) + stepsCount),
|
||||||
|
"Progress steps cannot go beyond max steps!"
|
||||||
|
);
|
||||||
|
progTracker.currProgress.fetch_add(stepsCount, std::memory_order::release);
|
||||||
|
}
|
||||||
|
void WgEditorImGuiLayer::setProgressTracerProgress(cbe::ProgressTrackerHnd hnd, float progress)
|
||||||
|
{
|
||||||
|
if (!progTrackerAlloc.isValid(hnd))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressDrawInfo &progTracker = *progTrackerAlloc.getAllocAt(hnd);
|
||||||
|
debugAssertf(progTracker.maxSteps == std::numeric_limits<uint16>::max(), "Continuous progress must be quantized to 16bit");
|
||||||
|
constexpr float progressScale = static_cast<float>(std::numeric_limits<uint16>::max() / 100000.0);
|
||||||
|
|
||||||
|
debugAssertf(progress >= 0.0f && progress <= 1.0f, "Progress must be between 0.0 to 1.0");
|
||||||
|
const uint32 quantProgress = static_cast<uint32>((progressScale * progress) * 100000.0);
|
||||||
|
|
||||||
|
if (quantProgress > progTracker.currProgress.load(std::memory_order::relaxed))
|
||||||
|
{
|
||||||
|
progTracker.currProgress.store(quantProgress, std::memory_order::release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool WgEditorImGuiLayer::isProgressTrackerCancelling(cbe::ProgressTrackerHnd hnd) const
|
||||||
|
{
|
||||||
|
if (!progTrackerAlloc.isValid(hnd))
|
||||||
|
{
|
||||||
|
/* Can never cancel */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return progTrackerAlloc.getAllocAt(hnd)->bCancelReq;
|
||||||
|
}
|
||||||
|
void WgEditorImGuiLayer::cancelProgressTracker(cbe::ProgressTrackerHnd hnd)
|
||||||
|
{
|
||||||
|
if (!progTrackerAlloc.isValid(hnd))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressDrawInfo &progTracker = *progTrackerAlloc.getAllocAt(hnd);
|
||||||
|
/* Immediately set the current progress to max and let next frame clear. */
|
||||||
|
progTracker.currProgress.store(progTracker.maxSteps, std::memory_order::relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WgEditorImGuiLayer::addAssetEditor(const cbe::ICoreAssetEditorRef &assetEditor)
|
void WgEditorImGuiLayer::addAssetEditor(const cbe::ICoreAssetEditorRef &assetEditor)
|
||||||
@@ -211,7 +347,239 @@ cbe::ETryExit WgEditorImGuiLayer::tryExitEditor()
|
|||||||
}
|
}
|
||||||
assetEdDat.closeState = cbe::DestroyState_Requested;
|
assetEdDat.closeState = cbe::DestroyState_Requested;
|
||||||
}
|
}
|
||||||
return assetEditors.empty() ? cbe::TryExit_Success : cbe::TryExit_Wait;
|
if (!assetEditors.empty())
|
||||||
|
{
|
||||||
|
return cbe::TryExit_Wait;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for any progress trackers */
|
||||||
|
return progTrackerHead != nullptr ? cbe::TryExit_Wait : cbe::TryExit_Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WgEditorImGuiLayer::drawEngineWidgetDemo()
|
||||||
|
{
|
||||||
|
ImGui::SetNextWindowSize(ImVec2{}, ImGuiCond_Appearing);
|
||||||
|
ImGui::SetNextWindowSizeConstraints(cbe::wg_consts::VMODAL_POPUP_SIZE, Vector2{ std::numeric_limits<float>::max() });
|
||||||
|
if (ImGui::Begin("Engine Widget Demo", &demoData.bShowEngineWgDemo))
|
||||||
|
{
|
||||||
|
if (ImGui::TreeNode("Notification Popup"))
|
||||||
|
{
|
||||||
|
ImGui::PushID("Basics");
|
||||||
|
ImGui::SeparatorText("Basic");
|
||||||
|
|
||||||
|
cbe::NotificationInfo info = {};
|
||||||
|
bool bIssueNotification = false;
|
||||||
|
bool bStickyNotification = false;
|
||||||
|
|
||||||
|
if (ImGui::Button("1s"))
|
||||||
|
{
|
||||||
|
bIssueNotification = true;
|
||||||
|
info.durrInSeconds = 1;
|
||||||
|
info.name = TCHAR("Demo Notification 1s");
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("Issues notification with 1 second lifetime");
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("2s"))
|
||||||
|
{
|
||||||
|
bIssueNotification = true;
|
||||||
|
info.durrInSeconds = 2;
|
||||||
|
info.name = TCHAR("Demo Notification 2s");
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("Issues notification with 2 seconds lifetime");
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("5s"))
|
||||||
|
{
|
||||||
|
bIssueNotification = true;
|
||||||
|
info.durrInSeconds = 5;
|
||||||
|
info.name = TCHAR("Demo Notification 5s");
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("Issues notification with 5 seconds lifetime");
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("sticky"))
|
||||||
|
{
|
||||||
|
info.name = TCHAR("Demo Notification Sticky");
|
||||||
|
bIssueNotification = true;
|
||||||
|
bStickyNotification = true;
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("Issues sticky notification");
|
||||||
|
|
||||||
|
if (bIssueNotification)
|
||||||
|
{
|
||||||
|
if (bStickyNotification)
|
||||||
|
{
|
||||||
|
addStickyNotification(info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addNotification(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
bIssueNotification = false;
|
||||||
|
bStickyNotification = false;
|
||||||
|
info = {};
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
|
||||||
|
ImGui::PushID("With Callback");
|
||||||
|
ImGui::SeparatorText("With Callback");
|
||||||
|
auto cb = []
|
||||||
|
{
|
||||||
|
// Animate a simple progress bar
|
||||||
|
static float progress = 0.0f, progress_dir = 1.0f;
|
||||||
|
progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime;
|
||||||
|
if (progress >= +1.1f)
|
||||||
|
{
|
||||||
|
progress = +1.1f;
|
||||||
|
progress_dir *= -1.0f;
|
||||||
|
}
|
||||||
|
if (progress <= -0.1f)
|
||||||
|
{
|
||||||
|
progress = -0.1f;
|
||||||
|
progress_dir *= -1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width,
|
||||||
|
// or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth.
|
||||||
|
ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f));
|
||||||
|
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||||
|
ImGui::Text("Progress Bar");
|
||||||
|
|
||||||
|
float progress_saturated = Math::clamp(progress, 0.0f, 1.0f);
|
||||||
|
char buf[32];
|
||||||
|
sprintf_s(buf, 32, "%d/%d", (int)(progress_saturated * 1753), 1753);
|
||||||
|
ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf);
|
||||||
|
|
||||||
|
// Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value.
|
||||||
|
// Adjust the factor if you want to adjust the animation speed.
|
||||||
|
ImGui::ProgressBar(-1.0f * (float)ImGui::GetTime(), ImVec2(0.0f, 0.0f), "Searching..");
|
||||||
|
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||||
|
ImGui::Text("Indeterminate");
|
||||||
|
};
|
||||||
|
info.callback.bindLambda(cb);
|
||||||
|
|
||||||
|
if (ImGui::Button("5s"))
|
||||||
|
{
|
||||||
|
bIssueNotification = true;
|
||||||
|
info.durrInSeconds = 5;
|
||||||
|
info.name = TCHAR("Demo Notification 5s");
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("Issues notification with 5 seconds lifetime");
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("sticky"))
|
||||||
|
{
|
||||||
|
info.name = TCHAR("Demo Notification Sticky");
|
||||||
|
bIssueNotification = true;
|
||||||
|
bStickyNotification = true;
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("Issues sticky notification");
|
||||||
|
|
||||||
|
if (bIssueNotification)
|
||||||
|
{
|
||||||
|
if (bStickyNotification)
|
||||||
|
{
|
||||||
|
addStickyNotification(info);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addNotification(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
bIssueNotification = false;
|
||||||
|
bStickyNotification = false;
|
||||||
|
info = {};
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::TreeNode("Progress Tracker"))
|
||||||
|
{
|
||||||
|
ImGui::BeginDisabled(progTrackerAlloc.isValid(demoData.progHnd));
|
||||||
|
if (ImGui::Button("FG"))
|
||||||
|
{
|
||||||
|
cbe::ProgressTrackerInfo info{
|
||||||
|
.name = TCHAR("Demo FG Progress"),
|
||||||
|
.stepsCount = 300,
|
||||||
|
.bBackground = false,
|
||||||
|
};
|
||||||
|
demoData.progHnd = addProgressTracker(info);
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("Foreground Progress Bar");
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(progTrackerAlloc.isValid(demoData.progHndBg));
|
||||||
|
if (ImGui::Button("BG"))
|
||||||
|
{
|
||||||
|
cbe::ProgressTrackerInfo info{
|
||||||
|
.name = TCHAR("Demo BG Progress"),
|
||||||
|
.stepsCount = 300,
|
||||||
|
.bBackground = true,
|
||||||
|
};
|
||||||
|
demoData.progHndBg = addProgressTracker(info);
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("Background Progress Bar");
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(progTrackerAlloc.isValid(demoData.cProgHnd));
|
||||||
|
if (ImGui::Button("FG Continuous"))
|
||||||
|
{
|
||||||
|
cbe::ProgressTrackerInfo info{
|
||||||
|
.name = TCHAR("Demo FG Continuous Progress"),
|
||||||
|
.stepsCount = 0,
|
||||||
|
.bBackground = false,
|
||||||
|
};
|
||||||
|
demoData.cProgHnd = addProgressTracker(info);
|
||||||
|
demoData.cProg = 0.0f;
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("Foreground Continuous Progress Bar");
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(progTrackerAlloc.isValid(demoData.cProgHndBg));
|
||||||
|
if (ImGui::Button("BG Continuous"))
|
||||||
|
{
|
||||||
|
cbe::ProgressTrackerInfo info{
|
||||||
|
.name = TCHAR("Demo BG Continuous Progress"),
|
||||||
|
.stepsCount = 0,
|
||||||
|
.bBackground = true,
|
||||||
|
};
|
||||||
|
demoData.cProgHndBg = addProgressTracker(info);
|
||||||
|
demoData.cProgBg = 0.0f;
|
||||||
|
}
|
||||||
|
ImGui::SetItemTooltip("Background Continuous Progress Bar");
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
|
if (progTrackerAlloc.isValid(demoData.progHnd))
|
||||||
|
{
|
||||||
|
progressProgressTracker(demoData.progHnd, 1, TCHAR("Demo FG Step"));
|
||||||
|
}
|
||||||
|
if (progTrackerAlloc.isValid(demoData.progHndBg))
|
||||||
|
{
|
||||||
|
progressProgressTracker(demoData.progHndBg, 1, TCHAR("Demo BG Step"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progTrackerAlloc.isValid(demoData.cProgHnd))
|
||||||
|
{
|
||||||
|
demoData.cProg += 0.01f;
|
||||||
|
demoData.cProg = Math::clamp(demoData.cProg, 0.0f, 1.0f);
|
||||||
|
setProgressTracerProgress(demoData.cProgHnd, demoData.cProg);
|
||||||
|
}
|
||||||
|
if (progTrackerAlloc.isValid(demoData.cProgHndBg))
|
||||||
|
{
|
||||||
|
demoData.cProgBg += 0.01f;
|
||||||
|
demoData.cProgBg = Math::clamp(demoData.cProgBg, 0.0f, 1.0f);
|
||||||
|
setProgressTracerProgress(demoData.cProgHndBg, demoData.cProgBg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WgEditorImGuiLayer::drawEditorWindows()
|
void WgEditorImGuiLayer::drawEditorWindows()
|
||||||
@@ -242,12 +610,11 @@ void WgEditorImGuiLayer::drawEditorWindows()
|
|||||||
ImGui::SetNextWindowDockID(ED_DOCK_SPACE);
|
ImGui::SetNextWindowDockID(ED_DOCK_SPACE);
|
||||||
|
|
||||||
cbe::World *mainWorld = gCBEEngine->worldManager()->getMainWorld();
|
cbe::World *mainWorld = gCBEEngine->worldManager()->getMainWorld();
|
||||||
const cbe::World *editorWorld = gCBEEngine->worldManager()->getEditorWorld();
|
|
||||||
bool bWorldOpen = cbe::isValidFast(mainWorld);
|
bool bWorldOpen = cbe::isValidFast(mainWorld);
|
||||||
const std::string windowTitle
|
const std::string windowTitle
|
||||||
= std::format("{}{}", bWorldOpen ? TCHAR_TO_UTF8(mainWorld->getObjectData().name) : "None", cbe::wg_consts::ED_MAIN_WINDOW);
|
= std::format("{}{}", bWorldOpen ? TCHAR_TO_UTF8(mainWorld->getObjectData().name) : "None", cbe::wg_consts::ED_MAIN_WINDOW);
|
||||||
ImGuiWindowFlags edWindowFlags = edWndBaseFlags | ImGuiWindowFlags_MenuBar;
|
ImGuiWindowFlags edWindowFlags = edWndBaseFlags | ImGuiWindowFlags_MenuBar;
|
||||||
if (cbe::isValidFast(editorWorld) && cbe::isPackageDirty(editorWorld))
|
if (cbe::gCBEditorEngine->getMainEd()->isAssetDirty())
|
||||||
{
|
{
|
||||||
edWindowFlags |= ImGuiWindowFlags_UnsavedDocument;
|
edWindowFlags |= ImGuiWindowFlags_UnsavedDocument;
|
||||||
}
|
}
|
||||||
@@ -303,6 +670,8 @@ void WgEditorImGuiLayer::drawEditorWindows()
|
|||||||
ImGui::PopStyleColor(1);
|
ImGui::PopStyleColor(1);
|
||||||
|
|
||||||
addMenubar();
|
addMenubar();
|
||||||
|
|
||||||
|
drawProgressTrackers();
|
||||||
drawNotifications();
|
drawNotifications();
|
||||||
|
|
||||||
/* Handle shortcuts. Do not handle if pop up is focused. */
|
/* Handle shortcuts. Do not handle if pop up is focused. */
|
||||||
@@ -447,6 +816,7 @@ void WgEditorImGuiLayer::drawEditorWindows()
|
|||||||
/* Only one window must be visible at any time. So draw notification inside it. */
|
/* Only one window must be visible at any time. So draw notification inside it. */
|
||||||
if (ImGui::GetWindowDockID() == ED_DOCK_SPACE)
|
if (ImGui::GetWindowDockID() == ED_DOCK_SPACE)
|
||||||
{
|
{
|
||||||
|
drawProgressTrackers();
|
||||||
drawNotifications();
|
drawNotifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,8 +874,27 @@ void WgEditorImGuiLayer::addMenubar()
|
|||||||
if (ImGui::BeginMenu("Developer"))
|
if (ImGui::BeginMenu("Developer"))
|
||||||
{
|
{
|
||||||
ImGui::MenuItem("CoPaT stats", nullptr, &bShowJobQueueStats);
|
ImGui::MenuItem("CoPaT stats", nullptr, &bShowJobQueueStats);
|
||||||
ImGui::MenuItem("ImGUI demo", nullptr, &bShowDemo);
|
ImGui::MenuItem("ImGui Metrics", nullptr, &bShowImGuiMetricWnd);
|
||||||
|
|
||||||
|
ImGui::SeparatorText("ImGui");
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("Item Picker", NULL, false, PlatformFunctions::hasAttachedDebugger()))
|
||||||
|
{
|
||||||
|
ImGui::DebugStartItemPicker();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::MenuItem("ID Stack", nullptr, &bShowImGuiIdStack);
|
||||||
ImGui::MenuItem("Style Editor", nullptr, &bShowStyleEditor);
|
ImGui::MenuItem("Style Editor", nullptr, &bShowStyleEditor);
|
||||||
|
ImGui::MenuItem("Debug Log", nullptr, &bShowDebugLog);
|
||||||
|
|
||||||
|
if (ImGui::BeginMenu("Test UI"))
|
||||||
|
{
|
||||||
|
ImGui::MenuItem("ImGUI demo", nullptr, &demoData.bShowImGuiDemo);
|
||||||
|
ImGui::MenuItem("Engine widget demo", nullptr, &demoData.bShowEngineWgDemo);
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,8 +1015,13 @@ void WgEditorImGuiLayer::undo()
|
|||||||
}
|
}
|
||||||
if (!editedObjs.empty())
|
if (!editedObjs.empty())
|
||||||
{
|
{
|
||||||
|
std::vector<cbe::WeakObjectPtr> weakObjPtrs{ editedObjs.size() };
|
||||||
|
for (SizeT i = 0; i < editedObjs.size(); ++i)
|
||||||
|
{
|
||||||
|
weakObjPtrs[i] = editedObjs[i];
|
||||||
|
}
|
||||||
ownerRoot->pushMessage(
|
ownerRoot->pushMessage(
|
||||||
cbe::MessageData{ .issuerData = std::bit_cast<uint64>(this), .msg = std::move(editedObjs) }, cbe::CoreEdMessage_ObjsEdited
|
cbe::MessageData{ .issuerData = std::bit_cast<uint64>(this), .msg = std::move(weakObjPtrs) }, cbe::CoreEdMessage_ObjsEdited
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -644,8 +1038,13 @@ void WgEditorImGuiLayer::redo()
|
|||||||
|
|
||||||
if (!editedObjs.empty())
|
if (!editedObjs.empty())
|
||||||
{
|
{
|
||||||
|
std::vector<cbe::WeakObjectPtr> weakObjPtrs{ editedObjs.size() };
|
||||||
|
for (SizeT i = 0; i < editedObjs.size(); ++i)
|
||||||
|
{
|
||||||
|
weakObjPtrs[i] = editedObjs[i];
|
||||||
|
}
|
||||||
ownerRoot->pushMessage(
|
ownerRoot->pushMessage(
|
||||||
cbe::MessageData{ .issuerData = std::bit_cast<uint64>(this), .msg = std::move(editedObjs) }, cbe::CoreEdMessage_ObjsEdited
|
cbe::MessageData{ .issuerData = std::bit_cast<uint64>(this), .msg = std::move(weakObjPtrs) }, cbe::CoreEdMessage_ObjsEdited
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,8 +1077,7 @@ void WgEditorImGuiLayer::drawNotifications()
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ImGuiStyle &style = ImGui::GetStyle();
|
const ImGuiStyle &style = ImGui::GetStyle();
|
||||||
|
ImGuiViewport *viewport = ImGui::GetWindowViewport() != nullptr ? ImGui::GetWindowViewport() : ImGui::GetMainViewport();
|
||||||
ImGuiViewport *viewport = ImGui::GetMainViewport();
|
|
||||||
|
|
||||||
const Vector2 notificationSpacing{ ImGui::GetFontSize(), ImGui::GetTextLineHeight() };
|
const Vector2 notificationSpacing{ ImGui::GetFontSize(), ImGui::GetTextLineHeight() };
|
||||||
|
|
||||||
@@ -699,8 +1097,20 @@ void WgEditorImGuiLayer::drawNotifications()
|
|||||||
uint32 idx = 0;
|
uint32 idx = 0;
|
||||||
while (notification != nullptr)
|
while (notification != nullptr)
|
||||||
{
|
{
|
||||||
ImGui::SetNextWindowPos(nextNotifyPos, ImGuiCond_Always);
|
if (notification->cb)
|
||||||
|
{
|
||||||
|
/* Custom callback must be able to resize in Y */
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(notifyWindowSize.x, 0.0f), ImGuiCond_Always);
|
||||||
|
|
||||||
|
/* Additional offset necessary to avoid overlapping notification below. Delta between ideal size and generated size */
|
||||||
|
nextNotifyPos.y -= (notification->wndHeight - notifyWindowSize.y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
ImGui::SetNextWindowSize(notifyWindowSize, ImGuiCond_Always);
|
ImGui::SetNextWindowSize(notifyWindowSize, ImGuiCond_Always);
|
||||||
|
}
|
||||||
|
ImGui::SetNextWindowPos(nextNotifyPos, ImGuiCond_Always);
|
||||||
|
ImGui::SetNextWindowSizeConstraints(notifyWindowSize, Vector2{ std::numeric_limits<float>::max() });
|
||||||
ImGui::SetNextWindowBgAlpha(0.8f);
|
ImGui::SetNextWindowBgAlpha(0.8f);
|
||||||
|
|
||||||
ImGui::Begin(
|
ImGui::Begin(
|
||||||
@@ -708,6 +1118,8 @@ void WgEditorImGuiLayer::drawNotifications()
|
|||||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoMove
|
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoMove
|
||||||
);
|
);
|
||||||
ImGui::TextUnformatted(notification->name.c_str(), notification->name.c_str() + notification->name.length());
|
ImGui::TextUnformatted(notification->name.c_str(), notification->name.c_str() + notification->name.length());
|
||||||
|
ImGui::SetItemTooltip(notification->name.c_str());
|
||||||
|
|
||||||
ImGui::SameLine(notifyWindowSize.x - cbe::ImGuiHelpers::calcButtonSize("x").x - style.FramePadding.x);
|
ImGui::SameLine(notifyWindowSize.x - cbe::ImGuiHelpers::calcButtonSize("x").x - style.FramePadding.x);
|
||||||
if (cbe::ImGuiHelpers::buttonNoBg("x"))
|
if (cbe::ImGuiHelpers::buttonNoBg("x"))
|
||||||
{
|
{
|
||||||
@@ -718,27 +1130,209 @@ void WgEditorImGuiLayer::drawNotifications()
|
|||||||
{
|
{
|
||||||
notification->cb.invoke();
|
notification->cb.invoke();
|
||||||
}
|
}
|
||||||
|
notification->wndHeight = ImGui::GetWindowSize().y;
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
notification = LinkedListHelpers::next(notification);
|
|
||||||
idx++;
|
idx++;
|
||||||
nextNotifyPos.y -= (notifyWindowSize.y + notificationSpacing.y);
|
nextNotifyPos.y -= (notifyWindowSize.y + notificationSpacing.y);
|
||||||
|
|
||||||
|
notification = LinkedListHelpers::next(notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto remNotification = [this](NotifyDrawInfo *notificationToRemove)
|
|
||||||
{
|
|
||||||
LinkedListHelpers::remove(¬ifyHead, notificationToRemove, nullptr);
|
|
||||||
notificationToRemove->~NotifyDrawInfo();
|
|
||||||
notifyAlloc.free(notificationToRemove);
|
|
||||||
currNotifyCount--;
|
|
||||||
};
|
|
||||||
if (notificationToRemove != nullptr)
|
if (notificationToRemove != nullptr)
|
||||||
{
|
{
|
||||||
remNotification(notificationToRemove);
|
remNotification(notificationToRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Skip to first non sticky notification from back. */
|
||||||
NotifyDrawInfo *tailNotification = LinkedListHelpers::tail(notifyHead);
|
NotifyDrawInfo *tailNotification = LinkedListHelpers::tail(notifyHead);
|
||||||
|
tailNotification = LinkedListHelpers::rfind(
|
||||||
|
notifyHead, tailNotification,
|
||||||
|
[](const NotifyDrawInfo ¬ification)
|
||||||
|
{
|
||||||
|
return !notification.bSticky;
|
||||||
|
}
|
||||||
|
);
|
||||||
if (tailNotification != nullptr && tailNotification->endAt <= Time::timeNow())
|
if (tailNotification != nullptr && tailNotification->endAt <= Time::timeNow())
|
||||||
{
|
{
|
||||||
remNotification(tailNotification);
|
remNotification(tailNotification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WgEditorImGuiLayer::remNotification(NotifyDrawInfo *rmNotification)
|
||||||
|
{
|
||||||
|
LinkedListHelpers::remove(¬ifyHead, rmNotification, nullptr);
|
||||||
|
rmNotification->~NotifyDrawInfo();
|
||||||
|
notifyAlloc.free(rmNotification);
|
||||||
|
currNotifyCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WgEditorImGuiLayer::drawProgressTrackers()
|
||||||
|
{
|
||||||
|
/* Clear the progress trackers that reached the end first. */
|
||||||
|
ProgressDrawInfo *progTracker = progTrackerHead;
|
||||||
|
while (progTracker != nullptr)
|
||||||
|
{
|
||||||
|
ProgressDrawInfo *nextTracker = LinkedListHelpers::next(progTracker);
|
||||||
|
|
||||||
|
if (progTracker->currProgress.load(std::memory_order::relaxed) < progTracker->maxSteps)
|
||||||
|
{
|
||||||
|
progTracker = nextTracker;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove the progTracker from list and erase it from allocator */
|
||||||
|
if (progTracker->stickyId != 0)
|
||||||
|
{
|
||||||
|
removeStickyNotification(progTracker->stickyId);
|
||||||
|
}
|
||||||
|
LinkedListHelpers::remove(&progTrackerHead, progTracker, nullptr);
|
||||||
|
progTracker->~ProgressDrawInfo();
|
||||||
|
progTrackerAlloc.free(progTracker);
|
||||||
|
|
||||||
|
progTracker = nextTracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
progTracker = LinkedListHelpers::find(
|
||||||
|
progTrackerHead,
|
||||||
|
[](const ProgressDrawInfo &progTracker)
|
||||||
|
{
|
||||||
|
return progTracker.stickyId == 0;
|
||||||
|
},
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
if (progTracker == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ImGui::IsPopupOpen(PROGRESS_TRACKER_MODAL_POPUP))
|
||||||
|
{
|
||||||
|
ImGui::OpenPopup(PROGRESS_TRACKER_MODAL_POPUP);
|
||||||
|
|
||||||
|
ImGuiViewport *viewport = ImGui::GetWindowViewport() != nullptr ? ImGui::GetWindowViewport() : ImGui::GetMainViewport();
|
||||||
|
ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing, Vector2(0.5f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* To allow matching content size always */
|
||||||
|
ImGui::SetNextWindowSize(ImVec2{}, ImGuiCond_Always);
|
||||||
|
ImGui::SetNextWindowSizeConstraints(ImVec2{ cbe::wg_consts::HMODAL_POPUP_SIZE.x, 0.0f }, Vector2{ std::numeric_limits<float>::max() });
|
||||||
|
|
||||||
|
if (!ImGui::BeginPopupModal(PROGRESS_TRACKER_MODAL_POPUP, nullptr, ImGuiWindowFlags_NoDecoration))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto drawProg = [](ProgressDrawInfo *progTracker)
|
||||||
|
{
|
||||||
|
const uint32 dotCount = static_cast<uint32>(
|
||||||
|
(Time::timeNow() / static_cast<TickRep>(PROGRESS_DOT_ANIM_TICKS * cbe::wg_consts::ANIMATION_TIME_SCALE)) % 4u
|
||||||
|
);
|
||||||
|
ImGui::Text("%s%.*s", progTracker->jobName.c_str(), dotCount, "...");
|
||||||
|
|
||||||
|
const uint32 currProgress = progTracker->currProgress.load(std::memory_order::relaxed);
|
||||||
|
const float progress = static_cast<float>(currProgress) / progTracker->maxSteps;
|
||||||
|
if (!progTracker->bContinuous)
|
||||||
|
{
|
||||||
|
ImGui::Text("%u/%u %s", currProgress, progTracker->maxSteps, progTracker->stepDesc.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
const Vector2 buttonSize = cbe::ImGuiHelpers::calcButtonSize(cbe::mat_icon::CANCEL);
|
||||||
|
const float progressSize = ImGui::GetContentRegionAvail().x - buttonSize.x;
|
||||||
|
const float cursorPosY = ImGui::GetCursorPosY();
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, cbe::style::ED_COLOR_PRIMARY);
|
||||||
|
if (progTracker->bContinuous)
|
||||||
|
{
|
||||||
|
ImGui::ProgressBar(progress, ImVec2(progressSize, 0.0f));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
constexpr float PROGRESS_HEIGHT = 4.0f;
|
||||||
|
/* Vertically align the progress bar. Reset after drawing */
|
||||||
|
ImGui::SetCursorPosY(cursorPosY + ((buttonSize.y - PROGRESS_HEIGHT) * 0.5f));
|
||||||
|
ImGui::ProgressBar(progress, ImVec2(progressSize, PROGRESS_HEIGHT), "");
|
||||||
|
ImGui::SetCursorPosY(cursorPosY);
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
|
||||||
|
/* No spacing since button has padding and it is transparent */
|
||||||
|
ImGui::SameLine(0.0f, 0.0f);
|
||||||
|
/* Must set after SameLine since same line restores the Cursor Y from prev line. */
|
||||||
|
ImGui::SetCursorPosY(cursorPosY);
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(progTracker->bCancelReq);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, LinearColorConst::RED);
|
||||||
|
if (cbe::ImGuiHelpers::buttonNoBg(cbe::mat_icon::CANCEL))
|
||||||
|
{
|
||||||
|
progTracker->bCancelReq = true;
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
ImGui::SetItemTooltip("Try cancel this task!");
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
};
|
||||||
|
|
||||||
|
drawProg(progTracker);
|
||||||
|
progTracker = LinkedListHelpers::next(progTracker);
|
||||||
|
while (progTracker != nullptr)
|
||||||
|
{
|
||||||
|
/* Only draw non background progress tracker here. */
|
||||||
|
if (progTracker->stickyId == 0)
|
||||||
|
{
|
||||||
|
ImGui::Separator();
|
||||||
|
drawProg(progTracker);
|
||||||
|
}
|
||||||
|
progTracker = LinkedListHelpers::next(progTracker);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WgEditorImGuiLayer::drawBackgroundProgressTracker(cbe::ProgressTrackerHnd hnd) const
|
||||||
|
{
|
||||||
|
if (!progTrackerAlloc.isValid(hnd))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressDrawInfo *progTracker = progTrackerAlloc.getAllocAt(hnd);
|
||||||
|
|
||||||
|
const uint32 currProgress = progTracker->currProgress.load(std::memory_order::relaxed);
|
||||||
|
const float progress = static_cast<float>(currProgress) / progTracker->maxSteps;
|
||||||
|
|
||||||
|
const Vector2 buttonSize = cbe::ImGuiHelpers::calcButtonSize(cbe::mat_icon::CANCEL);
|
||||||
|
const float progressSize = ImGui::GetContentRegionAvail().x - buttonSize.x;
|
||||||
|
|
||||||
|
constexpr float PROGRESS_HEIGHT = 2.0f;
|
||||||
|
const float cursorPosY = ImGui::GetCursorPosY();
|
||||||
|
/* Vertically align the progress bar. Reset after drawing */
|
||||||
|
ImGui::SetCursorPosY(cursorPosY + ((buttonSize.y - PROGRESS_HEIGHT) * 0.5f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, cbe::style::ED_COLOR_PRIMARY);
|
||||||
|
ImGui::ProgressBar(progress, ImVec2(progressSize, PROGRESS_HEIGHT), "");
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
ImGui::SetCursorPosY(cursorPosY);
|
||||||
|
if (progTracker->bContinuous)
|
||||||
|
{
|
||||||
|
ImGui::SetItemTooltip("Progress %.0f%%", progress);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui::SetItemTooltip("%u/%u %s", currProgress, progTracker->maxSteps, progTracker->stepDesc.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No spacing since button has padding and it is transparent */
|
||||||
|
ImGui::SameLine(0.0f, 0.0f);
|
||||||
|
/* Must set after SameLine since same line restores the Cursor Y from prev line. */
|
||||||
|
ImGui::SetCursorPosY(cursorPosY);
|
||||||
|
|
||||||
|
ImGui::BeginDisabled(progTracker->bCancelReq);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, LinearColorConst::RED);
|
||||||
|
if (cbe::ImGuiHelpers::buttonNoBg(cbe::mat_icon::CANCEL))
|
||||||
|
{
|
||||||
|
progTracker->bCancelReq = true;
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(1);
|
||||||
|
ImGui::SetItemTooltip("Try cancel this task!");
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -38,6 +38,19 @@ struct NotificationInfo
|
|||||||
/* Optional */
|
/* Optional */
|
||||||
NotificationCb callback;
|
NotificationCb callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using ProgressTrackerHnd = uint64;
|
||||||
|
struct ProgressTrackerInfo
|
||||||
|
{
|
||||||
|
String name;
|
||||||
|
|
||||||
|
/* If zero assumes continuous, uses 16bit/65536 values to represent continuous data. */
|
||||||
|
uint32 stepsCount = 0;
|
||||||
|
|
||||||
|
/* If true the task will be displayed as sticky notification. */
|
||||||
|
bool bBackground = false;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace cbe
|
} // namespace cbe
|
||||||
|
|
||||||
class WgEditorImGuiLayer : public IImGuiLayer
|
class WgEditorImGuiLayer : public IImGuiLayer
|
||||||
@@ -60,6 +73,34 @@ public:
|
|||||||
void removeMenuExtender(const TChar *menuName, DelegateHandle handle);
|
void removeMenuExtender(const TChar *menuName, DelegateHandle handle);
|
||||||
|
|
||||||
void addNotification(const cbe::NotificationInfo &info);
|
void addNotification(const cbe::NotificationInfo &info);
|
||||||
|
/**
|
||||||
|
* @brief Notifications that sticks until explicitly removed by Sticky ID.
|
||||||
|
* @param info Notification info.
|
||||||
|
*/
|
||||||
|
uint64 addStickyNotification(const cbe::NotificationInfo &info);
|
||||||
|
void removeStickyNotification(uint64 stickyId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a new progress tracker and returns tracker handle that can used to update progress.
|
||||||
|
* @param info Progress tracker information.
|
||||||
|
* @return Progress tracker handle.
|
||||||
|
*/
|
||||||
|
cbe::ProgressTrackerHnd addProgressTracker(const cbe::ProgressTrackerInfo &info);
|
||||||
|
/**
|
||||||
|
* @brief Progresses the tracker by provided steps count and updates the next step with new description.
|
||||||
|
* @param hnd Progress tracker handle.
|
||||||
|
* @param stepsCount Number of steps to progress.
|
||||||
|
* @param stepDesc If empty previous description is used.
|
||||||
|
*/
|
||||||
|
void progressProgressTracker(cbe::ProgressTrackerHnd hnd, uint32 stepsCount, StringView stepDesc);
|
||||||
|
/**
|
||||||
|
* @brief Sets the continuous progress.
|
||||||
|
* @param hnd Progress tracker handle.
|
||||||
|
* @param progress Progress value between 0.0 - 1.0.
|
||||||
|
*/
|
||||||
|
void setProgressTracerProgress(cbe::ProgressTrackerHnd hnd, float progress);
|
||||||
|
bool isProgressTrackerCancelling(cbe::ProgressTrackerHnd hnd) const;
|
||||||
|
void cancelProgressTracker(cbe::ProgressTrackerHnd hnd);
|
||||||
|
|
||||||
void addAssetEditor(const cbe::ICoreAssetEditorRef &assetEditor);
|
void addAssetEditor(const cbe::ICoreAssetEditorRef &assetEditor);
|
||||||
/* Returns true if removed, false means remove cannot be done now.
|
/* Returns true if removed, false means remove cannot be done now.
|
||||||
@@ -68,18 +109,6 @@ public:
|
|||||||
void tickAssetEditors(float deltaTime);
|
void tickAssetEditors(float deltaTime);
|
||||||
cbe::ETryExit tryExitEditor();
|
cbe::ETryExit tryExitEditor();
|
||||||
|
|
||||||
private:
|
|
||||||
void drawEditorWindows();
|
|
||||||
|
|
||||||
void addMenubar();
|
|
||||||
void aboutWindow();
|
|
||||||
void jobSystemJobsStats();
|
|
||||||
void handleShortcuts();
|
|
||||||
void drawNotifications();
|
|
||||||
|
|
||||||
void undo();
|
|
||||||
void redo();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string, ImGuiDrawInterfaceCallback> menuExtenders;
|
std::unordered_map<std::string, ImGuiDrawInterfaceCallback> menuExtenders;
|
||||||
|
|
||||||
@@ -99,6 +128,10 @@ private:
|
|||||||
WindowingManager *wndManager;
|
WindowingManager *wndManager;
|
||||||
Color wndFrameColor;
|
Color wndFrameColor;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Notifications
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification gets drawn only in scene window.
|
* Notification gets drawn only in scene window.
|
||||||
* This is because that is the only window that can never be undocked and moved.
|
* This is because that is the only window that can never be undocked and moved.
|
||||||
@@ -109,21 +142,87 @@ private:
|
|||||||
std::string name;
|
std::string name;
|
||||||
TickRep endAt;
|
TickRep endAt;
|
||||||
cbe::NotificationCb cb;
|
cbe::NotificationCb cb;
|
||||||
|
/* Necessary to offset self to avoid overlapping with other notifications below. */
|
||||||
|
float wndHeight = 0;
|
||||||
|
|
||||||
|
bool bSticky:1 = false;
|
||||||
|
|
||||||
NotifyDrawInfo *next = nullptr;
|
NotifyDrawInfo *next = nullptr;
|
||||||
NotifyDrawInfo *prev = nullptr;
|
NotifyDrawInfo *previous = nullptr;
|
||||||
};
|
};
|
||||||
constexpr static const uint32 NOTIFICATION_LINES_COUNT = 2;
|
constexpr static const uint32 NOTIFICATION_LINES_COUNT = 2;
|
||||||
constexpr static const uint32 NOTIFICATION_LINE_CHAR_COUNT = 20;
|
constexpr static const uint32 NOTIFICATION_LINE_CHAR_COUNT = 20;
|
||||||
|
constexpr static const uint32 NOTIFICATION_POOL_COUNT = 16;
|
||||||
/* Keeping as pool allocator to allow resizing if window size changes. */
|
/* Keeping as pool allocator to allow resizing if window size changes. */
|
||||||
PoolAllocator<NotifyDrawInfo, 16> notifyAlloc;
|
PoolAllocator<NotifyDrawInfo, NOTIFICATION_POOL_COUNT> notifyAlloc;
|
||||||
uint32 maxNotifies = 16;
|
uint32 maxNotifies = NOTIFICATION_POOL_COUNT;
|
||||||
uint32 currNotifyCount = 0;
|
uint32 currNotifyCount = 0;
|
||||||
NotifyDrawInfo *notifyHead = nullptr;
|
NotifyDrawInfo *notifyHead = nullptr;
|
||||||
|
|
||||||
bool bShowDemo = false;
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Progress trackers
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct ProgressDrawInfo
|
||||||
|
{
|
||||||
|
std::atomic_uint32_t currProgress;
|
||||||
|
uint32 maxSteps;
|
||||||
|
std::string stepDesc;
|
||||||
|
std::string jobName;
|
||||||
|
|
||||||
|
/* Progress that gets drawn as part of notification widgets. ID to sticky notification. */
|
||||||
|
uint64 stickyId = 0;
|
||||||
|
|
||||||
|
/* Becomes true when cancel is requested */
|
||||||
|
bool bCancelReq = false;
|
||||||
|
/* If the progress is continuous */
|
||||||
|
bool bContinuous:1 = false;
|
||||||
|
|
||||||
|
ProgressDrawInfo *next = nullptr;
|
||||||
|
ProgressDrawInfo *previous = nullptr;
|
||||||
|
};
|
||||||
|
constexpr static const uint32 PROG_TRACKER_POOL_COUNT = 16;
|
||||||
|
PoolAllocator<ProgressDrawInfo, PROG_TRACKER_POOL_COUNT> progTrackerAlloc;
|
||||||
|
ProgressDrawInfo *progTrackerHead = nullptr;
|
||||||
|
|
||||||
bool bShowStyleEditor = false;
|
bool bShowStyleEditor = false;
|
||||||
|
bool bShowImGuiMetricWnd = false;
|
||||||
|
bool bShowDebugLog = false;
|
||||||
|
bool bShowImGuiIdStack = false;
|
||||||
|
|
||||||
bool bShowAbout = false;
|
bool bShowAbout = false;
|
||||||
bool bShowJobQueueStats = false;
|
bool bShowJobQueueStats = false;
|
||||||
|
|
||||||
|
struct DemoWindowData
|
||||||
|
{
|
||||||
|
bool bShowImGuiDemo = false;
|
||||||
|
bool bShowEngineWgDemo = false;
|
||||||
|
|
||||||
|
cbe::ProgressTrackerHnd progHnd = 0;
|
||||||
|
cbe::ProgressTrackerHnd progHndBg = 0;
|
||||||
|
/* Continuous progress handles */
|
||||||
|
cbe::ProgressTrackerHnd cProgHnd = 0;
|
||||||
|
float cProg = 0.0f;
|
||||||
|
|
||||||
|
cbe::ProgressTrackerHnd cProgHndBg = 0;
|
||||||
|
float cProgBg = 0.0f;
|
||||||
|
} demoData;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void drawEngineWidgetDemo();
|
||||||
|
void drawEditorWindows();
|
||||||
|
|
||||||
|
void addMenubar();
|
||||||
|
void aboutWindow();
|
||||||
|
void jobSystemJobsStats();
|
||||||
|
void handleShortcuts();
|
||||||
|
|
||||||
|
void drawNotifications();
|
||||||
|
void remNotification(NotifyDrawInfo *rmNotification);
|
||||||
|
|
||||||
|
void drawProgressTrackers();
|
||||||
|
void drawBackgroundProgressTracker(cbe::ProgressTrackerHnd hnd) const;
|
||||||
|
|
||||||
|
void undo();
|
||||||
|
void redo();
|
||||||
};
|
};
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -114,6 +114,9 @@ void WgViewportImGuiLayer::drawImGui()
|
|||||||
ImGui::PopStyleVar(2);
|
ImGui::PopStyleVar(2);
|
||||||
if (window)
|
if (window)
|
||||||
{
|
{
|
||||||
|
/* Pump any current frame messages. This ensure any queued messages gets processed before core logic. */
|
||||||
|
ownerRoot->pumpMessages();
|
||||||
|
|
||||||
cbe::TransactionsLedger &ledger = getTransactionsLedger();
|
cbe::TransactionsLedger &ledger = getTransactionsLedger();
|
||||||
|
|
||||||
ImGuiWindow *currWindow = ImGui::GetCurrentWindow();
|
ImGuiWindow *currWindow = ImGui::GetCurrentWindow();
|
||||||
@@ -234,7 +237,7 @@ void WgViewportImGuiLayer::drawImGui()
|
|||||||
/* Clear selection if Esc is pressed */
|
/* Clear selection if Esc is pressed */
|
||||||
if (ImGui::IsKeyReleased(ImGuiKey::ImGuiKey_Escape))
|
if (ImGui::IsKeyReleased(ImGuiKey::ImGuiKey_Escape))
|
||||||
{
|
{
|
||||||
LOG("LogTemp", "Cleared selections!");
|
CBE_LOG("LogTemp", "Cleared selections!");
|
||||||
worldViewport.clearSelections();
|
worldViewport.clearSelections();
|
||||||
ownerRoot->pushMessage(cbe::CoreEdMessage_SelectionChanged);
|
ownerRoot->pushMessage(cbe::CoreEdMessage_SelectionChanged);
|
||||||
}
|
}
|
||||||
@@ -396,7 +399,7 @@ void WgViewportImGuiLayer::drawViewportToolBar()
|
|||||||
bPlaying = true;
|
bPlaying = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::BeginDisabled(!ownerRoot->isAssetDirt() || bPlaying);
|
ImGui::BeginDisabled(!ownerRoot->isAssetDirty() || bPlaying);
|
||||||
if (bDrawingMainEd)
|
if (bDrawingMainEd)
|
||||||
{
|
{
|
||||||
if (ImGui::Button(cbe::mat_icon::SAVE))
|
if (ImGui::Button(cbe::mat_icon::SAVE))
|
||||||
@@ -1413,6 +1416,12 @@ bool WorldViewportTools::drawTfGizmo(bool bCanHandleInputs, Short2 frameSize, Wo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If sub selection is active but there is nothing to transform do not draw the transform gizmo */
|
||||||
|
if (!selComps.empty() && (selTfComps.size() + selTfProxies.size()) == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle all the inputs first */
|
/* Handle all the inputs first */
|
||||||
TfGizmoProxy *tfSelProxy = nullptr;
|
TfGizmoProxy *tfSelProxy = nullptr;
|
||||||
if (hoverSelProxy && (hoverSelProxy->isType<TfGizmoScaleOrMove>() || hoverSelProxy->isType<TfGizmoRotate>()))
|
if (hoverSelProxy && (hoverSelProxy->isType<TfGizmoScaleOrMove>() || hoverSelProxy->isType<TfGizmoRotate>()))
|
||||||
@@ -1957,6 +1966,15 @@ bool WorldViewportTools::drawTfGizmo(bool bCanHandleInputs, Short2 frameSize, Wo
|
|||||||
|
|
||||||
void WorldViewportTools::TfGizmoScaleOrMove::setupReference()
|
void WorldViewportTools::TfGizmoScaleOrMove::setupReference()
|
||||||
{
|
{
|
||||||
|
if (bScale)
|
||||||
|
{
|
||||||
|
viewportTools->gizmoRefValue = viewportTools->gizmoTf.getScale();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
viewportTools->gizmoRefValue = viewportTools->gizmoTf.getTranslation();
|
||||||
|
}
|
||||||
|
|
||||||
if (!viewportTools->selTfComps.empty() || !viewportTools->selTfProxies.empty())
|
if (!viewportTools->selTfComps.empty() || !viewportTools->selTfProxies.empty())
|
||||||
{
|
{
|
||||||
if (bScale)
|
if (bScale)
|
||||||
@@ -1996,8 +2014,6 @@ void WorldViewportTools::TfGizmoScaleOrMove::setupReference()
|
|||||||
{
|
{
|
||||||
if (bScale)
|
if (bScale)
|
||||||
{
|
{
|
||||||
viewportTools->gizmoRefValue = viewportTools->gizmoTf.getScale();
|
|
||||||
|
|
||||||
for (SizeT selIdx = 0; selIdx < viewportTools->selectedActors.size(); ++selIdx)
|
for (SizeT selIdx = 0; selIdx < viewportTools->selectedActors.size(); ++selIdx)
|
||||||
{
|
{
|
||||||
cbe::Actor *selActor = viewportTools->selectedActors[selIdx];
|
cbe::Actor *selActor = viewportTools->selectedActors[selIdx];
|
||||||
@@ -2007,8 +2023,6 @@ void WorldViewportTools::TfGizmoScaleOrMove::setupReference()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
viewportTools->gizmoRefValue = viewportTools->gizmoTf.getTranslation();
|
|
||||||
|
|
||||||
for (SizeT selIdx = 0; selIdx < viewportTools->selectedActors.size(); ++selIdx)
|
for (SizeT selIdx = 0; selIdx < viewportTools->selectedActors.size(); ++selIdx)
|
||||||
{
|
{
|
||||||
cbe::Actor *selActor = viewportTools->selectedActors[selIdx];
|
cbe::Actor *selActor = viewportTools->selectedActors[selIdx];
|
||||||
@@ -2312,6 +2326,73 @@ void WorldViewportTools::TfGizmoScaleOrMove::transformSelection(
|
|||||||
worldOffset = Math::sign(offsetVec | gizmoAxis) * gizmoAxis * offsetVec.size();
|
worldOffset = Math::sign(offsetVec | gizmoAxis) * gizmoAxis * offsetVec.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* When not scaling update the gizmo transform */
|
||||||
|
if (!bScale)
|
||||||
|
{
|
||||||
|
viewportTools->gizmoTf.setTranslation(viewportTools->gizmoTf.getTranslation() + worldOffset);
|
||||||
|
currMouse3dPt = viewportTools->gizmoTf.getTranslation();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!viewportTools->selTfComps.empty() || !viewportTools->selTfProxies.empty())
|
||||||
|
{
|
||||||
|
if (bScale)
|
||||||
|
{
|
||||||
|
for (cbe::TransformComponent *tfComp : viewportTools->selTfComps)
|
||||||
|
{
|
||||||
|
const Vector3 worldScale = tfComp->getWorldScale();
|
||||||
|
|
||||||
|
Vector3 scaleDelta = worldOffset;
|
||||||
|
const Quat relativeRot = tfComp->getRelativeTransform().getRotation();
|
||||||
|
/* For scaling the relative local rotation must be applied only when we are scaling in world space.
|
||||||
|
* For local space we must do the scaling in actor local space so undo the applied rotation. */
|
||||||
|
if (viewportTools->tfGizmoSpace == GizmoSpaceLocal)
|
||||||
|
{
|
||||||
|
scaleDelta = relativeRot.inverse().rotateVector(worldOffset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scaleDelta = relativeRot.rotateVector(worldOffset);
|
||||||
|
}
|
||||||
|
tfComp->setWorldScale(worldScale + (Math::abs(scaleDelta) * scaleSign));
|
||||||
|
}
|
||||||
|
for (const ReferenceCountPtr<TfGizmoControllableProxy> &tfProxy : viewportTools->selTfProxies)
|
||||||
|
{
|
||||||
|
Transform3D srcTf = tfProxy->getTransform();
|
||||||
|
|
||||||
|
Vector3 scaleDelta = worldOffset;
|
||||||
|
const Quat relativeRot = tfProxy->getRelativeTransform().getRotation();
|
||||||
|
/* For scaling the relative local rotation must be applied only when we are scaling in world space.
|
||||||
|
* For local space we must do the scaling in actor local space so undo the applied rotation. */
|
||||||
|
if (viewportTools->tfGizmoSpace == GizmoSpaceLocal)
|
||||||
|
{
|
||||||
|
scaleDelta = relativeRot.inverse().rotateVector(worldOffset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scaleDelta = relativeRot.rotateVector(worldOffset);
|
||||||
|
}
|
||||||
|
srcTf.setScale(srcTf.getScale() + (Math::abs(scaleDelta) * scaleSign));
|
||||||
|
|
||||||
|
tfProxy->setTransform(srcTf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (cbe::TransformComponent *tfComp : viewportTools->selTfComps)
|
||||||
|
{
|
||||||
|
const Vector3 worldLoc = tfComp->getWorldLocation();
|
||||||
|
tfComp->setWorldLocation(worldLoc + worldOffset);
|
||||||
|
}
|
||||||
|
for (const ReferenceCountPtr<TfGizmoControllableProxy> &tfProxy : viewportTools->selTfProxies)
|
||||||
|
{
|
||||||
|
Transform3D srcTf = tfProxy->getTransform();
|
||||||
|
srcTf.setTranslation(srcTf.getTranslation() + worldOffset);
|
||||||
|
tfProxy->setTransform(srcTf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if (bScale)
|
if (bScale)
|
||||||
{
|
{
|
||||||
for (cbe::Actor *actor : viewportTools->selectedActors)
|
for (cbe::Actor *actor : viewportTools->selectedActors)
|
||||||
@@ -2336,9 +2417,6 @@ void WorldViewportTools::TfGizmoScaleOrMove::transformSelection(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
viewportTools->gizmoTf.setTranslation(viewportTools->gizmoTf.getTranslation() + worldOffset);
|
|
||||||
currMouse3dPt = viewportTools->gizmoTf.getTranslation();
|
|
||||||
|
|
||||||
for (cbe::Actor *actor : viewportTools->selectedActors)
|
for (cbe::Actor *actor : viewportTools->selectedActors)
|
||||||
{
|
{
|
||||||
debugAssert(actor->getRootComponent() != nullptr);
|
debugAssert(actor->getRootComponent() != nullptr);
|
||||||
@@ -2346,6 +2424,7 @@ void WorldViewportTools::TfGizmoScaleOrMove::transformSelection(
|
|||||||
actor->getRootComponent()->setWorldLocation(worldLoc + worldOffset);
|
actor->getRootComponent()->setWorldLocation(worldLoc + worldOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldViewportTools::TfGizmoRotate::setupReference()
|
void WorldViewportTools::TfGizmoRotate::setupReference()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -37,7 +37,7 @@ enum class EViewportFlags : uint8
|
|||||||
Gizmos = 0x2, ///< Allow drawing and controlling gizmo
|
Gizmos = 0x2, ///< Allow drawing and controlling gizmo
|
||||||
DragDrop = 0x4, ///< Allow dragging and dropping into viewport
|
DragDrop = 0x4, ///< Allow dragging and dropping into viewport
|
||||||
NoTools = 0x8, ///< If no tools must be drawn
|
NoTools = 0x8, ///< If no tools must be drawn
|
||||||
NoWorldEdit = 0x10, ///< World edit is not allowed
|
NoWorldEdit = 0x10, ///< World edit using viewport context menu is not allowed
|
||||||
EnableAll = BufferVisualization | Gizmos | DragDrop,
|
EnableAll = BufferVisualization | Gizmos | DragDrop,
|
||||||
};
|
};
|
||||||
MAKE_ENUM_BIT_OPS(EViewportFlags)
|
MAKE_ENUM_BIT_OPS(EViewportFlags)
|
||||||
@@ -354,6 +354,11 @@ public:
|
|||||||
|
|
||||||
const Camera &getCamera() const { return defaultCamera; }
|
const Camera &getCamera() const { return defaultCamera; }
|
||||||
WorldViewport &getWorldViewport() { return worldViewport; }
|
WorldViewport &getWorldViewport() { return worldViewport; }
|
||||||
|
void resetViewport()
|
||||||
|
{
|
||||||
|
/* Right now doing selection changed is equivalent to reset viewport drawing */
|
||||||
|
onSelectionChanged();
|
||||||
|
}
|
||||||
void onSelectionChanged();
|
void onSelectionChanged();
|
||||||
void
|
void
|
||||||
onSubselectionChanged(ArrayView<cbe::WeakObjectPtr> selectedObjs, const std::unordered_set<cbe::WorldSelectionProxyRef> &selectedProxies);
|
onSubselectionChanged(ArrayView<cbe::WeakObjectPtr> selectedObjs, const std::unordered_set<cbe::WorldSelectionProxyRef> &selectedProxies);
|
||||||
|
|||||||
@@ -4,14 +4,18 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <EditorEngine.hpp>
|
#include <EditorEngine.hpp>
|
||||||
|
#include <Types/Platform/Threading/CoPaT/CoroutineUtilities.h>
|
||||||
|
#include <Modules/ModuleManager.h>
|
||||||
|
#include <CBEAssetManager.hpp>
|
||||||
#include <IEditorCore.h>
|
#include <IEditorCore.h>
|
||||||
#include <ICBEEditor.h>
|
#include <ICBEEditor.h>
|
||||||
|
#include <CranberryEngineApp.h>
|
||||||
#include <EditorHelpers.h>
|
#include <EditorHelpers.h>
|
||||||
#include <GalWgWindowRenderer.h>
|
#include <GalWgWindowRenderer.h>
|
||||||
#include <Types/Platform/LFS/PlatformLFS.h>
|
#include <Types/Platform/LFS/PlatformLFS.h>
|
||||||
@@ -19,7 +23,6 @@
|
|||||||
#include <Types/Platform/LFS/PathFunctions.h>
|
#include <Types/Platform/LFS/PathFunctions.h>
|
||||||
#include <Types/Platform/LFS/Paths.h>
|
#include <Types/Platform/LFS/Paths.h>
|
||||||
#include <IApplicationModule.h>
|
#include <IApplicationModule.h>
|
||||||
#include <CbeApplication.h>
|
|
||||||
#include <Widgets/ImGui/WgImGui.h>
|
#include <Widgets/ImGui/WgImGui.h>
|
||||||
#include <Widgets/WgWindow.h>
|
#include <Widgets/WgWindow.h>
|
||||||
#include <Widgets/WgEditorImGuiLayer.h>
|
#include <Widgets/WgEditorImGuiLayer.h>
|
||||||
@@ -31,6 +34,7 @@
|
|||||||
#include <Widgets/NullWidget.h>
|
#include <Widgets/NullWidget.h>
|
||||||
#include <Classes/WorldsManager.hpp>
|
#include <Classes/WorldsManager.hpp>
|
||||||
#include <WorldViewport.h>
|
#include <WorldViewport.h>
|
||||||
|
#include <CoreObjectGC.h>
|
||||||
|
|
||||||
void tempTest();
|
void tempTest();
|
||||||
void tempTickTest(const ApplicationTimeData &timeData, const Camera &camera);
|
void tempTickTest(const ApplicationTimeData &timeData, const Camera &camera);
|
||||||
@@ -58,7 +62,7 @@ EngineMainEd::EngineMainEd()
|
|||||||
.edRoot = this,
|
.edRoot = this,
|
||||||
.viewportFlags = EViewportFlags::EnableAll,
|
.viewportFlags = EViewportFlags::EnableAll,
|
||||||
});
|
});
|
||||||
detailsLayer = std::make_shared<WgDetailsImGuiLayer>(WgDetailsCreateInfo{
|
detailsLayer = std::make_shared<WgDetailsImGuiLayer>(cbe::WgDetailsCreateInfo{
|
||||||
.edRoot = this,
|
.edRoot = this,
|
||||||
.detailWndName = cbe::wg_consts::WND_DETAILS_ID,
|
.detailWndName = cbe::wg_consts::WND_DETAILS_ID,
|
||||||
});
|
});
|
||||||
@@ -92,6 +96,9 @@ EngineMainEd::EngineMainEd()
|
|||||||
{
|
{
|
||||||
if (bIsMain)
|
if (bIsMain)
|
||||||
{
|
{
|
||||||
|
bIsPlayInEd = false;
|
||||||
|
bEditingAssetDirty = false;
|
||||||
|
|
||||||
viewportLayer->resetWorld(nullptr);
|
viewportLayer->resetWorld(nullptr);
|
||||||
worldLayer->setWorldViewport(nullptr, nullptr);
|
worldLayer->setWorldViewport(nullptr, nullptr);
|
||||||
detailsLayer->setWorldViewport(nullptr, nullptr);
|
detailsLayer->setWorldViewport(nullptr, nullptr);
|
||||||
@@ -106,7 +113,7 @@ void EngineMainEd::saveAsset()
|
|||||||
cbe::World *mainWorld = gCBEEngine->worldManager()->getMainWorld();
|
cbe::World *mainWorld = gCBEEngine->worldManager()->getMainWorld();
|
||||||
cbe::World *editWorld = gCBEEngine->worldManager()->getEditorWorld();
|
cbe::World *editWorld = gCBEEngine->worldManager()->getEditorWorld();
|
||||||
|
|
||||||
if (isAssetDirt())
|
if (isAssetDirty())
|
||||||
{
|
{
|
||||||
/* Copying and directly saving works since we use ActorPrefab for even actors spawned from classes. */
|
/* Copying and directly saving works since we use ActorPrefab for even actors spawned from classes. */
|
||||||
mainWorld->copyFrom(editWorld);
|
mainWorld->copyFrom(editWorld);
|
||||||
@@ -131,27 +138,49 @@ MessageResponse EngineMainEd::sendMessage(const MessageData &msgDat, ECoreEdMess
|
|||||||
}
|
}
|
||||||
case cbe::CoreEdMessage_RefreshEd:
|
case cbe::CoreEdMessage_RefreshEd:
|
||||||
{
|
{
|
||||||
if (msgDat.issuerData != std::bit_cast<uint64>(detailsLayer.get()) && std::holds_alternative<MessageData::ObjectsList>(msgDat.msg))
|
const bool bDetailsWgRefresh = msgDat.issuerData != std::bit_cast<uint64>(detailsLayer.get());
|
||||||
|
const bool bViewportWgRefresh = msgDat.issuerData != std::bit_cast<uint64>(viewportLayer.get());
|
||||||
|
if (std::holds_alternative<MessageData::ObjectsList>(msgDat.msg))
|
||||||
|
{
|
||||||
|
if (bDetailsWgRefresh)
|
||||||
{
|
{
|
||||||
detailsLayer->refreshDetailsDrawers(std::get<MessageData::ObjectsList>(msgDat.msg));
|
detailsLayer->refreshDetailsDrawers(std::get<MessageData::ObjectsList>(msgDat.msg));
|
||||||
}
|
}
|
||||||
response.state = MessageResponse::Remove;
|
response.state = MessageResponse::Remove;
|
||||||
|
}
|
||||||
|
else if (std::holds_alternative<EEdRefreshType>(msgDat.msg))
|
||||||
|
{
|
||||||
|
switch (std::get<EEdRefreshType>(msgDat.msg))
|
||||||
|
{
|
||||||
|
case EEdRefreshType::VisualOnly:
|
||||||
|
if (bDetailsWgRefresh)
|
||||||
|
{
|
||||||
|
detailsLayer->refreshDetailsDrawers({});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EEdRefreshType::FullReset:
|
||||||
|
if (bDetailsWgRefresh)
|
||||||
|
{
|
||||||
|
detailsLayer->resetDetails();
|
||||||
|
}
|
||||||
|
if (bViewportWgRefresh)
|
||||||
|
{
|
||||||
|
viewportLayer->resetViewport();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
response.state = MessageResponse::Remove;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case cbe::CoreEdMessage_SelectionChanged:
|
case cbe::CoreEdMessage_SelectionChanged:
|
||||||
{
|
{
|
||||||
const std::vector<WeakObjPtr<Object>> &newSelObjs = viewportLayer->getWorldViewport().getSelectedObjects();
|
|
||||||
const std::unordered_set<WorldSelectionProxyRef> &newSelProxies = viewportLayer->getWorldViewport().getSelectedProxies();
|
|
||||||
|
|
||||||
worldLayer->onSelectionChanged();
|
worldLayer->onSelectionChanged();
|
||||||
detailsLayer->onSelectionChanged();
|
detailsLayer->onSelectionChanged();
|
||||||
viewportLayer->onSelectionChanged();
|
viewportLayer->onSelectionChanged();
|
||||||
|
|
||||||
mainWorldSelObjs = newSelObjs;
|
|
||||||
mainWorldSelProxies.clear();
|
|
||||||
mainWorldSelProxies.reserve(newSelProxies.size());
|
|
||||||
mainWorldSelProxies.insert(mainWorldSelProxies.begin(), newSelProxies.cbegin(), newSelProxies.cend());
|
|
||||||
|
|
||||||
response.state = MessageResponse::Remove;
|
response.state = MessageResponse::Remove;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -177,9 +206,6 @@ void EngineMainEd::drawEditor() {}
|
|||||||
void EngineMainEd::collectReferencesInternal(MAYBE_UNUSED std::vector<cbe::Object *> &outObjects) const {}
|
void EngineMainEd::collectReferencesInternal(MAYBE_UNUSED std::vector<cbe::Object *> &outObjects) const {}
|
||||||
void EngineMainEd::onCloseEditor()
|
void EngineMainEd::onCloseEditor()
|
||||||
{
|
{
|
||||||
mainWorldSelProxies.clear();
|
|
||||||
mainWorldSelObjs.clear();
|
|
||||||
|
|
||||||
gCBEEngine->worldManager()->onWorldInitEvent().unbind(worldInitHandle);
|
gCBEEngine->worldManager()->onWorldInitEvent().unbind(worldInitHandle);
|
||||||
gCBEEngine->worldManager()->onWorldUnloadEvent().unbind(worldUnloadHandle);
|
gCBEEngine->worldManager()->onWorldUnloadEvent().unbind(worldUnloadHandle);
|
||||||
|
|
||||||
@@ -211,6 +237,11 @@ void EngineMainEd::onEdTick(MAYBE_UNUSED float deltaTime)
|
|||||||
bEditingAssetDirty = cbe::isPackageDirty(world);
|
bEditingAssetDirty = cbe::isPackageDirty(world);
|
||||||
bIsPlayInEd = EditorHelpers::isPlayInEd(*gCBEditorEngine->worldManager());
|
bIsPlayInEd = EditorHelpers::isPlayInEd(*gCBEditorEngine->worldManager());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bEditingAssetDirty = false;
|
||||||
|
bIsPlayInEd = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
@@ -235,7 +266,10 @@ void EditorEngine::engineStart()
|
|||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("EditorStartup"));
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("EditorStartup"));
|
||||||
|
|
||||||
const String engContentDir = Paths::engineContentDirectory();
|
const String engContentDir = Paths::engineContentDirectory();
|
||||||
CbeApplication *application = IApplicationModule::get()->getApplication();
|
CranberryEngineApp *application = static_cast<CranberryEngineApp *>(IApplicationModule::get()->getApplication());
|
||||||
|
|
||||||
|
assetManager = application->getAssetManager();
|
||||||
|
|
||||||
/* Initialize font data */
|
/* Initialize font data */
|
||||||
WgImGui::WgImGuiFont fonts[2];
|
WgImGui::WgImGuiFont fonts[2];
|
||||||
std::vector<uint8> fontData[2];
|
std::vector<uint8> fontData[2];
|
||||||
@@ -300,6 +334,10 @@ void EditorEngine::engineStart()
|
|||||||
}
|
}
|
||||||
CoreObjectDelegates::broadcastContentDirectoryAdded(engContentDir);
|
CoreObjectDelegates::broadcastContentDirectoryAdded(engContentDir);
|
||||||
|
|
||||||
|
modLoadCbHndl = ModuleManager::get()->onModuleLoad.bindObject(this, &EditorEngine::onModuleLoadCb);
|
||||||
|
modUnloadCbHndl = ModuleManager::get()->onModuleUnload.bindObject(this, &EditorEngine::onModuleUnloadCb);
|
||||||
|
refreshTypeInfo();
|
||||||
|
|
||||||
tempTest();
|
tempTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,6 +356,9 @@ void EditorEngine::engineExit()
|
|||||||
{
|
{
|
||||||
tempExitTest();
|
tempExitTest();
|
||||||
|
|
||||||
|
ModuleManager::get()->onModuleLoad.unbind(modLoadCbHndl);
|
||||||
|
ModuleManager::get()->onModuleUnload.unbind(modUnloadCbHndl);
|
||||||
|
|
||||||
mainEd->closeEditor();
|
mainEd->closeEditor();
|
||||||
/* GC is the last reference holder */
|
/* GC is the last reference holder */
|
||||||
debugAssert(mainEd->refCount() <= 2);
|
debugAssert(mainEd->refCount() <= 2);
|
||||||
@@ -328,23 +369,91 @@ void EditorEngine::engineExit()
|
|||||||
FileHelper::deleteDir(transactionDir);
|
FileHelper::deleteDir(transactionDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorEngine::destroy() {}
|
void EditorEngine::onDestructed() {}
|
||||||
|
|
||||||
|
copat::JobSystemFuncAwaiter asyncOpenScene(copat::JobSystem &jobSystem, StringView inObjPath)
|
||||||
|
{
|
||||||
|
debugAssert(copat::JobSystem::get()->isInThread(copat::EJobThreadType::MainThread));
|
||||||
|
|
||||||
|
AssetManager *assetManager = gCBEditorEngine->getAssetManager();
|
||||||
|
CoreObjectGC &gc = ICoreObjectsModule::get()->getGC();
|
||||||
|
|
||||||
|
AssetManager::PackageAllocator::AllocHandle pkgAllocHnd = {};
|
||||||
|
{
|
||||||
|
StringView outerObjPath;
|
||||||
|
StringView objName;
|
||||||
|
const StringView packagePath = ObjectPathHelper::getPathComponents(outerObjPath, objName, inObjPath);
|
||||||
|
const AssetManager::TreeNodeIdx pkgFolderFolderTreeIdx = assetManager->pathToFolderIdxInFolderTree(packagePath);
|
||||||
|
const AssetManager::TreeNodeIdx assetPkgFolderTreeIdx = assetManager->findAssetUnder(objName, pkgFolderFolderTreeIdx, false);
|
||||||
|
debugAssert(assetManager->isPackageNode(assetPkgFolderTreeIdx));
|
||||||
|
pkgAllocHnd = std::get<AssetManager::FolderTree_Package>(assetManager->getFolderInfo(assetPkgFolderTreeIdx)->nodeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<AssetManager::PackageAllocator::AllocHandle> allPkgsToLoad;
|
||||||
|
{
|
||||||
|
allPkgsToLoad.emplace_back(pkgAllocHnd);
|
||||||
|
SizeT startIdx = 0;
|
||||||
|
SizeT endIdx = allPkgsToLoad.size();
|
||||||
|
while (startIdx < endIdx)
|
||||||
|
{
|
||||||
|
for (SizeT i = startIdx; i < endIdx; ++i)
|
||||||
|
{
|
||||||
|
assetManager->getPackageDepsFromRefTree(allPkgsToLoad, assetManager->getAssetPackage(allPkgsToLoad[i])->folderTreeIdx);
|
||||||
|
}
|
||||||
|
startIdx = endIdx;
|
||||||
|
endIdx = allPkgsToLoad.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now start the progress, +1 for Preparing world, +1 after opening the world and keep progress showing. */
|
||||||
|
const cbe::ProgressTrackerHnd progTracker = gCBEditorEngine->addProgressTracker(cbe::ProgressTrackerInfo{
|
||||||
|
.name = TCHAR("Loading scene objects"),
|
||||||
|
.stepsCount = static_cast<uint32>(allPkgsToLoad.size() + 2),
|
||||||
|
});
|
||||||
|
/* To not clear the loaded objects while Async loading */
|
||||||
|
gc.pauseGc();
|
||||||
|
|
||||||
|
co_await copat::SwitchJobSystemThreadAwaiter{ jobSystem, copat::EJobThreadType::WorkerThreads };
|
||||||
|
|
||||||
|
/* Last load will be the world */
|
||||||
|
Object *lastLoadedObj = nullptr;
|
||||||
|
for (auto itr = allPkgsToLoad.rbegin(); itr != allPkgsToLoad.rend(); ++itr)
|
||||||
|
{
|
||||||
|
const AssetManager::AssetPackage *assetPkg = assetManager->getAssetPackage(*itr);
|
||||||
|
const StringView objName = ObjectPathHelper::getObjectName(assetPkg->rootObjectPath);
|
||||||
|
gCBEditorEngine->progressProgressTracker(progTracker, 1, STR_FORMAT("Loading {}", objName));
|
||||||
|
|
||||||
|
lastLoadedObj = cbe::getOrLoad(assetPkg->rootObjectPath, assetPkg->rootObjectClass);
|
||||||
|
co_await copat::YieldAwaiter{};
|
||||||
|
}
|
||||||
|
|
||||||
|
gCBEditorEngine->progressProgressTracker(progTracker, 1, STR_FORMAT("Preparing World {}", lastLoadedObj->getObjectData().name));
|
||||||
|
|
||||||
|
co_await copat::SwitchJobSystemThreadAwaiter{ *copat::JobSystem::get(), copat::EJobThreadType::MainThread };
|
||||||
|
|
||||||
|
debugAssert(lastLoadedObj != nullptr);
|
||||||
|
gc.resumeGc();
|
||||||
|
gCBEditorEngine->openAssetEditor(lastLoadedObj);
|
||||||
|
gCBEditorEngine->progressProgressTracker(progTracker, 1, {});
|
||||||
|
}
|
||||||
ICoreAssetEditorRef EditorEngine::openAssetEditor(Object *obj)
|
ICoreAssetEditorRef EditorEngine::openAssetEditor(Object *obj)
|
||||||
{
|
{
|
||||||
debugAssert(obj);
|
debugAssert(obj);
|
||||||
/* Worlds must be loaded and unloaded into world Manager and editors will listen to events and process them. */
|
/* Worlds must be loaded and unloaded into world Manager and editors will listen to events and process them. */
|
||||||
if (cbe::World *newWorld = cbe::cast<cbe::World>(obj))
|
if (cbe::World *newWorld = cbe::cast<cbe::World>(obj))
|
||||||
{
|
{
|
||||||
cbe::World *mainWorld = gCBEEngine->worldManager()->getMainWorld();
|
WorldsManager *worldMan = gCBEEngine->worldManager();
|
||||||
|
cbe::World *mainWorld = worldMan->getMainWorld();
|
||||||
if (newWorld != mainWorld)
|
if (newWorld != mainWorld)
|
||||||
{
|
{
|
||||||
if (mainWorld != nullptr)
|
if (mainWorld != nullptr)
|
||||||
{
|
{
|
||||||
gCBEEngine->worldManager()->unloadWorld(mainWorld);
|
worldMan->unloadWorld(mainWorld);
|
||||||
}
|
}
|
||||||
const bool bAsMainWorld = true;
|
const bool bAsMainWorld = true;
|
||||||
gCBEEngine->worldManager()->initWorld(newWorld, bAsMainWorld);
|
worldMan->initWorld(newWorld, bAsMainWorld);
|
||||||
|
|
||||||
|
ICoreObjectsModule::get()->getGC().waitGc();
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -371,6 +480,19 @@ ICoreAssetEditorRef EditorEngine::openAssetEditor(Object *obj)
|
|||||||
mainEd->editorLayer->addAssetEditor(assetEditor);
|
mainEd->editorLayer->addAssetEditor(assetEditor);
|
||||||
return assetEditor;
|
return assetEditor;
|
||||||
}
|
}
|
||||||
|
ICoreAssetEditorRef EditorEngine::openAssetEditor(StringView objPath, CBEClass objClass)
|
||||||
|
{
|
||||||
|
if (PropertyHelper::isChildOf<World>(objClass))
|
||||||
|
{
|
||||||
|
asyncOpenScene(*copat::JobSystem::get(), objPath);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
cbe::Object *asset = cbe::getOrLoad(objPath, objClass);
|
||||||
|
return openAssetEditor(asset);
|
||||||
|
}
|
||||||
|
ICoreAssetEditorRef EditorEngine::openAssetEditor(const ObjectPath &objPath) { return openAssetEditor(objPath.getObject()); }
|
||||||
|
|
||||||
void EditorEngine::closeAssetEditor(const ICoreAssetEditorRef &editor)
|
void EditorEngine::closeAssetEditor(const ICoreAssetEditorRef &editor)
|
||||||
{
|
{
|
||||||
if (!mainEd->editorLayer->removeAssetEditor(editor))
|
if (!mainEd->editorLayer->removeAssetEditor(editor))
|
||||||
@@ -410,6 +532,68 @@ void EditorEngine::refreshAssetEditor(Object *obj)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EditorEngine::addNotification(const cbe::NotificationInfo &info) const { mainEd->editorLayer->addNotification(info); }
|
void EditorEngine::addNotification(const cbe::NotificationInfo &info) const { mainEd->editorLayer->addNotification(info); }
|
||||||
|
uint64 EditorEngine::addStickyNotification(const cbe::NotificationInfo &info) { return mainEd->editorLayer->addStickyNotification(info); }
|
||||||
|
void EditorEngine::removeStickyNotification(uint64 stickyId) { mainEd->editorLayer->removeStickyNotification(stickyId); }
|
||||||
|
|
||||||
|
static_assert(std::same_as<uint64, cbe::ProgressTrackerHnd>, "Update the progress tracker handle type!");
|
||||||
|
uint64 EditorEngine::addProgressTracker(const cbe::ProgressTrackerInfo &info) { return mainEd->editorLayer->addProgressTracker(info); }
|
||||||
|
void EditorEngine::progressProgressTracker(uint64 hnd, uint32 stepsCount, StringView stepDesc)
|
||||||
|
{
|
||||||
|
mainEd->editorLayer->progressProgressTracker(hnd, stepsCount, stepDesc);
|
||||||
|
}
|
||||||
|
void EditorEngine::setProgressTracerProgress(uint64 hnd, float progress) { mainEd->editorLayer->setProgressTracerProgress(hnd, progress); }
|
||||||
|
bool EditorEngine::isProgressTrackerCancelling(uint64 hnd) const { return mainEd->editorLayer->isProgressTrackerCancelling(hnd); }
|
||||||
|
void EditorEngine::cancelProgressTracker(uint64 hnd) { mainEd->editorLayer->cancelProgressTracker(hnd); }
|
||||||
|
|
||||||
|
void EditorEngine::onModuleLoadCb(const String &) { refreshTypeInfo(); }
|
||||||
|
void EditorEngine::onModuleUnloadCb(const String &) { refreshTypeInfo(); }
|
||||||
|
void EditorEngine::refreshTypeInfo()
|
||||||
|
{
|
||||||
|
IReflectionRuntimeModule &rttiModule = *IReflectionRuntimeModule::get();
|
||||||
|
|
||||||
|
/* Erase if class cannot be instantiated */
|
||||||
|
auto isAbstractClass = [](CBEClass clazz)
|
||||||
|
{
|
||||||
|
return clazz->constructors.empty();
|
||||||
|
};
|
||||||
|
auto fillClasses
|
||||||
|
= [&rttiModule, isAbstractClass]<bool InsertBase>(EdClassCacheList &cacheList, CBEClass baseClass, ValueToType<InsertBase>)
|
||||||
|
{
|
||||||
|
cacheList.classList.clear();
|
||||||
|
cacheList.classDrawInfo.clear();
|
||||||
|
|
||||||
|
if constexpr (InsertBase)
|
||||||
|
{
|
||||||
|
cacheList.classList.emplace_back(baseClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
rttiModule.getChildsOf(baseClass, cacheList.classList, true);
|
||||||
|
|
||||||
|
std::erase_if(cacheList.classList, isAbstractClass);
|
||||||
|
|
||||||
|
cacheList.classDrawInfo.reserve(cacheList.classList.size());
|
||||||
|
for (CBEClass clazz : cacheList.classList)
|
||||||
|
{
|
||||||
|
EdClassCacheDrawInfo &drawInf = cacheList.classDrawInfo.emplace_back();
|
||||||
|
|
||||||
|
std::array<TChar, wg_consts::CLASS_INITIALS_COUNT + 1> initials = {};
|
||||||
|
|
||||||
|
PropertyHelper::fillTypeInitials({ initials.data(), wg_consts::CLASS_INITIALS_COUNT }, clazz->nameString);
|
||||||
|
|
||||||
|
std::copy_n(TCHAR_TO_UTF8(initials.data()).data(), wg_consts::CLASS_INITIALS_COUNT, drawInf.classInitials.data());
|
||||||
|
drawInf.typeColor = PropertyHelper::getTypeColor(clazz);
|
||||||
|
}
|
||||||
|
cacheList.filteredBits.resize(cacheList.classList.size(), std::numeric_limits<uint64>::max());
|
||||||
|
};
|
||||||
|
|
||||||
|
fillClasses(actorClasses, cbe::Actor::staticType(), ValueToType<true>{});
|
||||||
|
|
||||||
|
fillClasses(logicCompClasses, cbe::LogicComponent::staticType(), ValueToType<false>{});
|
||||||
|
|
||||||
|
fillClasses(tfLeafCompClasses, cbe::TransformLeafComponent::staticType(), ValueToType<false>{});
|
||||||
|
|
||||||
|
fillClasses(tfCompClass, cbe::TransformComponent::staticType(), ValueToType<true>{});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace cbe
|
} // namespace cbe
|
||||||
|
|
||||||
@@ -443,20 +627,11 @@ COMPILER_PRAGMA(COMPILER_DISABLE_WARNING(WARN_UNREACHABLE_CODE WARN_UNUSED_LOCAL
|
|||||||
#include "CbeRendererTypes.h"
|
#include "CbeRendererTypes.h"
|
||||||
#include "ICbeRendererModule.h"
|
#include "ICbeRendererModule.h"
|
||||||
#include "GCReferenceCollector.h"
|
#include "GCReferenceCollector.h"
|
||||||
#include "CoreObjectGC.h"
|
|
||||||
|
|
||||||
std::vector<cbe::Actor *> worldCubes;
|
std::vector<cbe::Actor *> worldCubes;
|
||||||
cbe::ActorPrefab *cubeActorPrefab = nullptr;
|
cbe::ActorPrefab *cubeActorPrefab = nullptr;
|
||||||
|
|
||||||
constexpr uint32 MATS_COUNT = 17;
|
// TODO(ASAP) : Keep this until the actual materials are ready.
|
||||||
const std::array<const TChar *, MATS_COUNT> texNames{
|
|
||||||
TCHAR("Bricks059"), TCHAR("Bricks065"), TCHAR("Gravel022"), TCHAR("Ground037"), TCHAR("Ground042"), TCHAR("Leather028"),
|
|
||||||
TCHAR("Marble006"), TCHAR("Metal034"), TCHAR("Metal038"), TCHAR("MetalPlates006"), TCHAR("PaintedPlaster016"), TCHAR("Rock035"),
|
|
||||||
TCHAR("Tiles074"), TCHAR("Tiles086"), TCHAR("Tiles108"), TCHAR("Wood051"), TCHAR("WoodFloor043"),
|
|
||||||
};
|
|
||||||
std::array<cbe::MaterialInstance *, MATS_COUNT> matInsts;
|
|
||||||
std::array<cbe::Texture2D *, MATS_COUNT * 2> textures;
|
|
||||||
|
|
||||||
CBE_MATERIAL_STRUCT_BEGIN(SampleMaterialBuffer, false)
|
CBE_MATERIAL_STRUCT_BEGIN(SampleMaterialBuffer, false)
|
||||||
CBE_MATERIAL_STRUCT_FIELD_TEXTURE(color, Linear)
|
CBE_MATERIAL_STRUCT_FIELD_TEXTURE(color, Linear)
|
||||||
CBE_MATERIAL_STRUCT_FIELD_TEXTURE(normal, Nearest)
|
CBE_MATERIAL_STRUCT_FIELD_TEXTURE(normal, Nearest)
|
||||||
@@ -473,122 +648,29 @@ cbe::physics::Body pHeightBody;
|
|||||||
std::vector<cbe::physics::Body> pWorldBodies;
|
std::vector<cbe::physics::Body> pWorldBodies;
|
||||||
|
|
||||||
bool bUseCubeScene = false;
|
bool bUseCubeScene = false;
|
||||||
bool bUseMaterials = false;
|
|
||||||
|
|
||||||
bool bStartTestScene = false;
|
bool bStartTestScene = false;
|
||||||
|
|
||||||
class TempRefCollector : public ISelfRegisterReferenceCollector
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void clearReferences(ArrayView<cbe::Object *> deletedObjects) override { debugAssert(deletedObjects.empty()); }
|
|
||||||
|
|
||||||
void collectReferences(std::vector<cbe::Object *> &outObjects) const override
|
|
||||||
{
|
|
||||||
if (bUseCubeScene && bUseMaterials)
|
|
||||||
{
|
|
||||||
outObjects.reserve(MATS_COUNT * 3);
|
|
||||||
outObjects.insert(outObjects.cend(), matInsts.cbegin(), matInsts.cend());
|
|
||||||
outObjects.insert(outObjects.cend(), textures.cbegin(), textures.cend());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ReferenceCountPtr<TempRefCollector> tempRefCollector;
|
|
||||||
|
|
||||||
void tempTestScenes()
|
void tempTestScenes()
|
||||||
{
|
{
|
||||||
String dir = Paths::engineContentDirectory();
|
String dir = Paths::engineContentDirectory();
|
||||||
String name = Paths::applicationName();
|
String name = Paths::applicationName();
|
||||||
#if 0
|
|
||||||
if (BasicPackagedObject *obj = cbe::load<BasicPackagedObject>(name))
|
|
||||||
{
|
|
||||||
LOG("Test", "Loaded object {} nameVal {}", obj->getObjectData().path, obj->nameVal);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cbe::Package *package = cbe::Package::createPackage(name, dir, false);
|
|
||||||
cbe::Package *package2 = cbe::Package::createPackage(name + TCHAR("2"), dir, false);
|
|
||||||
|
|
||||||
BasicPackagedObject *packedObj2 = cbe::create<BasicPackagedObject>(name, package2);
|
|
||||||
packedObj2->dt = 0.56f;
|
|
||||||
packedObj2->nameVal = TCHAR("Its connected object");
|
|
||||||
packedObj2->structData = { .a = 4124111.06f, .b = 2026, .testStr = "This must be connected to another package" };
|
|
||||||
BasicPackagedObject *packedObj = cbe::create<BasicPackagedObject>(name, package);
|
|
||||||
packedObj->dt = 0.28f;
|
|
||||||
packedObj->id = STRID("Hello Subity & Jeslas");
|
|
||||||
packedObj->nameVal = TCHAR("Its Me Jeslas");
|
|
||||||
packedObj->idxToStr = {
|
|
||||||
{ 1, TCHAR("Jeslas Pravin") },
|
|
||||||
{ 2, TCHAR("Subity Jerald") }
|
|
||||||
};
|
|
||||||
packedObj->structData = { .a = 8235.28f, .b = 834435, .testStr = "3528" };
|
|
||||||
packedObj->interLinked = packedObj2;
|
|
||||||
|
|
||||||
BasicFieldSerializedObject *testTemp = cbe::create<BasicFieldSerializedObject>(name, package);
|
|
||||||
testTemp->dt = 101.111f;
|
|
||||||
testTemp->id = STRID("HEll Let lOsE");
|
|
||||||
testTemp->interLinked = packedObj;
|
|
||||||
testTemp->nameVal = TCHAR("Test All field serialization!");
|
|
||||||
testTemp->structData = { .a = 4321, .b = 1234, .testStr = "Not a default value here!" };
|
|
||||||
testTemp->idxToStr[10] = {
|
|
||||||
{ TCHAR("ABC"), 123 },
|
|
||||||
{ TCHAR("CBA"), 321 }
|
|
||||||
};
|
|
||||||
testTemp->idxToStr[5] = {
|
|
||||||
{ TCHAR("XYZ"), 55667788 },
|
|
||||||
{ TCHAR("ZYX"), 8235 }
|
|
||||||
};
|
|
||||||
IInterfaceExample *interface1 = cbe::cast<IInterfaceExample>(static_cast<cbe::Object *>(testTemp));
|
|
||||||
IInterfaceExample2 *interface2 = cbe::cast<IInterfaceExample2>(static_cast<cbe::Object *>(testTemp));
|
|
||||||
IInterfaceExample2 *interface3 = cbe::cast<IInterfaceExample2>(interface1);
|
|
||||||
BasicFieldSerializedObject *ixToClassObj = cbe::cast<BasicFieldSerializedObject>(interface1);
|
|
||||||
cbe::Object *ix1ToClassObj = cbe::cast<cbe::Object>(interface1);
|
|
||||||
cbe::Object *ix2ToClassObj = cbe::cast<cbe::Object>(interface2);
|
|
||||||
BasicPackagedObject *failingCast = cbe::cast<BasicPackagedObject>(interface1);
|
|
||||||
|
|
||||||
BasicFieldSerializedObject *copied = cbe::cast<BasicFieldSerializedObject>(cbe::duplicateObject(testTemp, package));
|
|
||||||
|
|
||||||
cbe::save(packedObj);
|
|
||||||
cbe::save(packedObj2);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
|
|
||||||
String importContentTo = PathFunctions::combinePath(Paths::engineRuntimeRoot(), TCHAR("Content"));
|
String importContentTo = Paths::engineContentDirectory();
|
||||||
// Create cube first
|
// Create cube first
|
||||||
cbe::StaticMesh *cubeMesh = cbe::getOrLoad<cbe::StaticMesh>(TCHAR("Meshes/Cube:Cube"));
|
cbe::StaticMesh *cubeMesh = cbe::getOrLoad<cbe::StaticMesh>(TCHAR("Meshes/Cube:Cube"));
|
||||||
if (!cbe::isValid(cubeMesh))
|
if (!cbe::isValid(cubeMesh))
|
||||||
{
|
{
|
||||||
#if 0 // Enable below if want to import cube from obj file
|
|
||||||
const TChar *cubeObjPath = TCHAR("D:/Assets/EngineAssets/Cube.obj");
|
|
||||||
ImportOption opt;
|
|
||||||
opt.filePath = cubeObjPath;
|
|
||||||
opt.importContentPath = importContentTo;
|
|
||||||
opt.relativeDirPath = TCHAR("Meshes");
|
|
||||||
if (AssetImporterBase *importer = IEditorCore::get()->findAssetImporter(opt))
|
|
||||||
{
|
|
||||||
bool bImportAsScene = false;
|
|
||||||
float scale = 0.01f;
|
|
||||||
static_cast<const MemberFieldWrapper *>(PropertyHelper::findField(opt.optionsStructType, STRID("bImportAsScene"))->fieldPtr)
|
|
||||||
->setTypeless(&bImportAsScene, opt.optionsStruct);
|
|
||||||
static_cast<const MemberFieldWrapper *>(PropertyHelper::findField(opt.optionsStructType, STRID("scale"))->fieldPtr)
|
|
||||||
->setTypeless(&scale, opt.optionsStruct);
|
|
||||||
|
|
||||||
std::vector<cbe::Object *> objs = *importer->tryImporting(opt);
|
|
||||||
debugAssert(objs.size() == 1);
|
|
||||||
|
|
||||||
cubeMesh = cbe::cast<cbe::StaticMesh>(objs[0]);
|
|
||||||
cbe::save(cubeMesh);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
cbe::SMCreateInfo createInfo;
|
cbe::SMCreateInfo createInfo;
|
||||||
cbe::EditorGeomHelpers::createCube(createInfo, 1.0f);
|
cbe::EditorGeomHelpers::createCube(createInfo, 1.0f);
|
||||||
cubeMesh = EditorHelpers::createStaticMesh(TCHAR("Meshes/Cube"), importContentTo, TCHAR("Cube"), std::move(createInfo));
|
cubeMesh = EditorHelpers::createStaticMesh(TCHAR("Meshes/Cube"), importContentTo, TCHAR("Cube"), std::move(createInfo));
|
||||||
cbe::save(cubeMesh);
|
cbe::save(cubeMesh);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
// Create or get cube actor prefab
|
// Create or get cube actor prefab
|
||||||
debugAssert(cubeMesh);
|
debugAssert(cubeMesh);
|
||||||
|
|
||||||
const TChar *cubeActorPath = TCHAR("Prefabs/CubeActor:CubeActor");
|
const TChar *cubeActorPath = TCHAR("Prefabs/CubeActor:CubeActor");
|
||||||
cubeActorPrefab = cbe::getOrLoad<cbe::ActorPrefab>(cubeActorPath);
|
cubeActorPrefab = cbe::getOrLoad<cbe::ActorPrefab>(cubeActorPath);
|
||||||
if (cubeActorPrefab == nullptr)
|
if (cubeActorPrefab == nullptr)
|
||||||
@@ -621,65 +703,6 @@ void tempTestScenes()
|
|||||||
cbe::save(cubeActorPrefab);
|
cbe::save(cubeActorPrefab);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bUseCubeScene)
|
|
||||||
{
|
|
||||||
const TChar *meshObjPath = TCHAR("D:/Assets/Scenes/CrytekSponza/sponza.obj");
|
|
||||||
const TChar *meshEnginePath = TCHAR("Scenes/sponza:sponza");
|
|
||||||
// const TChar *meshObjPath = TCHAR("D:/Assets/Scenes/LumberyardBistro/Exterior/LumberyardBistroExterior.obj");
|
|
||||||
// const TChar *meshEnginePath = TCHAR("Scenes/LumberyardBistroExterior:LumberyardBistroExterior");
|
|
||||||
cbe::World *sceneObj = cbe::getOrLoad<cbe::World>(meshEnginePath);
|
|
||||||
if (sceneObj == nullptr && FileSystemFunctions::fileExists(meshObjPath))
|
|
||||||
{
|
|
||||||
ImportOption opt;
|
|
||||||
opt.filePath = meshObjPath;
|
|
||||||
opt.importContentPath = importContentTo;
|
|
||||||
opt.relativeDirPath = TCHAR("Scenes");
|
|
||||||
if (AssetImporterBase *importer = IEditorCore::get()->findAssetImporter(opt))
|
|
||||||
{
|
|
||||||
bool bImportAsScene = true;
|
|
||||||
bool bIsYUp = true;
|
|
||||||
float scale = 0.01f;
|
|
||||||
static_cast<const MemberFieldWrapper *>(PropertyHelper::findField(opt.optionsStructType, STRID("bImportAsScene"))->fieldPtr)
|
|
||||||
->setTypeless(&bImportAsScene, opt.optionsStruct);
|
|
||||||
static_cast<const MemberFieldWrapper *>(PropertyHelper::findField(opt.optionsStructType, STRID("bFromYUp"))->fieldPtr)
|
|
||||||
->setTypeless(&bIsYUp, opt.optionsStruct);
|
|
||||||
static_cast<const MemberFieldWrapper *>(PropertyHelper::findField(opt.optionsStructType, STRID("scale"))->fieldPtr)
|
|
||||||
->setTypeless(&scale, opt.optionsStruct);
|
|
||||||
|
|
||||||
std::vector<cbe::Object *> objs = *importer->tryImporting(opt);
|
|
||||||
for (cbe::Object *obj : objs)
|
|
||||||
{
|
|
||||||
cbe::save(obj);
|
|
||||||
}
|
|
||||||
if (!objs.empty())
|
|
||||||
{
|
|
||||||
sceneObj = cbe::cast<cbe::World>(objs[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sceneObj)
|
|
||||||
{
|
|
||||||
|
|
||||||
gCBEEngine->worldManager()->initWorld(sceneObj, true);
|
|
||||||
|
|
||||||
EditorHelpers::addComponentToPrefab(cubeActorPrefab, test::TestLogicComp::staticType(), TCHAR("TestLogic"));
|
|
||||||
cbe::ActorPrefab *smActorPrefab = cbe::ActorPrefab::prefabFromActor(
|
|
||||||
EditorHelpers::addActorToWorld(gCBEEngine->worldManager()->getEditorWorld(), cubeActorPrefab, TCHAR("TestCube"), 0)
|
|
||||||
);
|
|
||||||
smActorPrefab = cbe::get<cbe::ActorPrefab>(
|
|
||||||
ObjectPathHelper::getFullPath(smActorPrefab->getObjectData().name, gCBEEngine->worldManager()->getEditorWorld())
|
|
||||||
);
|
|
||||||
debugAssert(smActorPrefab);
|
|
||||||
worldCubes.emplace_back(smActorPrefab->getActorTemplate());
|
|
||||||
smActorPrefab->getActorTemplate()->setWorldLocation({ 1.50f, 0, 1.00f });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bUseCubeScene = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bUseCubeScene)
|
if (bUseCubeScene)
|
||||||
{
|
{
|
||||||
const TChar *scenePath = TCHAR("Scenes/TestCubes:TestCubes");
|
const TChar *scenePath = TCHAR("Scenes/TestCubes:TestCubes");
|
||||||
@@ -772,71 +795,6 @@ void tempTestScenes()
|
|||||||
{
|
{
|
||||||
bUseCubeScene = false;
|
bUseCubeScene = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bUseMaterials)
|
|
||||||
{
|
|
||||||
tempRefCollector = new TempRefCollector();
|
|
||||||
|
|
||||||
/* If using cube scene add few textures and materials */
|
|
||||||
const TChar *texturesPathBase = TCHAR("D:/Assets/EngineAssets/Textures");
|
|
||||||
for (SizeT i = 0; i < MATS_COUNT; ++i)
|
|
||||||
{
|
|
||||||
const SizeT texIdx = i * 2;
|
|
||||||
const String engineColorTexturePath = ObjectPathHelper::combinePathComponents(
|
|
||||||
STR_FORMAT("Textures/{}_D", texNames[i]), TCHAR(""), STR_FORMAT("{}_D", texNames[i])
|
|
||||||
);
|
|
||||||
const String engineNormalTexturePath = ObjectPathHelper::combinePathComponents(
|
|
||||||
STR_FORMAT("Textures/{}_N", texNames[i]), TCHAR(""), STR_FORMAT("{}_N", texNames[i])
|
|
||||||
);
|
|
||||||
const String engineMatInstPath
|
|
||||||
= ObjectPathHelper::combinePathComponents(STR_FORMAT("Materials/{}", texNames[i]), TCHAR(""), texNames[i]);
|
|
||||||
textures[texIdx + 0] = cbe::getOrLoad<cbe::Texture2D>(engineColorTexturePath);
|
|
||||||
if (textures[texIdx + 0] != nullptr)
|
|
||||||
{
|
|
||||||
matInsts[i] = cbe::getOrLoad<cbe::MaterialInstance>(engineMatInstPath);
|
|
||||||
textures[texIdx + 1] = cbe::getOrLoad<cbe::Texture2D>(engineNormalTexturePath);
|
|
||||||
debugAssert(matInsts[i] != nullptr && textures[texIdx + 1] != nullptr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Import textures and create materials. */
|
|
||||||
const String colorTexturePath
|
|
||||||
= PathFunctions::combinePath(texturesPathBase, texNames[i], STR_FORMAT("{}_D.jpg", texNames[i]));
|
|
||||||
const String normalTexturePath
|
|
||||||
= PathFunctions::combinePath(texturesPathBase, texNames[i], STR_FORMAT("{}_N.jpg", texNames[i]));
|
|
||||||
|
|
||||||
ImportOption opt;
|
|
||||||
opt.filePath = colorTexturePath;
|
|
||||||
opt.importContentPath = importContentTo;
|
|
||||||
opt.relativeDirPath = TCHAR("Textures");
|
|
||||||
if (AssetImporterBase *importer = IEditorCore::get()->findAssetImporter(opt))
|
|
||||||
{
|
|
||||||
std::vector<cbe::Object *> objs = *importer->tryImporting(opt);
|
|
||||||
debugAssert(objs.size() == 1);
|
|
||||||
textures[texIdx + 0] = cbe::cast<cbe::Texture2D>(objs[0]);
|
|
||||||
}
|
|
||||||
opt.filePath = normalTexturePath;
|
|
||||||
if (AssetImporterBase *importer = IEditorCore::get()->findAssetImporter(opt))
|
|
||||||
{
|
|
||||||
std::vector<cbe::Object *> objs = *importer->tryImporting(opt);
|
|
||||||
debugAssert(objs.size() == 1);
|
|
||||||
textures[texIdx + 1] = cbe::cast<cbe::Texture2D>(objs[0]);
|
|
||||||
}
|
|
||||||
cbe::save(textures[texIdx + 0]);
|
|
||||||
cbe::save(textures[texIdx + 1]);
|
|
||||||
|
|
||||||
cbe::MaterialInstance *matInst
|
|
||||||
= EditorHelpers::createMaterialInstance(STR_FORMAT("Materials/{}", texNames[i]), importContentTo, texNames[i]);
|
|
||||||
matInsts[i] = matInst;
|
|
||||||
matInst->data.resize(sizeof(SampleMaterialBuffer));
|
|
||||||
matInst->dataLayoutName = SampleMaterialBuffer::bufferLayout().name;
|
|
||||||
matInst->materialName = TCHAR("SampleLit");
|
|
||||||
matInst->texture2dRefs.emplace_back(TCHAR("color"), textures[texIdx + 0]);
|
|
||||||
matInst->texture2dRefs.emplace_back(TCHAR("normal"), textures[texIdx + 1]);
|
|
||||||
cbe::save(matInst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -850,7 +808,6 @@ void tempTest()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
bUseCubeScene = false;
|
bUseCubeScene = false;
|
||||||
bUseMaterials = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -894,15 +851,6 @@ void tempTickTest(const ApplicationTimeData &timeData, const Camera &camera)
|
|||||||
));
|
));
|
||||||
worldCubes.emplace_back(smActorPrefab->getActorTemplate());
|
worldCubes.emplace_back(smActorPrefab->getActorTemplate());
|
||||||
smActorPrefab->getActorTemplate()->setWorldLocation(offset[worldCubes.size() % 4]);
|
smActorPrefab->getActorTemplate()->setWorldLocation(offset[worldCubes.size() % 4]);
|
||||||
/* Set a new material */
|
|
||||||
std::vector<cbe::TransformLeafComponent *> leafComps;
|
|
||||||
cbe::WACHelpers::getComponentLeafs(smActorPrefab->getRootComponent(), leafComps);
|
|
||||||
debugAssert(leafComps.size() == 1);
|
|
||||||
if (bUseMaterials)
|
|
||||||
{
|
|
||||||
cbe::StaticMeshComponent *smComp = cbe::cast<cbe::StaticMeshComponent>(leafComps[0]);
|
|
||||||
smComp->setMaterial(matInsts[worldCubes.size() % MATS_COUNT]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pWorldBodies.resize(startIdx + 4);
|
pWorldBodies.resize(startIdx + 4);
|
||||||
@@ -922,45 +870,10 @@ void tempTickTest(const ApplicationTimeData &timeData, const Camera &camera)
|
|||||||
app->getPhysicsBackend()->addBodies(pWorld, { pWorldBodies.data() + startIdx, 4 }, true);
|
app->getPhysicsBackend()->addBodies(pWorld, { pWorldBodies.data() + startIdx, 4 }, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!worldCubes.empty())
|
|
||||||
{
|
|
||||||
for (auto itr = worldCubes.begin(); itr != worldCubes.end();)
|
|
||||||
{
|
|
||||||
cbe::Actor *ac = *itr;
|
|
||||||
if (!cbe::isValid(ac))
|
|
||||||
{
|
|
||||||
itr = worldCubes.erase(itr);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 i = 1;
|
|
||||||
while (i > 0)
|
|
||||||
{
|
|
||||||
Rotation r = ac->getWorldRotation();
|
|
||||||
static Vector3 n = Vector3::ONE.normalized() * Vector3(-1, 1, 1);
|
|
||||||
Quat rQ = Quat::fromRotation(r);
|
|
||||||
Quat delQ = Quat::fromAngleAxis(180 * timeData.deltaTime, n);
|
|
||||||
Quat newQ = delQ * rQ;
|
|
||||||
Rotation newR = newQ.toRotation();
|
|
||||||
alertAlways(!newR.isNan() && newR.isFinite());
|
|
||||||
|
|
||||||
Vector3 l = ac->getWorldLocation();
|
|
||||||
l.x = 2 * MathEasing::sharpTooth(Math::frac(Time::asSeconds(Time::timeNow()) / 3.0f));
|
|
||||||
|
|
||||||
ac->setWorldRotation(newR);
|
|
||||||
ac->setWorldLocation(l);
|
|
||||||
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
++itr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tempExitTest()
|
void tempExitTest()
|
||||||
{
|
{
|
||||||
tempRefCollector.reset();
|
|
||||||
|
|
||||||
if (bUseCubeScene)
|
if (bUseCubeScene)
|
||||||
{
|
{
|
||||||
CranberryEngineApp *app = static_cast<CranberryEngineApp *>(IApplicationModule::get()->getApplication());
|
CranberryEngineApp *app = static_cast<CranberryEngineApp *>(IApplicationModule::get()->getApplication());
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -17,9 +17,10 @@
|
|||||||
#include <Classes/EngineBase.hpp>
|
#include <Classes/EngineBase.hpp>
|
||||||
#include <Classes/Actor.hpp>
|
#include <Classes/Actor.hpp>
|
||||||
#include <ObjectPtrs.h>
|
#include <ObjectPtrs.h>
|
||||||
#include <CbeWorldRenderer/WorldSelectionProxy.hpp>
|
|
||||||
#include <Transaction/TransactionsLedger.hpp>
|
#include <Transaction/TransactionsLedger.hpp>
|
||||||
#include <ICoreAssetEditor.hpp>
|
#include <ICoreAssetEditor.hpp>
|
||||||
|
#include <EditorTypes.h>
|
||||||
|
#include <Widgets/EditorWidgetsHelper.hpp>
|
||||||
|
|
||||||
#include "EditorEngine.gen.hpp"
|
#include "EditorEngine.gen.hpp"
|
||||||
|
|
||||||
@@ -43,7 +44,9 @@ class Texture2D;
|
|||||||
class StaticMesh;
|
class StaticMesh;
|
||||||
|
|
||||||
struct NotificationInfo;
|
struct NotificationInfo;
|
||||||
|
struct ProgressTrackerInfo;
|
||||||
class EditorEngine;
|
class EditorEngine;
|
||||||
|
class AssetManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Inheriting ICoreAssetEditor in order to support the message queue in unified manner.
|
* @brief Inheriting ICoreAssetEditor in order to support the message queue in unified manner.
|
||||||
@@ -88,10 +91,6 @@ private:
|
|||||||
DelegateHandle worldInitHandle;
|
DelegateHandle worldInitHandle;
|
||||||
DelegateHandle worldUnloadHandle;
|
DelegateHandle worldUnloadHandle;
|
||||||
|
|
||||||
/* For tracking selections only. */
|
|
||||||
std::vector<WeakObjPtr<Object>> mainWorldSelObjs;
|
|
||||||
std::vector<WorldSelectionProxyRef> mainWorldSelProxies;
|
|
||||||
|
|
||||||
TransactionsLedger undoRedoLedger;
|
TransactionsLedger undoRedoLedger;
|
||||||
|
|
||||||
bool bIsPlayInEd:1 = false;
|
bool bIsPlayInEd:1 = false;
|
||||||
@@ -109,7 +108,7 @@ public:
|
|||||||
cbe::ETryExit engineTryExit() override;
|
cbe::ETryExit engineTryExit() override;
|
||||||
void engineExit() override;
|
void engineExit() override;
|
||||||
/* EngineBase overrides */
|
/* EngineBase overrides */
|
||||||
void destroy() override;
|
void onDestructed() override;
|
||||||
/* Override ends */
|
/* Override ends */
|
||||||
|
|
||||||
/* Do not hold reference to pointer */
|
/* Do not hold reference to pointer */
|
||||||
@@ -117,22 +116,75 @@ public:
|
|||||||
EngineMainEd *getMainEd() const { return mainEd.get(); }
|
EngineMainEd *getMainEd() const { return mainEd.get(); }
|
||||||
|
|
||||||
const String &transactionFolder() const { return transactionDir; }
|
const String &transactionFolder() const { return transactionDir; }
|
||||||
|
TransactionsLedger &getTransactionLedger() const { return *mainEd->getTransactionLedger(); }
|
||||||
|
|
||||||
|
AssetManager *getAssetManager() const { return assetManager; }
|
||||||
|
|
||||||
/* Asset editor helpers */
|
/* Asset editor helpers */
|
||||||
ICoreAssetEditorRef openAssetEditor(Object *obj);
|
ICoreAssetEditorRef openAssetEditor(Object *obj);
|
||||||
|
ICoreAssetEditorRef openAssetEditor(StringView objPath, CBEClass objClass);
|
||||||
|
ICoreAssetEditorRef openAssetEditor(const ObjectPath &objPath);
|
||||||
void closeAssetEditor(const ICoreAssetEditorRef &editor);
|
void closeAssetEditor(const ICoreAssetEditorRef &editor);
|
||||||
ICoreAssetEditorRef getAssetEditor(Object *obj);
|
ICoreAssetEditorRef getAssetEditor(Object *obj);
|
||||||
void refreshAssetEditor(Object *obj);
|
void refreshAssetEditor(Object *obj);
|
||||||
|
|
||||||
void addNotification(const cbe::NotificationInfo &info) const;
|
void addNotification(const cbe::NotificationInfo &info) const;
|
||||||
TransactionsLedger &getTransactionLedger() const { return *mainEd->getTransactionLedger(); }
|
/**
|
||||||
|
* @brief Notifications that sticks until explicitly removed by Sticky ID.
|
||||||
|
* @param info Notification info.
|
||||||
|
*/
|
||||||
|
uint64 addStickyNotification(const cbe::NotificationInfo &info);
|
||||||
|
void removeStickyNotification(uint64 stickyId);
|
||||||
|
/**
|
||||||
|
* @brief Adds a new progress tracker and returns tracker handle that can used to update progress.
|
||||||
|
* @param info Progress tracker information.
|
||||||
|
* @return Progress tracker handle.
|
||||||
|
*/
|
||||||
|
uint64 addProgressTracker(const cbe::ProgressTrackerInfo &info);
|
||||||
|
/**
|
||||||
|
* @brief Progresses the tracker by provided steps count and updates the next step with new description.
|
||||||
|
* @param hnd Progress tracker handle.
|
||||||
|
* @param stepsCount Number of steps to progress.
|
||||||
|
* @param stepDesc If empty previous description is used.
|
||||||
|
*/
|
||||||
|
void progressProgressTracker(uint64 hnd, uint32 stepsCount, StringView stepDesc);
|
||||||
|
/**
|
||||||
|
* @brief Sets the continuous progress.
|
||||||
|
* @param hnd Progress tracker handle.
|
||||||
|
* @param progress Progress value between 0.0 - 1.0.
|
||||||
|
*/
|
||||||
|
void setProgressTracerProgress(uint64 hnd, float progress);
|
||||||
|
bool isProgressTrackerCancelling(uint64 hnd) const;
|
||||||
|
void cancelProgressTracker(uint64 hnd);
|
||||||
|
|
||||||
|
EdClassCacheList &getActorClassesCache() { return actorClasses; }
|
||||||
|
EdClassCacheList &getTfLeafClassesCache() { return tfLeafCompClasses; }
|
||||||
|
EdClassCacheList &getLogicCompClassesCache() { return logicCompClasses; }
|
||||||
|
EdClassCacheList &getTfCompClassCache() { return tfCompClass; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SharedPtr<WgImGui> wgImgui;
|
SharedPtr<WgImGui> wgImgui;
|
||||||
ReferenceCountPtr<EngineMainEd> mainEd;
|
ReferenceCountPtr<EngineMainEd> mainEd;
|
||||||
|
|
||||||
|
AssetManager *assetManager;
|
||||||
|
|
||||||
String transactionDir;
|
String transactionDir;
|
||||||
|
|
||||||
|
DelegateHandle modLoadCbHndl;
|
||||||
|
DelegateHandle modUnloadCbHndl;
|
||||||
|
EdClassCacheList actorClasses;
|
||||||
|
EdClassCacheList tfLeafCompClasses;
|
||||||
|
EdClassCacheList logicCompClasses;
|
||||||
|
/* Only one but it is okay to follow same code path for similar logic */
|
||||||
|
EdClassCacheList tfCompClass;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onModuleLoadCb(const String &);
|
||||||
|
void onModuleUnloadCb(const String &);
|
||||||
|
|
||||||
|
/* Refreshes editor cached RTTI data */
|
||||||
|
void refreshTypeInfo();
|
||||||
|
|
||||||
} META_ANNOTATE(NoExport);
|
} META_ANNOTATE(NoExport);
|
||||||
|
|
||||||
CBEEDITOR_EXPORT extern EditorEngine *gCBEditorEngine;
|
CBEEDITOR_EXPORT extern EditorEngine *gCBEditorEngine;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2025
|
* \date August 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -54,16 +54,22 @@ void EditorGeomHelpers::createCube(SMCreateInfo &outCreateInfo, float size, Vect
|
|||||||
texCoord[0] = (km + 1) / 2;
|
texCoord[0] = (km + 1) / 2;
|
||||||
texCoord[1] = (jm + 1) / 2;
|
texCoord[1] = (jm + 1) / 2;
|
||||||
|
|
||||||
|
/* Left Handedness: Flip along Y - Axis */
|
||||||
|
position.y = -position.y;
|
||||||
|
normal.y = -normal.y;
|
||||||
|
tangent.y = -tangent.y;
|
||||||
|
|
||||||
outCreateInfo.vertsS0.emplace_back(vert0);
|
outCreateInfo.vertsS0.emplace_back(vert0);
|
||||||
outCreateInfo.vertsS1.emplace_back(vert1);
|
outCreateInfo.vertsS1.emplace_back(vert1);
|
||||||
}
|
}
|
||||||
|
|
||||||
outCreateInfo.indices.emplace_back(startVertIdx + 0);
|
/* Left Handedness: Reverse winding order */
|
||||||
|
outCreateInfo.indices.emplace_back(startVertIdx + 2);
|
||||||
outCreateInfo.indices.emplace_back(startVertIdx + 1);
|
outCreateInfo.indices.emplace_back(startVertIdx + 1);
|
||||||
|
outCreateInfo.indices.emplace_back(startVertIdx + 0);
|
||||||
|
outCreateInfo.indices.emplace_back(startVertIdx + 3);
|
||||||
outCreateInfo.indices.emplace_back(startVertIdx + 2);
|
outCreateInfo.indices.emplace_back(startVertIdx + 2);
|
||||||
outCreateInfo.indices.emplace_back(startVertIdx + 0);
|
outCreateInfo.indices.emplace_back(startVertIdx + 0);
|
||||||
outCreateInfo.indices.emplace_back(startVertIdx + 2);
|
|
||||||
outCreateInfo.indices.emplace_back(startVertIdx + 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32 i = 0; i < outCreateInfo.vertsS0.size(); ++i)
|
for (uint32 i = 0; i < outCreateInfo.vertsS0.size(); ++i)
|
||||||
@@ -79,6 +85,8 @@ void EditorGeomHelpers::createCube(SMCreateInfo &outCreateInfo, float size, Vect
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(Jeslas) : Make everything left handed. Only cube is converted to left handed. Right handed mesh has flip textures in Camera view.
|
||||||
|
|
||||||
void EditorGeomHelpers::createSphere(SMCreateInfo &outCreateInfo, float diameter, uint32 latSegCount, uint32 longSegCount)
|
void EditorGeomHelpers::createSphere(SMCreateInfo &outCreateInfo, float diameter, uint32 latSegCount, uint32 longSegCount)
|
||||||
{
|
{
|
||||||
longSegCount = Math::max(longSegCount, 4);
|
longSegCount = Math::max(longSegCount, 4);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -66,6 +66,30 @@ void EditorHelpers::makePackageUnique(String &inOutPackagePath, StringView suffi
|
|||||||
inOutPackagePath = outPath;
|
inOutPackagePath = outPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditorHelpers::makeComponentUnique(cbe::Actor *actor, String &inOutCompName, StringView suffix, const CoreObjectsDB &objDb)
|
||||||
|
{
|
||||||
|
std::vector<cbe::Object *> comps = getActorCompSubObjects(actor, objDb);
|
||||||
|
std::unordered_set<NameString> objNames;
|
||||||
|
|
||||||
|
objNames.reserve(comps.size());
|
||||||
|
for (cbe::Object *obj : comps)
|
||||||
|
{
|
||||||
|
objNames.insert(obj->getObjectData().name);
|
||||||
|
}
|
||||||
|
|
||||||
|
String outName = inOutCompName;
|
||||||
|
outName.append(suffix);
|
||||||
|
|
||||||
|
uint32 counter = 0;
|
||||||
|
while (objNames.contains(NameString(outName)))
|
||||||
|
{
|
||||||
|
counter++;
|
||||||
|
outName = inOutCompName + String::toString(counter);
|
||||||
|
outName.append(suffix);
|
||||||
|
}
|
||||||
|
inOutCompName = outName;
|
||||||
|
}
|
||||||
|
|
||||||
void EditorHelpers::renamePkgObjsToMatchPkg(
|
void EditorHelpers::renamePkgObjsToMatchPkg(
|
||||||
const CoreObjectsDB &objectsDb, StringView orgPkgName, StringView orgRootObjName, cbe::Package *pkg, bool bRecurse
|
const CoreObjectsDB &objectsDb, StringView orgPkgName, StringView orgRootObjName, cbe::Package *pkg, bool bRecurse
|
||||||
)
|
)
|
||||||
@@ -195,7 +219,7 @@ std::pair<String, std::vector<cbe::Object *>> EditorHelpers::redoTransaction(cbe
|
|||||||
}
|
}
|
||||||
|
|
||||||
cbe::Object *EditorHelpers::createAsset(
|
cbe::Object *EditorHelpers::createAsset(
|
||||||
cbe::ICoreAssetFactory *assetFactory, CBEClass clazz, StringView packageRelPath, StringView contentDir,
|
ArrayView<cbe::ICoreAssetFactory *> assetFactories, CBEClass clazz, StringView packageRelPath, StringView contentDir,
|
||||||
const SingleCastDelegate<cbe::Object *, const cbe::AssetCreateInfo &> &createCb
|
const SingleCastDelegate<cbe::Object *, const cbe::AssetCreateInfo &> &createCb
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -209,24 +233,36 @@ cbe::Object *EditorHelpers::createAsset(
|
|||||||
|
|
||||||
const StringView objName = ObjectPathHelper::getObjectName(packageRelPath);
|
const StringView objName = ObjectPathHelper::getObjectName(packageRelPath);
|
||||||
cbe::Object *obj = nullptr;
|
cbe::Object *obj = nullptr;
|
||||||
if (assetFactory != nullptr)
|
|
||||||
{
|
|
||||||
const cbe::AssetCreateInfo assetCi{
|
const cbe::AssetCreateInfo assetCi{
|
||||||
.assetName = objName,
|
.assetName = objName,
|
||||||
.clazz = clazz,
|
.clazz = clazz,
|
||||||
.outer = package,
|
.outer = package,
|
||||||
.flags = cbe::ObjFlag_PackageLoaded,
|
.flags = cbe::ObjFlag_PackageLoaded,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (createCb.isBound())
|
if (createCb.isBound())
|
||||||
{
|
{
|
||||||
obj = createCb(assetCi);
|
obj = createCb(assetCi);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (obj == nullptr && !assetFactories.empty())
|
||||||
{
|
{
|
||||||
obj = assetFactory->createAsset(assetCi);
|
for (cbe::ICoreAssetFactory *factory : assetFactories)
|
||||||
|
{
|
||||||
|
if (!factory->canCreateAsset())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
obj = factory->createAsset(assetCi);
|
||||||
|
if (obj != nullptr)
|
||||||
|
{
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
|
||||||
|
if (obj == nullptr)
|
||||||
{
|
{
|
||||||
obj = cbe::create(clazz, objName, package, cbe::ObjFlag_PackageLoaded);
|
obj = cbe::create(clazz, objName, package, cbe::ObjFlag_PackageLoaded);
|
||||||
}
|
}
|
||||||
@@ -278,12 +314,12 @@ void EditorHelpers::saveEditingAsset(cbe::ICoreAssetEditor *assetEditor, cbe::IC
|
|||||||
ArrayArchiveStream archiveStream;
|
ArrayArchiveStream archiveStream;
|
||||||
EPackageLoadSaveResult result
|
EPackageLoadSaveResult result
|
||||||
= PackageSaver::saveFromPackage(static_cast<cbe::Package *>(editingAsset->getOuterMost()), archiveStream);
|
= PackageSaver::saveFromPackage(static_cast<cbe::Package *>(editingAsset->getOuterMost()), archiveStream);
|
||||||
LOG_ERROR_C(
|
CBE_LOG_ERROR_C(
|
||||||
result != EPackageLoadSaveResult::Success, LOG_CATEGORY, "Failed to serialize editing asset {}",
|
result != EPackageLoadSaveResult::Success, LOG_CATEGORY, "Failed to serialize editing asset {}",
|
||||||
editingAsset->getObjectData().path
|
editingAsset->getObjectData().path
|
||||||
);
|
);
|
||||||
result = PackageLoader::loadIntoPackage(archiveStream, static_cast<cbe::Package *>(asset->getOuterMost()));
|
result = PackageLoader::loadIntoPackage(archiveStream, static_cast<cbe::Package *>(asset->getOuterMost()));
|
||||||
LOG_ERROR_C(
|
CBE_LOG_ERROR_C(
|
||||||
result != EPackageLoadSaveResult::Success, LOG_CATEGORY, "Failed to deserialize into asset {}", asset->getObjectData().path
|
result != EPackageLoadSaveResult::Success, LOG_CATEGORY, "Failed to deserialize into asset {}", asset->getObjectData().path
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -342,22 +378,6 @@ cbe::Texture2D *EditorHelpers::createTexture2D(
|
|||||||
return texture2d;
|
return texture2d;
|
||||||
}
|
}
|
||||||
|
|
||||||
cbe::MaterialInstance *EditorHelpers::createMaterialInstance(const String &packageRelPath, const String &packagePath, StringView name)
|
|
||||||
{
|
|
||||||
cbe::Package *package = cbe::Package::createPackage(packageRelPath, packagePath, false);
|
|
||||||
if (package == nullptr)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
cbe::markPackageDirty(package);
|
|
||||||
cbe::INTERNAL_ObjectCoreAccessors::addFlags(package, cbe::ObjFlag_PackageLoaded);
|
|
||||||
|
|
||||||
cbe::MaterialInstance *matInst = cbe::create<cbe::MaterialInstance>(name, package, cbe::ObjFlag_PackageLoaded);
|
|
||||||
debugAssert(matInst);
|
|
||||||
|
|
||||||
return matInst;
|
|
||||||
}
|
|
||||||
|
|
||||||
cbe::StaticMesh *
|
cbe::StaticMesh *
|
||||||
EditorHelpers::createStaticMesh(const String &packageRelPath, const String &packagePath, StringView name, cbe::SMCreateInfo &&createInfo)
|
EditorHelpers::createStaticMesh(const String &packageRelPath, const String &packagePath, StringView name, cbe::SMCreateInfo &&createInfo)
|
||||||
{
|
{
|
||||||
@@ -385,12 +405,12 @@ cbe::Actor *EditorHelpers::addStaticMeshesToWorld(
|
|||||||
|
|
||||||
if (rootActorName.empty())
|
if (rootActorName.empty())
|
||||||
{
|
{
|
||||||
LOG_WARN(LOG_CATEGORY, "Root actor name must be valid! Cannot add static meshes to the world");
|
CBE_LOG_WARN(LOG_CATEGORY, "Root actor name must be valid! Cannot add static meshes to the world");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (staticMeshes.empty() || world == nullptr)
|
if (staticMeshes.empty() || world == nullptr)
|
||||||
{
|
{
|
||||||
LOG_WARN(LOG_CATEGORY, "World or staticMeshes are invalid! Cannot add static meshes to the world");
|
CBE_LOG_WARN(LOG_CATEGORY, "World or staticMeshes are invalid! Cannot add static meshes to the world");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,13 +445,14 @@ cbe::Actor *EditorHelpers::addStaticMeshToWorld(cbe::StaticMesh *sm, const Trans
|
|||||||
cbe::Object *modifyingComp
|
cbe::Object *modifyingComp
|
||||||
= modifyPrefabCompField(PropertyHelper::findField(smComp->getType(), GET_MEMBER_ID_CHECKED(cbe::StaticMeshComponent, mesh)), smComp);
|
= modifyPrefabCompField(PropertyHelper::findField(smComp->getType(), GET_MEMBER_ID_CHECKED(cbe::StaticMeshComponent, mesh)), smComp);
|
||||||
debugAssert(modifyingComp == smComp);
|
debugAssert(modifyingComp == smComp);
|
||||||
|
/* Setting mesh also modifies batchMaterials */
|
||||||
modifyingComp = modifyPrefabCompField(
|
modifyingComp = modifyPrefabCompField(
|
||||||
PropertyHelper::findField(smComp->getType(), GET_MEMBER_ID_CHECKED(cbe::StaticMeshComponent, batchMaterials)), smComp
|
PropertyHelper::findField(smComp->getType(), GET_MEMBER_ID_CHECKED(cbe::StaticMeshComponent, batchMaterials)), smComp
|
||||||
);
|
);
|
||||||
debugAssert(modifyingComp == smComp);
|
debugAssert(modifyingComp == smComp);
|
||||||
|
|
||||||
smComp->setMesh(sm);
|
smComp->setMesh(sm);
|
||||||
/* Attach static mesh to root even though in Prefab, added component will get attached to root by default */
|
/* Attach static mesh to root even though in Prefab, added component will get attached to root by default. */
|
||||||
cbe::WACHelpers::attachComponent(smComp, smActorPrefab->getRootComponent());
|
cbe::WACHelpers::attachComponent(smComp, smActorPrefab->getRootComponent());
|
||||||
|
|
||||||
modifyingComp = modifyPrefabCompField(
|
modifyingComp = modifyPrefabCompField(
|
||||||
@@ -502,8 +523,12 @@ cbe::Actor *EditorHelpers::addActorToWorld(cbe::World *world, cbe::ActorPrefab *
|
|||||||
cbe::markPackageDirty(world);
|
cbe::markPackageDirty(world);
|
||||||
return prefab->getActorTemplate();
|
return prefab->getActorTemplate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorHelpers::removeActorFromWorld(cbe::World *world, cbe::Actor *actor) { postRemoveActorFromWorld(world, actor); }
|
void EditorHelpers::removeActorFromWorld(cbe::World *world, cbe::Actor *actor) { postRemoveActorFromWorld(world, actor); }
|
||||||
|
void EditorHelpers::attachActor(cbe::World *world, cbe::Actor *actor, cbe::TransformComponent *attachTo)
|
||||||
|
{
|
||||||
|
debugAssert(world != nullptr && actor->getWorld() == world);
|
||||||
|
cbe::WACHelpers::attachActor(actor, attachTo);
|
||||||
|
}
|
||||||
|
|
||||||
cbe::Object *EditorHelpers::addComponentToPrefab(cbe::ActorPrefab *prefab, CBEClass compClass, const String &compName)
|
cbe::Object *EditorHelpers::addComponentToPrefab(cbe::ActorPrefab *prefab, CBEClass compClass, const String &compName)
|
||||||
{
|
{
|
||||||
@@ -550,6 +575,8 @@ void EditorHelpers::removeComponentFromPrefab(cbe::ActorPrefab *prefab, cbe::Obj
|
|||||||
cbe::markPackageDirty(prefab);
|
cbe::markPackageDirty(prefab);
|
||||||
}
|
}
|
||||||
prefab->removeComponent(comp);
|
prefab->removeComponent(comp);
|
||||||
|
|
||||||
|
// TODO(Jeslas) : Patch up the world transform hierarchy based on prefab's tf hierarchy
|
||||||
}
|
}
|
||||||
|
|
||||||
cbe::Object *EditorHelpers::modifyComponentInPrefab(cbe::ActorPrefab *prefab, cbe::Object *modifyingComp)
|
cbe::Object *EditorHelpers::modifyComponentInPrefab(cbe::ActorPrefab *prefab, cbe::Object *modifyingComp)
|
||||||
@@ -558,6 +585,45 @@ cbe::Object *EditorHelpers::modifyComponentInPrefab(cbe::ActorPrefab *prefab, cb
|
|||||||
return modifiedComp;
|
return modifiedComp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditorHelpers::attachComponent(
|
||||||
|
std::variant<cbe::TransformComponent *, cbe::TransformLeafComponent *> thisComp, cbe::TransformComponent *attachToComp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
/* If not transform component it must be a leaf */
|
||||||
|
if (std::holds_alternative<cbe::TransformComponent *>(thisComp))
|
||||||
|
{
|
||||||
|
cbe::TransformComponent *tfComp = std::get<cbe::TransformComponent *>(thisComp);
|
||||||
|
|
||||||
|
cbe::ActorPrefab *prefab = cbe::ActorPrefab::prefabFromComponent(tfComp);
|
||||||
|
debugAssert(prefab != nullptr);
|
||||||
|
|
||||||
|
prefab->setComponentAttachedTo(tfComp, attachToComp);
|
||||||
|
|
||||||
|
/* If world is prepared already then the attachment changes must be propagated there */
|
||||||
|
if (cbe::World *world = prefab->getActorTemplate()->getWorld();
|
||||||
|
world != nullptr && cbe::EWorldState::isPreparedState(world->getState()))
|
||||||
|
{
|
||||||
|
cbe::WACHelpers::attachComponent(tfComp, attachToComp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cbe::TransformLeafComponent *leafComp = std::get<cbe::TransformLeafComponent *>(thisComp);
|
||||||
|
|
||||||
|
cbe::ActorPrefab *prefab = cbe::ActorPrefab::prefabFromComponent(leafComp);
|
||||||
|
debugAssert(prefab != nullptr);
|
||||||
|
|
||||||
|
prefab->setLeafAttachedTo(leafComp, attachToComp);
|
||||||
|
|
||||||
|
/* If world is prepared already then the attachment changes must be propagated there */
|
||||||
|
if (cbe::World *world = prefab->getActorTemplate()->getWorld();
|
||||||
|
world != nullptr && cbe::EWorldState::isPreparedState(world->getState()))
|
||||||
|
{
|
||||||
|
cbe::WACHelpers::attachComponent(leafComp, attachToComp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cbe::Object *EditorHelpers::modifyPrefabCompField(const FieldProperty *prop, cbe::Object *comp)
|
cbe::Object *EditorHelpers::modifyPrefabCompField(const FieldProperty *prop, cbe::Object *comp)
|
||||||
{
|
{
|
||||||
debugAssert(comp && prop);
|
debugAssert(comp && prop);
|
||||||
@@ -640,6 +706,11 @@ std::vector<cbe::Object *> EditorHelpers::getActorCompSubObjects(cbe::Actor *act
|
|||||||
|
|
||||||
for (cbe::Object *child : children)
|
for (cbe::Object *child : children)
|
||||||
{
|
{
|
||||||
|
if (!cbe::isValidFast(child))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (child->getType() != cbe::ObjectTemplate::staticType())
|
if (child->getType() != cbe::ObjectTemplate::staticType())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -835,10 +906,11 @@ void EditorHelpers::postRemoveActorFromWorld(cbe::World *world, cbe::Actor *acto
|
|||||||
|
|
||||||
void EditorHelpers::componentAddedToWorld(cbe::World *world, cbe::Actor *actor, cbe::Object *component, bool bAddToActorPending)
|
void EditorHelpers::componentAddedToWorld(cbe::World *world, cbe::Actor *actor, cbe::Object *component, bool bAddToActorPending)
|
||||||
{
|
{
|
||||||
|
const bool bWorldPrepared = cbe::EWorldState::isPreparedState(world->getState());
|
||||||
/* Must broadcast if world is not prepared or if component is only added to actor prefab in a prepared world.
|
/* Must broadcast if world is not prepared or if component is only added to actor prefab in a prepared world.
|
||||||
* Must add to component list only if dealing with component added to actor prefab and world is already prepared. */
|
* Must add to component list only if dealing with component added to actor prefab and world is already prepared. */
|
||||||
const bool bBroadcast = !cbe::EWorldState::isPreparedState(world->getState()) || bAddToActorPending;
|
const bool bBroadcast = !bWorldPrepared || (bWorldPrepared && bAddToActorPending);
|
||||||
const bool bAddToCompList = cbe::EWorldState::isPreparedState(world->getState()) && bAddToActorPending;
|
const bool bAddToCompList = bWorldPrepared && bAddToActorPending;
|
||||||
if (!bBroadcast)
|
if (!bBroadcast)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -846,27 +918,40 @@ void EditorHelpers::componentAddedToWorld(cbe::World *world, cbe::Actor *actor,
|
|||||||
|
|
||||||
if (PropertyHelper::isChildOf<cbe::TransformComponent>(component->getType()))
|
if (PropertyHelper::isChildOf<cbe::TransformComponent>(component->getType()))
|
||||||
{
|
{
|
||||||
world->broadcastTfCompAdded(component);
|
cbe::TransformComponent *tfComponent = static_cast<cbe::TransformComponent *>(component);
|
||||||
|
/* Only setup transform component's world setup if we need to add to component list.
|
||||||
|
* This is because in other cases component should get setup during actor/world setup. */
|
||||||
if (bAddToCompList)
|
if (bAddToCompList)
|
||||||
{
|
{
|
||||||
actor->transformComps.insert(static_cast<cbe::TransformComponent *>(component));
|
debugAssert(!world->compToTf.contains(tfComponent));
|
||||||
|
|
||||||
|
/* Insert into component list first */
|
||||||
|
world->compToTf[tfComponent] = world->txHierarchy.add(cbe::ComponentWorldTF{ tfComponent, tfComponent->getRelativeTransform() });
|
||||||
|
if (tfComponent != actor->getRootComponent())
|
||||||
|
{
|
||||||
|
world->updateTfAttachment(tfComponent, cbe::ActorPrefab::prefabFromActor(actor)->getAttachedToComp(tfComponent), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actor->transformComps.insert(tfComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
world->broadcastTfCompAdded(tfComponent);
|
||||||
}
|
}
|
||||||
else if (PropertyHelper::isChildOf<cbe::LogicComponent>(component->getType()))
|
else if (PropertyHelper::isChildOf<cbe::LogicComponent>(component->getType()))
|
||||||
{
|
{
|
||||||
world->broadcastLogicCompAdded(component);
|
|
||||||
if (bAddToCompList)
|
if (bAddToCompList)
|
||||||
{
|
{
|
||||||
actor->logicComps.insert(static_cast<cbe::LogicComponent *>(component));
|
actor->logicComps.insert(static_cast<cbe::LogicComponent *>(component));
|
||||||
}
|
}
|
||||||
|
world->broadcastLogicCompAdded(component);
|
||||||
}
|
}
|
||||||
else if (PropertyHelper::isChildOf<cbe::TransformLeafComponent>(component->getType()))
|
else if (PropertyHelper::isChildOf<cbe::TransformLeafComponent>(component->getType()))
|
||||||
{
|
{
|
||||||
world->broadcastLeafCompAdded(component);
|
|
||||||
if (bAddToCompList)
|
if (bAddToCompList)
|
||||||
{
|
{
|
||||||
actor->leafComps.insert(static_cast<cbe::TransformLeafComponent *>(component));
|
actor->leafComps.insert(static_cast<cbe::TransformLeafComponent *>(component));
|
||||||
}
|
}
|
||||||
|
world->broadcastLeafCompAdded(component);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -879,28 +964,45 @@ void EditorHelpers::componentAddedToWorld(cbe::World *world, cbe::Actor *actor,
|
|||||||
|
|
||||||
void EditorHelpers::componentRemovedFromWorld(cbe::World *world, cbe::Actor *actor, cbe::Object *component, bool bRemoveFromActorPending)
|
void EditorHelpers::componentRemovedFromWorld(cbe::World *world, cbe::Actor *actor, cbe::Object *component, bool bRemoveFromActorPending)
|
||||||
{
|
{
|
||||||
|
const bool bWorldPrepared = cbe::EWorldState::isPreparedState(world->getState());
|
||||||
/* Must broadcast if world is not prepared or if actor prefab's component removal is requested in a prepared world.
|
/* Must broadcast if world is not prepared or if actor prefab's component removal is requested in a prepared world.
|
||||||
* Must remove from component list only if dealing with component added to actor prefab and world is already prepared. */
|
* Must remove from component list only if dealing with component added to actor prefab and world is already prepared. */
|
||||||
const bool bBroadcast = cbe::EWorldState::isPreparedState(world->getState()) || bRemoveFromActorPending;
|
const bool bBroadcast = !bWorldPrepared || (bWorldPrepared && bRemoveFromActorPending);
|
||||||
const bool bRemoveFromCompList = cbe::EWorldState::isPreparedState(world->getState()) && bRemoveFromActorPending;
|
/* Not sure if we need to check condition to remove from component list we could just remove always just for removal. */
|
||||||
|
const bool bRemoveFromCompList = bWorldPrepared && bRemoveFromActorPending;
|
||||||
if (!bBroadcast)
|
if (!bBroadcast)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Just to be safe and not have any hanging references */
|
|
||||||
if (cbe::TransformComponent *tfComponent = cbe::cast<cbe::TransformComponent>(component))
|
if (cbe::TransformComponent *tfComponent = cbe::cast<cbe::TransformComponent>(component))
|
||||||
{
|
{
|
||||||
auto compWorldTfItr = world->compToTf.find(tfComponent);
|
if (bWorldPrepared)
|
||||||
cbe::World::TFHierarchyIdx compTfIdx;
|
|
||||||
if (compWorldTfItr != world->compToTf.end())
|
|
||||||
{
|
{
|
||||||
compTfIdx = compWorldTfItr->second;
|
auto compWorldTfItr = world->compToTf.find(tfComponent);
|
||||||
#if DEBUG_VALIDATIONS_ENABLED
|
debugAssert(compWorldTfItr != world->compToTf.end());
|
||||||
std::vector<cbe::World::TFHierarchyIdx> directAttachments;
|
|
||||||
world->txHierarchy.getChildren(directAttachments, compTfIdx, false);
|
cbe::World::TFHierarchyIdx compTfIdx = compWorldTfItr->second;
|
||||||
/* So if ever below assert fails. There is desync in logic on how world components are attached and tree updates */
|
|
||||||
debugAssert(directAttachments.empty());
|
/* Do not try to reattach since root do not have in actor parent to reattach to.
|
||||||
#endif // DEBUG_VALIDATIONS_ENABLED
|
* Also hierarchy tree becomes invalid when parent gets removed thereby invalidating the compTfIdx */
|
||||||
|
if (actor->getRootComponent() != tfComponent && world->txHierarchy.isValid(compTfIdx))
|
||||||
|
{
|
||||||
|
std::vector<cbe::TransformLeafComponent *> leaves;
|
||||||
|
cbe::WACHelpers::getComponentLeafs(tfComponent, leaves);
|
||||||
|
|
||||||
|
cbe::World::TFHierarchyIdx compTfParentIdx = world->txHierarchy.getNode(compTfIdx).parent;
|
||||||
|
for (cbe::TransformLeafComponent *leaf : leaves)
|
||||||
|
{
|
||||||
|
cbe::WACHelpers::attachComponent(leaf, world->txHierarchy[compTfParentIdx].component);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<cbe::World::TFHierarchyIdx> children = world->txHierarchy.getChildren(compTfIdx, false);
|
||||||
|
for (cbe::World::TFHierarchyIdx tfIdx : children)
|
||||||
|
{
|
||||||
|
cbe::WACHelpers::attachComponent(world->txHierarchy[tfIdx].component, world->txHierarchy[compTfParentIdx].component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
world->txHierarchy.remove(compTfIdx);
|
world->txHierarchy.remove(compTfIdx);
|
||||||
world->compToTf.erase(compWorldTfItr);
|
world->compToTf.erase(compWorldTfItr);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -38,6 +38,7 @@ class StaticMesh;
|
|||||||
class World;
|
class World;
|
||||||
class Actor;
|
class Actor;
|
||||||
class TransformComponent;
|
class TransformComponent;
|
||||||
|
class TransformLeafComponent;
|
||||||
class MaterialInstance;
|
class MaterialInstance;
|
||||||
class WorldsManager;
|
class WorldsManager;
|
||||||
|
|
||||||
@@ -67,6 +68,8 @@ public:
|
|||||||
/* Name must be a full path to package */
|
/* Name must be a full path to package */
|
||||||
static void makePackageUnique(String &inOutPackagePath) { makePackageUnique(inOutPackagePath, {}); }
|
static void makePackageUnique(String &inOutPackagePath) { makePackageUnique(inOutPackagePath, {}); }
|
||||||
static void makePackageUnique(String &inOutPackagePath, StringView suffix);
|
static void makePackageUnique(String &inOutPackagePath, StringView suffix);
|
||||||
|
static void makeComponentUnique(cbe::Actor *actor, String &inOutCompName, StringView suffix, const CoreObjectsDB &objDb);
|
||||||
|
|
||||||
/* When package is renames/copied/moved there is chance new package name and object name might not match.
|
/* When package is renames/copied/moved there is chance new package name and object name might not match.
|
||||||
* Calling this will make sure object names matches the new package name.
|
* Calling this will make sure object names matches the new package name.
|
||||||
* Pass bRecurse to true to allow renaming all the objects that matches the package name. */
|
* Pass bRecurse to true to allow renaming all the objects that matches the package name. */
|
||||||
@@ -77,8 +80,9 @@ public:
|
|||||||
static std::pair<String, std::vector<cbe::Object *>> undoTransaction(cbe::TransactionsLedger &ledger);
|
static std::pair<String, std::vector<cbe::Object *>> undoTransaction(cbe::TransactionsLedger &ledger);
|
||||||
static std::pair<String, std::vector<cbe::Object *>> redoTransaction(cbe::TransactionsLedger &ledger);
|
static std::pair<String, std::vector<cbe::Object *>> redoTransaction(cbe::TransactionsLedger &ledger);
|
||||||
|
|
||||||
|
/* Any asset that can be created manually in editor must use this routine to create in code */
|
||||||
static cbe::Object *createAsset(
|
static cbe::Object *createAsset(
|
||||||
cbe::ICoreAssetFactory *assetFactory, CBEClass clazz, StringView packageRelPath, StringView contentDir,
|
ArrayView<cbe::ICoreAssetFactory *> assetFactories, CBEClass clazz, StringView packageRelPath, StringView contentDir,
|
||||||
const SingleCastDelegate<cbe::Object *, const cbe::AssetCreateInfo &> &createCb
|
const SingleCastDelegate<cbe::Object *, const cbe::AssetCreateInfo &> &createCb
|
||||||
);
|
);
|
||||||
static cbe::Object *createEditingAsset(cbe::Object *asset);
|
static cbe::Object *createEditingAsset(cbe::Object *asset);
|
||||||
@@ -91,8 +95,6 @@ public:
|
|||||||
static cbe::Texture2D *
|
static cbe::Texture2D *
|
||||||
createTexture2D(const String &packageRelPath, const String &packagePath, StringView name, cbe::Texture2DCreateInfo &&createInfo);
|
createTexture2D(const String &packageRelPath, const String &packagePath, StringView name, cbe::Texture2DCreateInfo &&createInfo);
|
||||||
|
|
||||||
static cbe::MaterialInstance *createMaterialInstance(const String &packageRelPath, const String &packagePath, StringView name);
|
|
||||||
|
|
||||||
static cbe::StaticMesh *
|
static cbe::StaticMesh *
|
||||||
createStaticMesh(const String &packageName, const String &packagePath, StringView name, cbe::SMCreateInfo &&createInfo);
|
createStaticMesh(const String &packageName, const String &packagePath, StringView name, cbe::SMCreateInfo &&createInfo);
|
||||||
// Returns the root actor to which all this static mesh actors are attached to
|
// Returns the root actor to which all this static mesh actors are attached to
|
||||||
@@ -107,6 +109,7 @@ public:
|
|||||||
static cbe::Actor *addActorToWorld(cbe::World *world, CBEClass actorClass, const String &actorName, EObjectFlags flags);
|
static cbe::Actor *addActorToWorld(cbe::World *world, CBEClass actorClass, const String &actorName, EObjectFlags flags);
|
||||||
static cbe::Actor *addActorToWorld(cbe::World *world, cbe::ActorPrefab *inPrefab, const String &name, EObjectFlags flags);
|
static cbe::Actor *addActorToWorld(cbe::World *world, cbe::ActorPrefab *inPrefab, const String &name, EObjectFlags flags);
|
||||||
static void removeActorFromWorld(cbe::World *world, cbe::Actor *actor);
|
static void removeActorFromWorld(cbe::World *world, cbe::Actor *actor);
|
||||||
|
static void attachActor(cbe::World *world, cbe::Actor *actor, cbe::TransformComponent *attachTo);
|
||||||
|
|
||||||
/* Below two supposed to be used by editor to create actor from the prefab and issues transaction into ledger.
|
/* Below two supposed to be used by editor to create actor from the prefab and issues transaction into ledger.
|
||||||
* Copy from prefab must not be any actual actor in world. It must be a transient actor prefab that is only used to spawn prefab. */
|
* Copy from prefab must not be any actual actor in world. It must be a transient actor prefab that is only used to spawn prefab. */
|
||||||
@@ -118,6 +121,18 @@ public:
|
|||||||
static cbe::Object *addComponentToPrefab(cbe::ActorPrefab *prefab, cbe::ObjectTemplate *compTemplate, const String &compName);
|
static cbe::Object *addComponentToPrefab(cbe::ActorPrefab *prefab, cbe::ObjectTemplate *compTemplate, const String &compName);
|
||||||
static void removeComponentFromPrefab(cbe::ActorPrefab *prefab, cbe::Object *comp);
|
static void removeComponentFromPrefab(cbe::ActorPrefab *prefab, cbe::Object *comp);
|
||||||
static cbe::Object *modifyComponentInPrefab(cbe::ActorPrefab *prefab, cbe::Object *modifyingComp);
|
static cbe::Object *modifyComponentInPrefab(cbe::ActorPrefab *prefab, cbe::Object *modifyingComp);
|
||||||
|
/* Just helper. Does only attachment change and updates necessary in world */
|
||||||
|
static void
|
||||||
|
attachComponent(std::variant<cbe::TransformComponent *, cbe::TransformLeafComponent *> thisComp, cbe::TransformComponent *attachToComp);
|
||||||
|
|
||||||
|
/* Needed helper since world attachment information must be refreshed when reverting or applying.
|
||||||
|
* Expects all the components to be of same/similar types */
|
||||||
|
static void removeComponentFromPrefabs(
|
||||||
|
ArrayView<cbe::ActorPrefab *> prefabs, ArrayView<cbe::Object *> compPerPrefab, bool bLeafComp, bool bLogicComp,
|
||||||
|
cbe::Transaction *transaction
|
||||||
|
);
|
||||||
|
static void
|
||||||
|
setRootComponent(ArrayView<cbe::ActorPrefab *> prefabs, ArrayView<cbe::TransformComponent *> newRootComps, cbe::Transaction *transaction);
|
||||||
|
|
||||||
/* Helpers that composes and adds to modifyComponent(), Must be called before modifying for component. Actors always has overrides. */
|
/* Helpers that composes and adds to modifyComponent(), Must be called before modifying for component. Actors always has overrides. */
|
||||||
static cbe::Object *modifyPrefabCompField(const FieldProperty *prop, cbe::Object *comp);
|
static cbe::Object *modifyPrefabCompField(const FieldProperty *prop, cbe::Object *comp);
|
||||||
@@ -143,6 +158,16 @@ private:
|
|||||||
static void postAddActorToWorld(cbe::World *world, cbe::ActorPrefab *prefab);
|
static void postAddActorToWorld(cbe::World *world, cbe::ActorPrefab *prefab);
|
||||||
static void postRemoveActorFromWorld(cbe::World *world, cbe::Actor *actor);
|
static void postRemoveActorFromWorld(cbe::World *world, cbe::Actor *actor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds component to the world and broadcasts necessary event.
|
||||||
|
* Ensures component is ready for use by various component in world for world's current state.
|
||||||
|
*
|
||||||
|
* @param world
|
||||||
|
* @param actor
|
||||||
|
* @param component
|
||||||
|
* @param bAddToActorPending If component could be added to actor.
|
||||||
|
* Usually need to be false if world actor setup in world is not done already.
|
||||||
|
*/
|
||||||
static void componentAddedToWorld(cbe::World *world, cbe::Actor *actor, cbe::Object *component, bool bAddToActorPending);
|
static void componentAddedToWorld(cbe::World *world, cbe::Actor *actor, cbe::Object *component, bool bAddToActorPending);
|
||||||
static void componentRemovedFromWorld(cbe::World *world, cbe::Actor *actor, cbe::Object *component, bool bRemoveFromActorPending);
|
static void componentRemovedFromWorld(cbe::World *world, cbe::Actor *actor, cbe::Object *component, bool bRemoveFromActorPending);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date December 2025
|
* \date December 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -291,6 +291,8 @@ public:
|
|||||||
|
|
||||||
static String beforeSaveFileName(StringView baseName) { return STR_FORMAT("{}_BeforeSave", baseName) + TCHAR(".") + cbe::PACKAGE_EXT; }
|
static String beforeSaveFileName(StringView baseName) { return STR_FORMAT("{}_BeforeSave", baseName) + TCHAR(".") + cbe::PACKAGE_EXT; }
|
||||||
static String afterSaveFileName(StringView baseName) { return STR_FORMAT("{}_AfterSave", baseName) + TCHAR(".") + cbe::PACKAGE_EXT; }
|
static String afterSaveFileName(StringView baseName) { return STR_FORMAT("{}_AfterSave", baseName) + TCHAR(".") + cbe::PACKAGE_EXT; }
|
||||||
|
static String beforeEditFileName(StringView baseName) { return STR_FORMAT("{}_BeforeEdit", baseName) + TCHAR(".") + cbe::PACKAGE_EXT; }
|
||||||
|
static String afterEditFileName(StringView baseName) { return STR_FORMAT("{}_AfterEdit", baseName) + TCHAR(".") + cbe::PACKAGE_EXT; }
|
||||||
|
|
||||||
GenericSaveAssetAction::~GenericSaveAssetAction()
|
GenericSaveAssetAction::~GenericSaveAssetAction()
|
||||||
{
|
{
|
||||||
@@ -351,6 +353,510 @@ void GenericSaveAssetAction::endTransaction()
|
|||||||
debugAssert(bWritten);
|
debugAssert(bWritten);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// ActorPrefabBackupAction implementations
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static cbe::ActorPrefab *createActorPrefab(cbe::ActorPrefab *copyFromPrefab, cbe::Object *outer, StringView prefabName)
|
||||||
|
{
|
||||||
|
const cbe::ObjectPrivateDataView objDatV = copyFromPrefab->getObjectData();
|
||||||
|
cbe::ActorPrefab *prefab = nullptr;
|
||||||
|
if (cbe::ActorPrefab *parentPrefab = copyFromPrefab->getParentPrefab())
|
||||||
|
{
|
||||||
|
prefab = cbe::create<cbe::ActorPrefab, cbe::ActorPrefab *, String>(
|
||||||
|
prefabName, outer, objDatV.flags, parentPrefab, copyFromPrefab->getActorTemplate()->getObjectData().name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prefab = cbe::create<cbe::ActorPrefab, StringID, String>(
|
||||||
|
prefabName, outer, objDatV.flags, copyFromPrefab->getActorClass()->name, copyFromPrefab->getActorTemplate()->getObjectData().name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
prefab->copyFrom(copyFromPrefab);
|
||||||
|
return prefab;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serializePrefabsToFile(ArrayView<cbe::ActorPrefab *> prefabs, bool bPostEdit, const String &backupDir)
|
||||||
|
{
|
||||||
|
debugAssert(!backupDir.empty());
|
||||||
|
|
||||||
|
for (cbe::ActorPrefab *prefab : prefabs)
|
||||||
|
{
|
||||||
|
const String prefabName = prefab->getObjectData().name;
|
||||||
|
cbe::Package *prefabPkg = cast<Package>(prefab->getOuterMost());
|
||||||
|
debugAssert(prefabPkg != nullptr);
|
||||||
|
|
||||||
|
const String prefabBackupDir = PathFunctions::combinePath(backupDir, prefabPkg->getPackagePath());
|
||||||
|
if (!FileSystemFunctions::dirExists(prefabBackupDir.getChar()))
|
||||||
|
{
|
||||||
|
FileHelper::makeDir(prefabBackupDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayArchiveStream archiveStream;
|
||||||
|
|
||||||
|
/* If prefab is part of a world we have to create dummy prefab to serialize as a package. */
|
||||||
|
if (prefab->getActorTemplate()->getWorld())
|
||||||
|
{
|
||||||
|
cbe::Package *dummyPkg = cbe::Package::createTransientPackage(
|
||||||
|
PathFunctions::combinePathWithSep(ObjectPathHelper::OBJECT_OBJECT_SEPARATOR, prefabPkg->getObjectData().path, prefabName)
|
||||||
|
);
|
||||||
|
cbe::ActorPrefab *serializePrefab = createActorPrefab(prefab, dummyPkg, prefabName);
|
||||||
|
debugAssert(serializePrefab != nullptr);
|
||||||
|
|
||||||
|
/* Serialize the dummy package */
|
||||||
|
const EPackageLoadSaveResult result = PackageSaver::saveFromPackage(dummyPkg, archiveStream);
|
||||||
|
debugAssert(result == EPackageLoadSaveResult::Success);
|
||||||
|
|
||||||
|
/* No need for the dummy package and prefab anymore */
|
||||||
|
dummyPkg->beginDestroy();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const EPackageLoadSaveResult result = PackageSaver::saveFromPackage(prefabPkg, archiveStream);
|
||||||
|
debugAssert(result == EPackageLoadSaveResult::Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool bWritten = FileHelper::writeBytes(
|
||||||
|
archiveStream.getBuffer(),
|
||||||
|
PathFunctions::combinePath(prefabBackupDir, bPostEdit ? afterEditFileName(prefabName) : beforeEditFileName(prefabName))
|
||||||
|
);
|
||||||
|
debugAssert(bWritten);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ActorPrefabsBackupAction::ActorPrefabsBackupAction()
|
||||||
|
: objsDb(&ICoreObjectsModule::get()->objectsDB())
|
||||||
|
{}
|
||||||
|
|
||||||
|
ActorPrefabsBackupAction::~ActorPrefabsBackupAction()
|
||||||
|
{
|
||||||
|
for (const ObjectPath &actorPrefabPath : actorPrefabPaths)
|
||||||
|
{
|
||||||
|
if (!actorPrefabPath.getObjectName().empty() && FileSystemFunctions::dirExists(backupDir.c_str()))
|
||||||
|
{
|
||||||
|
FileHelper::deleteFile(PathFunctions::combinePath(backupDir, beforeEditFileName(actorPrefabPath.getObjectName())));
|
||||||
|
FileHelper::deleteFile(PathFunctions::combinePath(backupDir, afterEditFileName(actorPrefabPath.getObjectName())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorPrefabsBackupAction::setEditingPrefabs(ArrayView<cbe::ActorPrefab *> prefabs)
|
||||||
|
{
|
||||||
|
actorPrefabPaths.clear();
|
||||||
|
actorPrefabPaths.reserve(prefabs.size());
|
||||||
|
for (cbe::ActorPrefab *prefab : prefabs)
|
||||||
|
{
|
||||||
|
actorPrefabPaths.emplace_back(prefab);
|
||||||
|
}
|
||||||
|
serializePrefabsToFile(prefabs, false, backupDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorPrefabsBackupAction::collectReferences(std::vector<Object *> &outObjects) const { cbe::ignoreUnused(outObjects); }
|
||||||
|
|
||||||
|
void ActorPrefabsBackupAction::apply(TransactedEntriesCollector &outEntriesCollector)
|
||||||
|
{
|
||||||
|
for (const ObjectPath &actorPrefabPath : actorPrefabPaths)
|
||||||
|
{
|
||||||
|
debugAssert(actorPrefabPath.isValid());
|
||||||
|
|
||||||
|
cbe::ActorPrefab *copyToPrefab = actorPrefabPath.getObject<cbe::ActorPrefab>();
|
||||||
|
const String prefabName = copyToPrefab->getObjectData().name;
|
||||||
|
cbe::Package *prefabPkg = cast<Package>(copyToPrefab->getOuterMost());
|
||||||
|
debugAssert(prefabPkg != nullptr);
|
||||||
|
|
||||||
|
const String loadFromPath
|
||||||
|
= PathFunctions::combinePath(backupDir, prefabPkg->getPackagePath(), afterEditFileName(actorPrefabPath.getObjectName()));
|
||||||
|
|
||||||
|
/* If prefab is part of a world we have to create dummy prefab to serialize as a package and then copy it to original prefab. */
|
||||||
|
if (copyToPrefab->getActorTemplate()->getWorld())
|
||||||
|
{
|
||||||
|
cbe::Package *dummyPkg = cbe::Package::createTransientPackage(PathFunctions::combinePathWithSep(
|
||||||
|
ObjectPathHelper::OBJECT_OBJECT_SEPARATOR, prefabPkg->getObjectData().path, actorPrefabPath.getObjectName()
|
||||||
|
));
|
||||||
|
/* We have to create dummy from current prefab in order to retain transient fields like fields that gets init at world prepare.
|
||||||
|
* Any field that got modified as part of template will still get overwritten.
|
||||||
|
* Ensure no such a field gets transiently written to. */
|
||||||
|
ActorPrefab *dummyPrefab = createActorPrefab(copyToPrefab, dummyPkg, prefabName);
|
||||||
|
|
||||||
|
/* Serialize the dummy package */
|
||||||
|
const EPackageLoadSaveResult result = PackageLoader::loadIntoPackage(loadFromPath, dummyPkg);
|
||||||
|
debugAssert(result == EPackageLoadSaveResult::Success);
|
||||||
|
|
||||||
|
std::vector<cbe::Object *> pkgChildren;
|
||||||
|
objsDb->getChildren(pkgChildren, dummyPkg->getDbIdx());
|
||||||
|
debugAssert(pkgChildren.size() == 1);
|
||||||
|
debugAssert(dummyPrefab != nullptr && pkgChildren[0] == dummyPrefab);
|
||||||
|
|
||||||
|
copyToPrefab->copyFrom(dummyPrefab);
|
||||||
|
|
||||||
|
/* No need for the dummy package and prefab anymore */
|
||||||
|
dummyPkg->beginDestroy();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const EPackageLoadSaveResult result = PackageLoader::loadIntoPackage(loadFromPath, prefabPkg);
|
||||||
|
debugAssert(result == EPackageLoadSaveResult::Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
outEntriesCollector.addTransactedObj(copyToPrefab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ActorPrefabsBackupAction::revert(TransactedEntriesCollector &outEntriesCollector)
|
||||||
|
{
|
||||||
|
for (const ObjectPath &actorPrefabPath : actorPrefabPaths)
|
||||||
|
{
|
||||||
|
debugAssert(actorPrefabPath.isValid());
|
||||||
|
|
||||||
|
cbe::ActorPrefab *copyToPrefab = actorPrefabPath.getObject<cbe::ActorPrefab>();
|
||||||
|
const String prefabName = copyToPrefab->getObjectData().name;
|
||||||
|
cbe::Package *prefabPkg = cast<Package>(copyToPrefab->getOuterMost());
|
||||||
|
debugAssert(prefabPkg != nullptr);
|
||||||
|
|
||||||
|
const String loadFromPath
|
||||||
|
= PathFunctions::combinePath(backupDir, prefabPkg->getPackagePath(), beforeEditFileName(actorPrefabPath.getObjectName()));
|
||||||
|
|
||||||
|
/* If prefab is part of a world we have to create dummy prefab to serialize as a package and then copy it to original prefab. */
|
||||||
|
if (copyToPrefab->getActorTemplate()->getWorld())
|
||||||
|
{
|
||||||
|
cbe::Package *dummyPkg = cbe::Package::createTransientPackage(PathFunctions::combinePathWithSep(
|
||||||
|
ObjectPathHelper::OBJECT_OBJECT_SEPARATOR, prefabPkg->getObjectData().path, actorPrefabPath.getObjectName()
|
||||||
|
));
|
||||||
|
/* We have to create dummy from current prefab in order to retain transient fields like fields that gets init at world prepare.
|
||||||
|
* Any field that got modified as part of template will still get overwritten.
|
||||||
|
* Ensure no such a field gets transiently written to. */
|
||||||
|
ActorPrefab *dummyPrefab = createActorPrefab(copyToPrefab, dummyPkg, prefabName);
|
||||||
|
|
||||||
|
/* Serialize the dummy package */
|
||||||
|
const EPackageLoadSaveResult result = PackageLoader::loadIntoPackage(loadFromPath, dummyPkg);
|
||||||
|
debugAssert(result == EPackageLoadSaveResult::Success);
|
||||||
|
|
||||||
|
std::vector<cbe::Object *> pkgChildren;
|
||||||
|
objsDb->getChildren(pkgChildren, dummyPkg->getDbIdx());
|
||||||
|
debugAssert(pkgChildren.size() == 1);
|
||||||
|
debugAssert(dummyPrefab != nullptr && pkgChildren[0] == dummyPrefab);
|
||||||
|
|
||||||
|
copyToPrefab->copyFrom(dummyPrefab);
|
||||||
|
|
||||||
|
/* No need for the dummy package and prefab anymore */
|
||||||
|
dummyPkg->beginDestroy();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const EPackageLoadSaveResult result = PackageLoader::loadIntoPackage(loadFromPath, prefabPkg);
|
||||||
|
debugAssert(result == EPackageLoadSaveResult::Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
outEntriesCollector.addTransactedObj(copyToPrefab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ActorPrefabsBackupAction::isValid() const
|
||||||
|
{
|
||||||
|
if (objsDb == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ObjectPath &actorPrefabPath : actorPrefabPaths)
|
||||||
|
{
|
||||||
|
if (!actorPrefabPath.isValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorPrefabsBackupAction::endTransaction()
|
||||||
|
{
|
||||||
|
std::vector<cbe::ActorPrefab *> prefabs;
|
||||||
|
prefabs.reserve(actorPrefabPaths.size());
|
||||||
|
for (const ObjectPath &actorPrefabPath : actorPrefabPaths)
|
||||||
|
{
|
||||||
|
debugAssert(actorPrefabPath.isValid());
|
||||||
|
|
||||||
|
prefabs.emplace_back(actorPrefabPath.getObject<cbe::ActorPrefab>());
|
||||||
|
}
|
||||||
|
|
||||||
|
serializePrefabsToFile(prefabs, true, backupDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Remove Components from selected prefabs actions
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class RemoveCompsFromActorPrefabsAction : public TransactionCustomAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RemoveCompsFromActorPrefabsAction() = default;
|
||||||
|
RemoveCompsFromActorPrefabsAction(RemoveCompsFromActorPrefabsAction &&other)
|
||||||
|
: world(std::move(other.world))
|
||||||
|
, prefabs(std::move(other.prefabs))
|
||||||
|
{}
|
||||||
|
~RemoveCompsFromActorPrefabsAction() = default;
|
||||||
|
|
||||||
|
/* TransactionCustomAction overrides */
|
||||||
|
void collectReferences(std::vector<Object *> &outObjects) const final;
|
||||||
|
void apply(TransactedEntriesCollector &outEntriesCollector) final;
|
||||||
|
void revert(TransactedEntriesCollector &outEntriesCollector) final;
|
||||||
|
bool isValid() const final;
|
||||||
|
/* Overrides ends */
|
||||||
|
|
||||||
|
public:
|
||||||
|
WeakObjPtr<World> world;
|
||||||
|
|
||||||
|
struct PerPrefabData
|
||||||
|
{
|
||||||
|
ObjectPath prefab;
|
||||||
|
ObjectPath compRemoved;
|
||||||
|
|
||||||
|
/* Will be valid only if the another component actually was attached to removed component. */
|
||||||
|
std::vector<ObjectPath> attachedActors;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<PerPrefabData> prefabs;
|
||||||
|
};
|
||||||
|
|
||||||
|
void RemoveCompsFromActorPrefabsAction::collectReferences(std::vector<Object *> &) const {}
|
||||||
|
void RemoveCompsFromActorPrefabsAction::apply(TransactedEntriesCollector &outEntriesCollector)
|
||||||
|
{
|
||||||
|
if (world.isSet())
|
||||||
|
{
|
||||||
|
outEntriesCollector.addTransactedObj(world.get());
|
||||||
|
}
|
||||||
|
for (const PerPrefabData &prefabData : prefabs)
|
||||||
|
{
|
||||||
|
ActorPrefab *prefab = prefabData.prefab.getObject<ActorPrefab>();
|
||||||
|
|
||||||
|
EditorHelpers::removeComponentFromPrefab(prefab, prefabData.compRemoved.getObject());
|
||||||
|
outEntriesCollector.addTransactedObj(prefab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void RemoveCompsFromActorPrefabsAction::revert(TransactedEntriesCollector &outEntriesCollector)
|
||||||
|
{
|
||||||
|
/* Add back must be handled separately */
|
||||||
|
if (!world.isSet())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do not need to broadcast if the world is not prepared */
|
||||||
|
if (!EWorldState::isPreparedState(world->getState()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
outEntriesCollector.addTransactedObj(world.get());
|
||||||
|
for (const PerPrefabData &prefabData : prefabs)
|
||||||
|
{
|
||||||
|
Object *compRemoved = prefabData.compRemoved.getObject();
|
||||||
|
if (compRemoved->getType() != cbe::TransformComponent::staticType())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransformComponent *tfComp = static_cast<TransformComponent *>(compRemoved);
|
||||||
|
|
||||||
|
ActorPrefab *prefab = prefabData.prefab.getObject<ActorPrefab>();
|
||||||
|
debugAssert(prefab);
|
||||||
|
|
||||||
|
/* Force update the attached components. To update the tf tree and other systems */
|
||||||
|
std::vector<TransformComponent *> tfComps;
|
||||||
|
std::vector<TransformLeafComponent *> leafComps;
|
||||||
|
prefab->getCompAttaches(tfComp, tfComps);
|
||||||
|
prefab->getCompAttaches(tfComp, leafComps);
|
||||||
|
for (TransformComponent *attachedComp : tfComps)
|
||||||
|
{
|
||||||
|
EditorHelpers::attachComponent(attachedComp, tfComp);
|
||||||
|
}
|
||||||
|
for (TransformLeafComponent *leafComp : leafComps)
|
||||||
|
{
|
||||||
|
EditorHelpers::attachComponent(leafComp, tfComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force refresh attachment change update which in turn push transformed updates to world renderer and others. */
|
||||||
|
TransformComponent *attachedToComp = prefab->getAttachedToComp(tfComp);
|
||||||
|
if (attachedToComp != nullptr)
|
||||||
|
{
|
||||||
|
EditorHelpers::attachComponent(tfComp, attachedToComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
outEntriesCollector.addTransactedObj(prefab);
|
||||||
|
|
||||||
|
for (const ObjectPath &actorPath : prefabData.attachedActors)
|
||||||
|
{
|
||||||
|
if (Actor *attachedActor = actorPath.getObject<Actor>())
|
||||||
|
{
|
||||||
|
EditorHelpers::attachActor(world.get(), attachedActor, tfComp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool RemoveCompsFromActorPrefabsAction::isValid() const { return !world.isSet() || world.isValid(); }
|
||||||
|
|
||||||
|
class RemoveCompsAddBackCompAction : public TransactionCustomAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RemoveCompsAddBackCompAction() = default;
|
||||||
|
RemoveCompsAddBackCompAction(RemoveCompsAddBackCompAction &&other)
|
||||||
|
: prefabs(std::move(other.prefabs))
|
||||||
|
{}
|
||||||
|
~RemoveCompsAddBackCompAction() = default;
|
||||||
|
|
||||||
|
/* TransactionCustomAction overrides */
|
||||||
|
void collectReferences(std::vector<Object *> &outObjects) const final;
|
||||||
|
void apply(TransactedEntriesCollector &outEntriesCollector) final;
|
||||||
|
void revert(TransactedEntriesCollector &outEntriesCollector) final;
|
||||||
|
bool isValid() const final { return true; }
|
||||||
|
/* Overrides ends */
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct PerPrefabData
|
||||||
|
{
|
||||||
|
ObjectPath prefab;
|
||||||
|
String compName;
|
||||||
|
|
||||||
|
std::variant<CBEClass, ObjectPath> compTemplate;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<PerPrefabData> prefabs;
|
||||||
|
};
|
||||||
|
void RemoveCompsAddBackCompAction::collectReferences(std::vector<Object *> &) const {}
|
||||||
|
void RemoveCompsAddBackCompAction::apply(TransactedEntriesCollector &) {}
|
||||||
|
void RemoveCompsAddBackCompAction::revert(TransactedEntriesCollector &outEntriesCollector)
|
||||||
|
{
|
||||||
|
for (const PerPrefabData &prefabData : prefabs)
|
||||||
|
{
|
||||||
|
ActorPrefab *prefab = prefabData.prefab.getObject<ActorPrefab>();
|
||||||
|
|
||||||
|
if (std::holds_alternative<CBEClass>(prefabData.compTemplate))
|
||||||
|
{
|
||||||
|
EditorHelpers::addComponentToPrefab(prefab, std::get<CBEClass>(prefabData.compTemplate), prefabData.compName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorHelpers::addComponentToPrefab(
|
||||||
|
prefab, std::get<ObjectPath>(prefabData.compTemplate).getObject<ObjectTemplate>(), prefabData.compName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
outEntriesCollector.addTransactedObj(prefab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Root component when changed must be propagated to world as well.
|
||||||
|
* This works in conjunction with ActorPrefabsBackupAction to restore attachment information in world.
|
||||||
|
*/
|
||||||
|
class RootCompsReattachAction : public TransactionCustomAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RootCompsReattachAction() = default;
|
||||||
|
RootCompsReattachAction(RootCompsReattachAction &&other)
|
||||||
|
: prefabs(std::move(other.prefabs))
|
||||||
|
, worldPtr(std::move(other.worldPtr))
|
||||||
|
{}
|
||||||
|
~RootCompsReattachAction() = default;
|
||||||
|
|
||||||
|
/* TransactionCustomAction overrides */
|
||||||
|
void collectReferences(std::vector<Object *> &outObjects) const final;
|
||||||
|
void apply(TransactedEntriesCollector &outEntriesCollector) final;
|
||||||
|
void revert(TransactedEntriesCollector &outEntriesCollector) final;
|
||||||
|
bool isValid() const final { return worldPtr.isValid(); }
|
||||||
|
/* Overrides ends */
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct PerPrefabData
|
||||||
|
{
|
||||||
|
ObjectPath prefab;
|
||||||
|
|
||||||
|
ObjectPath oldRoot;
|
||||||
|
ObjectPath newRoot;
|
||||||
|
|
||||||
|
/* Transform component old root was attached to. To setup world actor attachment. */
|
||||||
|
ObjectPath oldRootAttachedTo;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<PerPrefabData> prefabs;
|
||||||
|
WeakObjPtr<World> worldPtr;
|
||||||
|
|
||||||
|
/* If want to do the appropriate action at apply */
|
||||||
|
bool bAtApply:1 = true;
|
||||||
|
/* If want to do the appropriate action at revert */
|
||||||
|
bool bAtRevert:1 = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
void RootCompsReattachAction::collectReferences(std::vector<Object *> &) const {}
|
||||||
|
|
||||||
|
void RootCompsReattachAction::apply(TransactedEntriesCollector &outEntriesCollector)
|
||||||
|
{
|
||||||
|
if (!bAtApply)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
World *world = worldPtr.get();
|
||||||
|
for (const PerPrefabData &prefabData : prefabs)
|
||||||
|
{
|
||||||
|
ActorPrefab *prefab = prefabData.prefab.getObject<ActorPrefab>();
|
||||||
|
debugAssert(prefab != nullptr);
|
||||||
|
|
||||||
|
TransformComponent *oldRoot = prefabData.oldRoot.getObject<TransformComponent>();
|
||||||
|
TransformComponent *newRoot = prefabData.newRoot.getObject<TransformComponent>();
|
||||||
|
TransformComponent *oldRootAttachedTo = prefabData.oldRootAttachedTo.getObject<TransformComponent>();
|
||||||
|
debugAssert(oldRoot != nullptr && newRoot != nullptr);
|
||||||
|
|
||||||
|
/* Just set the root component to world's actor root component. That means newRoot must have been setup already. */
|
||||||
|
debugAssert(prefab->getRootComponent() == newRoot);
|
||||||
|
prefab->setRootComponent(newRoot, true);
|
||||||
|
|
||||||
|
/* Attach the old root to new root. */
|
||||||
|
debugAssert(prefab->getAttachedToComp(oldRoot) == newRoot);
|
||||||
|
/* First detach the newRoot from oldRoot or its child to avoid Cyclic Graph */
|
||||||
|
world->tfAttachmentChanged(newRoot, nullptr);
|
||||||
|
EditorHelpers::attachComponent(oldRoot, newRoot);
|
||||||
|
/* Attach the actor to previously attached actor */
|
||||||
|
EditorHelpers::attachActor(world, prefab->getActorTemplate(), oldRootAttachedTo);
|
||||||
|
|
||||||
|
outEntriesCollector.addTransactedObj(prefab);
|
||||||
|
}
|
||||||
|
outEntriesCollector.addTransactedObj(world);
|
||||||
|
}
|
||||||
|
void RootCompsReattachAction::revert(TransactedEntriesCollector &outEntriesCollector)
|
||||||
|
{
|
||||||
|
if (!bAtRevert)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
World *world = worldPtr.get();
|
||||||
|
for (const PerPrefabData &prefabData : prefabs)
|
||||||
|
{
|
||||||
|
ActorPrefab *prefab = prefabData.prefab.getObject<ActorPrefab>();
|
||||||
|
debugAssert(prefab != nullptr);
|
||||||
|
|
||||||
|
TransformComponent *oldRoot = prefabData.oldRoot.getObject<TransformComponent>();
|
||||||
|
TransformComponent *newRoot = prefabData.newRoot.getObject<TransformComponent>();
|
||||||
|
TransformComponent *oldRootAttachedTo = prefabData.oldRootAttachedTo.getObject<TransformComponent>();
|
||||||
|
debugAssert(oldRoot != nullptr && newRoot != nullptr);
|
||||||
|
|
||||||
|
/* Just set the root component to world's actor root component. That means oldRoot must have been setup already. */
|
||||||
|
debugAssert(prefab->getRootComponent() == oldRoot);
|
||||||
|
prefab->setRootComponent(oldRoot, true);
|
||||||
|
|
||||||
|
/* First detach the oldRoot from oldRoot newRoot to avoid Cyclic Graph */
|
||||||
|
world->tfAttachmentChanged(oldRoot, nullptr);
|
||||||
|
/* In world attach the new root to its attached to component. */
|
||||||
|
EditorHelpers::attachComponent(newRoot, prefab->getAttachedToComp(newRoot));
|
||||||
|
/* Attach the actor to previously attached actor */
|
||||||
|
EditorHelpers::attachActor(world, prefab->getActorTemplate(), oldRootAttachedTo);
|
||||||
|
|
||||||
|
outEntriesCollector.addTransactedObj(prefab);
|
||||||
|
}
|
||||||
|
outEntriesCollector.addTransactedObj(world);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace cbe
|
} // namespace cbe
|
||||||
|
|
||||||
cbe::ActorPrefab *
|
cbe::ActorPrefab *
|
||||||
@@ -497,3 +1003,200 @@ void EditorHelpers::removeActorsFromWorld(cbe::World *world, ArrayView<cbe::Acto
|
|||||||
|
|
||||||
ledger.endTransaction(transaction);
|
ledger.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditorHelpers::removeComponentFromPrefabs(
|
||||||
|
ArrayView<cbe::ActorPrefab *> prefabs, ArrayView<cbe::Object *> compPerPrefab, bool bLeafComp, bool bLogicComp,
|
||||||
|
cbe::Transaction *transaction
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (transaction == nullptr)
|
||||||
|
{
|
||||||
|
/* Do regular remove without any transaction */
|
||||||
|
for (uint32 i = 0; i < prefabs.size(); ++i)
|
||||||
|
{
|
||||||
|
removeComponentFromPrefab(prefabs[i], compPerPrefab[i]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cbe::World *world = prefabs.front()->getActorTemplate()->getWorld();
|
||||||
|
#if DEBUG_VALIDATIONS_ENABLED
|
||||||
|
if (world != nullptr)
|
||||||
|
{
|
||||||
|
for (cbe::ActorPrefab *prefab : prefabs)
|
||||||
|
{
|
||||||
|
debugAssert(world == prefab->getActorTemplate()->getWorld());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool bAnyActorDetached = false;
|
||||||
|
cbe::RemoveCompsFromActorPrefabsAction rmCompsAction;
|
||||||
|
cbe::RemoveCompsAddBackCompAction addCompsAction;
|
||||||
|
|
||||||
|
rmCompsAction.world = world;
|
||||||
|
rmCompsAction.prefabs.reserve(prefabs.size());
|
||||||
|
addCompsAction.prefabs.reserve(prefabs.size());
|
||||||
|
for (uint32 i = 0; i < prefabs.size(); ++i)
|
||||||
|
{
|
||||||
|
cbe::ObjectTemplate *compTemplate = cbe::ActorPrefab::objectTemplateFromObj(compPerPrefab[i]);
|
||||||
|
debugAssert(compTemplate != nullptr);
|
||||||
|
cbe::RemoveCompsAddBackCompAction::PerPrefabData addBackData{
|
||||||
|
.prefab = cbe::ObjectPath(prefabs[i]),
|
||||||
|
.compName = compPerPrefab[i]->getObjectData().name,
|
||||||
|
};
|
||||||
|
if (cbe::ObjectTemplate *parentTemplate = compTemplate->getParentTemplate())
|
||||||
|
{
|
||||||
|
addBackData.compTemplate.emplace<cbe::ObjectPath>(parentTemplate);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addBackData.compTemplate.emplace<CBEClass>(compTemplate->getTemplateClass());
|
||||||
|
}
|
||||||
|
addCompsAction.prefabs.emplace_back(std::move(addBackData));
|
||||||
|
|
||||||
|
rmCompsAction.prefabs.emplace_back(cbe::RemoveCompsFromActorPrefabsAction::PerPrefabData{
|
||||||
|
.prefab = cbe::ObjectPath(prefabs[i]),
|
||||||
|
.compRemoved = cbe::ObjectPath(compPerPrefab[i]),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill the actions with attachment information if TransformComponent is removed */
|
||||||
|
if (!bLeafComp && !bLogicComp && world != nullptr)
|
||||||
|
{
|
||||||
|
for (uint32 i = 0; i < prefabs.size(); ++i)
|
||||||
|
{
|
||||||
|
cbe::TransformComponent *tfComp = static_cast<cbe::TransformComponent *>(compPerPrefab[i]);
|
||||||
|
auto tfToHierarchyIdxItr = world->compToTf.find(tfComp);
|
||||||
|
debugAssert(tfToHierarchyIdxItr != world->compToTf.cend());
|
||||||
|
|
||||||
|
for (cbe::World::TFHierarchyIdx hierarchyIdx : world->txHierarchy.getChildren(tfToHierarchyIdxItr->second, false))
|
||||||
|
{
|
||||||
|
cbe::TransformComponent *otherComp = world->txHierarchy[hierarchyIdx].component;
|
||||||
|
|
||||||
|
/* Actor is attached to this component gather it. */
|
||||||
|
if (cbe::ActorPrefab *otherPrefab = cbe::ActorPrefab::prefabFromComponent(otherComp); otherPrefab != prefabs[i])
|
||||||
|
{
|
||||||
|
bAnyActorDetached = true;
|
||||||
|
rmCompsAction.prefabs[i].attachedActors.emplace_back(otherPrefab->getActorTemplate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cbe::RemoveCompsFromActorPrefabsAction *finalRmAction
|
||||||
|
= transaction->addCustomAction<cbe::RemoveCompsFromActorPrefabsAction>(nullptr, std::move(rmCompsAction));
|
||||||
|
|
||||||
|
/* Must be done after the setup component remove and world attachment action */
|
||||||
|
if (!bLeafComp && !bLogicComp && world != nullptr)
|
||||||
|
{
|
||||||
|
transaction->captureObjectBaseline(world);
|
||||||
|
}
|
||||||
|
cbe::ActorPrefabsBackupAction *prefabBackupAction = transaction->addCustomAction<cbe::ActorPrefabsBackupAction>(world);
|
||||||
|
prefabBackupAction->backupDir = EditorHelpers::getTransactionBackupDir(TCHAR("RemComp"));
|
||||||
|
prefabBackupAction->setEditingPrefabs(prefabs);
|
||||||
|
if (bAnyActorDetached)
|
||||||
|
{
|
||||||
|
transaction->editObject(
|
||||||
|
world, PropertyHelper::findField(cbe::World::staticType(), GET_MEMBER_ID_CHECKED(cbe::World, actorAttachedTo))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction->addCustomAction<cbe::RemoveCompsAddBackCompAction>(nullptr, std::move(addCompsAction));
|
||||||
|
|
||||||
|
/* Finally apply the remove */
|
||||||
|
cbe::TransactedEntriesCollector dummyCollector;
|
||||||
|
finalRmAction->apply(dummyCollector);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorHelpers::setRootComponent(
|
||||||
|
ArrayView<cbe::ActorPrefab *> prefabs, ArrayView<cbe::TransformComponent *> newRootComps, cbe::Transaction *transaction
|
||||||
|
)
|
||||||
|
{
|
||||||
|
cbe::World *world = prefabs.front()->getActorTemplate()->getWorld();
|
||||||
|
#if DEBUG_VALIDATIONS_ENABLED
|
||||||
|
if (world != nullptr)
|
||||||
|
{
|
||||||
|
for (cbe::ActorPrefab *prefab : prefabs)
|
||||||
|
{
|
||||||
|
debugAssert(world == prefab->getActorTemplate()->getWorld());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::vector<cbe::RootCompsReattachAction::PerPrefabData> prefabsData;
|
||||||
|
prefabsData.reserve(prefabs.size());
|
||||||
|
if (transaction != nullptr)
|
||||||
|
{
|
||||||
|
for (uint32 i = 0; i < prefabs.size(); ++i)
|
||||||
|
{
|
||||||
|
cbe::ActorPrefab *prefab = prefabs[i];
|
||||||
|
cbe::TransformComponent *newRoot = newRootComps[i];
|
||||||
|
|
||||||
|
cbe::TransformComponent *oldRootAttachedTo = nullptr;
|
||||||
|
cbe::TransformComponent *oldRoot = prefab->getRootComponent();
|
||||||
|
if (world != nullptr)
|
||||||
|
{
|
||||||
|
oldRootAttachedTo = oldRoot->getAttachedTo();
|
||||||
|
}
|
||||||
|
|
||||||
|
prefabsData.emplace_back(cbe::RootCompsReattachAction::PerPrefabData{
|
||||||
|
.prefab = cbe::ObjectPath(prefab),
|
||||||
|
.oldRoot = cbe::ObjectPath(oldRoot),
|
||||||
|
.newRoot = cbe::ObjectPath(newRoot),
|
||||||
|
.oldRootAttachedTo = cbe::ObjectPath(oldRootAttachedTo),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (world != nullptr)
|
||||||
|
{
|
||||||
|
cbe::RootCompsReattachAction *postRevertAction = transaction->addCustomAction<cbe::RootCompsReattachAction>(nullptr);
|
||||||
|
postRevertAction->bAtApply = false;
|
||||||
|
postRevertAction->bAtRevert = true;
|
||||||
|
postRevertAction->prefabs = prefabsData;
|
||||||
|
postRevertAction->worldPtr = world;
|
||||||
|
}
|
||||||
|
|
||||||
|
cbe::ActorPrefabsBackupAction *prefabBackupAction = transaction->addCustomAction<cbe::ActorPrefabsBackupAction>(world);
|
||||||
|
prefabBackupAction->backupDir = EditorHelpers::getTransactionBackupDir(TCHAR("SetRootComp"));
|
||||||
|
prefabBackupAction->setEditingPrefabs(prefabs);
|
||||||
|
|
||||||
|
if (world != nullptr)
|
||||||
|
{
|
||||||
|
cbe::RootCompsReattachAction *postApplyAction = transaction->addCustomAction<cbe::RootCompsReattachAction>(nullptr);
|
||||||
|
postApplyAction->bAtApply = true;
|
||||||
|
postApplyAction->bAtRevert = false;
|
||||||
|
postApplyAction->prefabs = prefabsData;
|
||||||
|
postApplyAction->worldPtr = world;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do regular root setup without any transaction */
|
||||||
|
for (uint32 i = 0; i < prefabs.size(); ++i)
|
||||||
|
{
|
||||||
|
cbe::ActorPrefab *prefab = prefabs[i];
|
||||||
|
cbe::TransformComponent *newRoot = newRootComps[i];
|
||||||
|
|
||||||
|
cbe::TransformComponent *oldRootAttachedTo = nullptr;
|
||||||
|
cbe::TransformComponent *oldRoot = prefab->getRootComponent();
|
||||||
|
if (world != nullptr)
|
||||||
|
{
|
||||||
|
oldRootAttachedTo = oldRoot->getAttachedTo();
|
||||||
|
}
|
||||||
|
|
||||||
|
prefab->setRootComponent(newRoot, true);
|
||||||
|
cbe::markPackageDirty(prefab);
|
||||||
|
|
||||||
|
if (world != nullptr)
|
||||||
|
{
|
||||||
|
/* Attach the old root to new root. */
|
||||||
|
debugAssert(prefab->getAttachedToComp(oldRoot) == newRoot);
|
||||||
|
|
||||||
|
/* First detach the newRoot from oldRoot or its child to avoid Cyclic Graph */
|
||||||
|
world->tfAttachmentChanged(newRoot, nullptr);
|
||||||
|
EditorHelpers::attachComponent(oldRoot, newRoot);
|
||||||
|
/* Attach the actor to previously attached actor */
|
||||||
|
EditorHelpers::attachActor(world, prefab->getActorTemplate(), oldRootAttachedTo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date December 2025
|
* \date December 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -13,9 +13,12 @@
|
|||||||
|
|
||||||
#include <CBEEditorExports.h>
|
#include <CBEEditorExports.h>
|
||||||
#include <Transaction/Transaction.hpp>
|
#include <Transaction/Transaction.hpp>
|
||||||
|
#include <ICoreAssetEditor.hpp>
|
||||||
|
|
||||||
|
class CoreObjectsDB;
|
||||||
namespace cbe
|
namespace cbe
|
||||||
{
|
{
|
||||||
|
class ActorPrefab;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Asset save action can use this to transact editor saving asset to disk.
|
* @brief Asset save action can use this to transact editor saving asset to disk.
|
||||||
@@ -51,4 +54,90 @@ private:
|
|||||||
cbe::Object *obj;
|
cbe::Object *obj;
|
||||||
cbe::Package *objPkg;
|
cbe::Package *objPkg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Saves the given actor prefab's original form to a provided backup directory.
|
||||||
|
* At end transaction takes the post edit backup.
|
||||||
|
*/
|
||||||
|
class CBEEDITOR_EXPORT ActorPrefabsBackupAction final : public TransactionCustomAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ActorPrefabsBackupAction();
|
||||||
|
ActorPrefabsBackupAction(ActorPrefabsBackupAction &&other)
|
||||||
|
: backupDir(std::move(other.backupDir))
|
||||||
|
, actorPrefabPaths(std::move(other.actorPrefabPaths))
|
||||||
|
, objsDb(other.objsDb)
|
||||||
|
{
|
||||||
|
other.objsDb = nullptr;
|
||||||
|
}
|
||||||
|
~ActorPrefabsBackupAction();
|
||||||
|
|
||||||
|
void setEditingPrefabs(ArrayView<cbe::ActorPrefab *> prefabs);
|
||||||
|
|
||||||
|
/* TransactionCustomAction overrides */
|
||||||
|
void collectReferences(std::vector<Object *> &outObjects) const final;
|
||||||
|
void apply(TransactedEntriesCollector &outEntriesCollector) final;
|
||||||
|
void revert(TransactedEntriesCollector &outEntriesCollector) final;
|
||||||
|
bool isValid() const final;
|
||||||
|
void endTransaction() final;
|
||||||
|
/* Overrides ends */
|
||||||
|
|
||||||
|
public:
|
||||||
|
String backupDir;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<cbe::ObjectPath> actorPrefabPaths;
|
||||||
|
|
||||||
|
const CoreObjectsDB *objsDb;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AssetEditorTxnEventAction final : public TransactionCustomAction
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
using EventCb = SingleCastEvent<
|
||||||
|
AssetEditorTxnEventAction, void, const ICoreAssetEditorRef & /* editorRef */, uint64 /* userData */, bool /* bIsApply */>;
|
||||||
|
AssetEditorTxnEventAction(ICoreAssetEditorRef inEditorRef, const ObjectPath &inObj, EventCb &&inCallback, uint64 inUserData)
|
||||||
|
: editorRef(inEditorRef)
|
||||||
|
, validationObject(inObj)
|
||||||
|
, callback(inCallback)
|
||||||
|
, userData(inUserData)
|
||||||
|
{}
|
||||||
|
AssetEditorTxnEventAction(AssetEditorTxnEventAction &&other)
|
||||||
|
: editorRef(std::move(other.editorRef))
|
||||||
|
, validationObject(std::move(other.validationObject))
|
||||||
|
, callback(std::move(other.callback))
|
||||||
|
, userData(other.userData)
|
||||||
|
{
|
||||||
|
other.userData = 0;
|
||||||
|
}
|
||||||
|
~AssetEditorTxnEventAction() = default;
|
||||||
|
|
||||||
|
/* TransactionCustomAction overrides */
|
||||||
|
void collectReferences(std::vector<Object *> &) const final {}
|
||||||
|
void apply(TransactedEntriesCollector &) final
|
||||||
|
{
|
||||||
|
if (callback.isBound())
|
||||||
|
{
|
||||||
|
callback.invoke(editorRef, userData, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void revert(TransactedEntriesCollector &) final
|
||||||
|
{
|
||||||
|
if (callback.isBound())
|
||||||
|
{
|
||||||
|
callback.invoke(editorRef, userData, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool isValid() const final { return validationObject.isValid(); }
|
||||||
|
/* Overrides ends */
|
||||||
|
|
||||||
|
private:
|
||||||
|
ICoreAssetEditorRef editorRef;
|
||||||
|
ObjectPath validationObject;
|
||||||
|
|
||||||
|
EventCb callback;
|
||||||
|
uint64 userData;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace cbe
|
} // namespace cbe
|
||||||
@@ -4,15 +4,39 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date August 2022
|
* \date August 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2024
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Types/Delegates/Delegate.h"
|
#include <Types/Delegates/Delegate.h>
|
||||||
|
#include <CBEObjectTypes.h>
|
||||||
|
#include <Types/Colors.h>
|
||||||
|
|
||||||
class ImGuiDrawInterface;
|
class ImGuiDrawInterface;
|
||||||
|
|
||||||
using ImGuiDrawInterfaceCallback = SimpleDelegate;
|
using ImGuiDrawInterfaceCallback = SimpleDelegate;
|
||||||
|
|
||||||
|
namespace cbe
|
||||||
|
{
|
||||||
|
|
||||||
|
struct EdClassCacheDrawInfo
|
||||||
|
{
|
||||||
|
/* Not using constants header to reduce include pollution */
|
||||||
|
std::array<AChar, 4> classInitials;
|
||||||
|
Color typeColor;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EdClassCacheList
|
||||||
|
{
|
||||||
|
static_assert(!IsTCharWide::value, "Wide character support is not optimized here! It will work but performs bad");
|
||||||
|
|
||||||
|
std::vector<CBEClass> classList;
|
||||||
|
std::vector<EdClassCacheDrawInfo> classDrawInfo;
|
||||||
|
|
||||||
|
String filter;
|
||||||
|
BitArray<uint64> filteredBits;
|
||||||
|
};
|
||||||
|
} // namespace cbe
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date July 2025
|
* \date July 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -65,6 +65,8 @@ void ObjectsDetailsDrawer::drawWidgets(const DrawWidgetArgs &args) const
|
|||||||
|
|
||||||
if (!args.bDrawHeader || cbe::ImGuiHelpers::collapsingHeaderInTable(headerName, PROPS_DETAIL_OBJ_HEADER_FLAGS))
|
if (!args.bDrawHeader || cbe::ImGuiHelpers::collapsingHeaderInTable(headerName, PROPS_DETAIL_OBJ_HEADER_FLAGS))
|
||||||
{
|
{
|
||||||
|
ImGui::PushID(modelObjData.sid.getID());
|
||||||
|
|
||||||
/* Draw all class level customizations first */
|
/* Draw all class level customizations first */
|
||||||
for (const CustomizerContext &cntx : classCustomizers)
|
for (const CustomizerContext &cntx : classCustomizers)
|
||||||
{
|
{
|
||||||
@@ -122,6 +124,8 @@ void ObjectsDetailsDrawer::drawWidgets(const DrawWidgetArgs &args) const
|
|||||||
.bFilterPassed = bFilterPassed,
|
.bFilterPassed = bFilterPassed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +299,7 @@ bool StructDetailsDrawer::drawWidgets(const DrawWidgetArgs &args) const
|
|||||||
String filter = args.filter;
|
String filter = args.filter;
|
||||||
filter.toLower();
|
filter.toLower();
|
||||||
|
|
||||||
LOG_WARN_C(!filter.empty(), "StructDetailsDrawer", "Struct details filtering is not implemented yet. Filter {}", args.filter);
|
CBE_LOG_WARN_C(!filter.empty(), "StructDetailsDrawer", "Struct details filtering is not implemented yet. Filter {}", args.filter);
|
||||||
|
|
||||||
if (headerName.empty() || cbe::ImGuiHelpers::collapsingHeaderInTable(headerName, PROPS_DETAIL_OBJ_HEADER_FLAGS))
|
if (headerName.empty() || cbe::ImGuiHelpers::collapsingHeaderInTable(headerName, PROPS_DETAIL_OBJ_HEADER_FLAGS))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date July 2025
|
* \date July 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
#include <CBEAssetManager.hpp>
|
#include <CBEAssetManager.hpp>
|
||||||
#include <Property/Property.h>
|
#include <Property/Property.h>
|
||||||
#include <Classes/ActorPrefab.hpp>
|
#include <Classes/ActorPrefab.hpp>
|
||||||
|
#include <EditorTypes.h>
|
||||||
|
|
||||||
namespace cbe
|
namespace cbe
|
||||||
{
|
{
|
||||||
@@ -462,4 +463,98 @@ void EditorWidgetsHelper::drawPackageIcon(Rect bb, std::string_view classInitial
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CBEClass EditorWidgetsHelper::drawClassMenuList(EdClassCacheList &classList, float popupItemWidth, bool bShowFilter)
|
||||||
|
{
|
||||||
|
if (bShowFilter)
|
||||||
|
{
|
||||||
|
const float filterStartPos = ImGui::GetCursorStartPos().y;
|
||||||
|
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||||
|
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip);
|
||||||
|
if (cbe::ImGuiHelpers::inputTextWithHint(
|
||||||
|
"###AssetFilterText", "Filter Asset", &classList.filter,
|
||||||
|
ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_NoHorizontalScroll
|
||||||
|
))
|
||||||
|
{
|
||||||
|
/* Scroll back to filter every time filter is modified. */
|
||||||
|
ImGui::SetScrollFromPosY(filterStartPos, 0.0f);
|
||||||
|
|
||||||
|
const String filter = classList.filter.toLowerCopy();
|
||||||
|
for (SizeT i = 0; i < classList.classList.size(); ++i)
|
||||||
|
{
|
||||||
|
String className = String(classList.classList[i]->nameString);
|
||||||
|
className.toLower();
|
||||||
|
classList.filteredBits[i] = filter.empty() || className.find(filter) != String::npos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Scroll to text filter the moment it becomes active. */
|
||||||
|
if (ImGui::IsItemActivated())
|
||||||
|
{
|
||||||
|
ImGui::SetScrollFromPosY(filterStartPos, 0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImDrawList *drawList = ImGui::GetWindowDrawList();
|
||||||
|
const ImGuiStyle &imguiStyle = ImGui::GetStyle();
|
||||||
|
const Vector2 selectableSize{
|
||||||
|
popupItemWidth,
|
||||||
|
Math::max(wg_consts::ASSET_SELECTOR_ICON_SIZE, 2 * ImGui::GetTextLineHeight()) + (2 * imguiStyle.FramePadding.y),
|
||||||
|
};
|
||||||
|
|
||||||
|
CBEClass selectedClazz = nullptr;
|
||||||
|
|
||||||
|
for (uint64 i = 0; i < classList.classList.size(); ++i)
|
||||||
|
{
|
||||||
|
if (!classList.filteredBits[i])
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CBEClass clazz = classList.classList[i];
|
||||||
|
const EdClassCacheDrawInfo &clazzDrawInf = classList.classDrawInfo[i];
|
||||||
|
ImGui::PushID(std::bit_cast<int32>(clazz->name.getID()));
|
||||||
|
|
||||||
|
const Vector2 contentStartPos = ImGui::GetCursorScreenPos();
|
||||||
|
|
||||||
|
const bool bSelected = ImGui::Selectable("", false, ImGuiSelectableFlags_None, selectableSize);
|
||||||
|
|
||||||
|
/* Draw Package icon, center align in Y */
|
||||||
|
const float iconAlignOffsetY = (selectableSize.y - (2 * imguiStyle.FramePadding.y) - wg_consts::ASSET_SELECTOR_ICON_SIZE) * 0.5f;
|
||||||
|
const Vector2 iconRectStartPos = contentStartPos + Vector2(imguiStyle.FramePadding) + Vector2(0, iconAlignOffsetY);
|
||||||
|
const Vector2 iconRectEndPos = iconRectStartPos + wg_consts::ASSET_SELECTOR_ICON_SIZE;
|
||||||
|
cbe::EditorWidgetsHelper::drawPackageIcon(
|
||||||
|
{ iconRectStartPos, iconRectEndPos }, clazzDrawInf.classInitials.data(), clazzDrawInf.typeColor,
|
||||||
|
wg_consts::ASSET_SELECTOR_ICON_FONT_SCALE
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Center align text, some logic can be moved out of loop */
|
||||||
|
const float labelStartX = iconRectEndPos.x + imguiStyle.ItemInnerSpacing.x;
|
||||||
|
const float labelWidth = selectableSize.x - (labelStartX - contentStartPos.x) - imguiStyle.FramePadding.x;
|
||||||
|
const float labelEndX = labelStartX + labelWidth;
|
||||||
|
const float labelHeight = ImGui::CalcTextSize(TCHAR_TO_UTF8(clazz->nameString).data(), nullptr, false, labelWidth).y;
|
||||||
|
const float labelAlignOffsetY = (selectableSize.y - (2 * imguiStyle.FramePadding.y) - labelHeight) * 0.5f;
|
||||||
|
const float labelStartY = contentStartPos.y + imguiStyle.FramePadding.y + labelAlignOffsetY;
|
||||||
|
const float labelEndY = labelStartY + labelHeight;
|
||||||
|
|
||||||
|
const ImVec4 clipRect{
|
||||||
|
labelStartX,
|
||||||
|
labelStartY,
|
||||||
|
labelEndX,
|
||||||
|
labelEndY,
|
||||||
|
};
|
||||||
|
drawList->AddText(
|
||||||
|
ImGui::GetFont(), ImGui::GetFontSize(), ImVec2(clipRect.x, clipRect.y), ImGui::GetColorU32(ImGuiCol_Text),
|
||||||
|
TCHAR_TO_UTF8(clazz->nameString).data(), nullptr, labelWidth, &clipRect
|
||||||
|
);
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
|
||||||
|
if (bSelected)
|
||||||
|
{
|
||||||
|
selectedClazz = clazz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedClazz;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace cbe
|
} // namespace cbe
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date July 2025
|
* \date July 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <CBEEditorExports.h>
|
#include <CBEEditorExports.h>
|
||||||
|
#include <CBEObjectTypes.h>
|
||||||
#include <Widgets/WgEditorConstants.hpp>
|
#include <Widgets/WgEditorConstants.hpp>
|
||||||
#include <String/String.h>
|
#include <String/String.h>
|
||||||
#include <Widgets/ImGui/CbeImGui.hpp>
|
#include <Widgets/ImGui/CbeImGui.hpp>
|
||||||
@@ -24,6 +25,7 @@ class Transform3D;
|
|||||||
|
|
||||||
namespace cbe
|
namespace cbe
|
||||||
{
|
{
|
||||||
|
struct EdClassCacheList;
|
||||||
class Object;
|
class Object;
|
||||||
class AssetManager;
|
class AssetManager;
|
||||||
enum class ECloseConfirmationState : uint8
|
enum class ECloseConfirmationState : uint8
|
||||||
@@ -87,6 +89,8 @@ public:
|
|||||||
/* Only draws the icon using drawlist does not add any bb items. */
|
/* Only draws the icon using drawlist does not add any bb items. */
|
||||||
static void drawPackageIcon(Rect bb, std::string_view classInitials, Color bgColor, float fontScale = 1.0f);
|
static void drawPackageIcon(Rect bb, std::string_view classInitials, Color bgColor, float fontScale = 1.0f);
|
||||||
|
|
||||||
|
static CBEClass drawClassMenuList(EdClassCacheList &classList, float popupItemWidth, bool bShowFilter);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EditorWidgetsHelper() = default;
|
EditorWidgetsHelper() = default;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date July 2025
|
* \date July 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -39,6 +39,8 @@
|
|||||||
#define CBE_MAT_ICON_COPY "\xEE\x85\x8D"
|
#define CBE_MAT_ICON_COPY "\xEE\x85\x8D"
|
||||||
/* \ue14f */
|
/* \ue14f */
|
||||||
#define CBE_MAT_ICON_PASTE "\xEE\x85\x8F"
|
#define CBE_MAT_ICON_PASTE "\xEE\x85\x8F"
|
||||||
|
/* \ue5c9 */
|
||||||
|
#define CBE_MAT_ICON_CANCEL "\xEE\x97\x89"
|
||||||
|
|
||||||
/* \ue2c7 */
|
/* \ue2c7 */
|
||||||
#define CBE_MAT_ICON_FOLDER_CLOSED "\xEE\x8B\x87"
|
#define CBE_MAT_ICON_FOLDER_CLOSED "\xEE\x8B\x87"
|
||||||
@@ -103,6 +105,7 @@ constexpr const AChar *REMOVE = CBE_MAT_ICON_REMOVE;
|
|||||||
constexpr const AChar *CUT = CBE_MAT_ICON_CUT;
|
constexpr const AChar *CUT = CBE_MAT_ICON_CUT;
|
||||||
constexpr const AChar *COPY = CBE_MAT_ICON_COPY;
|
constexpr const AChar *COPY = CBE_MAT_ICON_COPY;
|
||||||
constexpr const AChar *PASTE = CBE_MAT_ICON_PASTE;
|
constexpr const AChar *PASTE = CBE_MAT_ICON_PASTE;
|
||||||
|
constexpr const AChar *CANCEL = CBE_MAT_ICON_CANCEL;
|
||||||
|
|
||||||
constexpr const AChar *FOLDER_CLOSED = CBE_MAT_ICON_FOLDER_CLOSED;
|
constexpr const AChar *FOLDER_CLOSED = CBE_MAT_ICON_FOLDER_CLOSED;
|
||||||
constexpr const AChar *FOLDER_OPEN = CBE_MAT_ICON_FOLDER_OPEN;
|
constexpr const AChar *FOLDER_OPEN = CBE_MAT_ICON_FOLDER_OPEN;
|
||||||
@@ -150,6 +153,17 @@ constexpr const uint32 DEFAULT_LAYER_DEPTH = 1;
|
|||||||
constexpr ImGuiTreeNodeFlags PROP_TREE_NODE_FLAGS
|
constexpr ImGuiTreeNodeFlags PROP_TREE_NODE_FLAGS
|
||||||
= ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_LabelSpanAllColumns | ImGuiTreeNodeFlags_Framed;
|
= ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_LabelSpanAllColumns | ImGuiTreeNodeFlags_Framed;
|
||||||
constexpr ImGuiChildFlags INLINE_FITTING_CHILD_FLAGS = ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_ResizeY | ImGuiChildFlags_Borders;
|
constexpr ImGuiChildFlags INLINE_FITTING_CHILD_FLAGS = ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_ResizeY | ImGuiChildFlags_Borders;
|
||||||
|
|
||||||
|
constexpr const uint8 CLASS_INITIALS_COUNT = 3;
|
||||||
|
constexpr const float ASSET_SELECTOR_ICON_SIZE = 40;
|
||||||
|
constexpr const float ASSET_SELECTOR_ICON_FONT_SCALE = 1.5f;
|
||||||
|
constexpr const float MENUITEM_WIDTH = 200.f;
|
||||||
|
|
||||||
|
constexpr Vector2 HMODAL_POPUP_SIZE = { 500, 200 };
|
||||||
|
constexpr Vector2 VMODAL_POPUP_SIZE = { 300, 500 };
|
||||||
|
|
||||||
|
constexpr const float ANIMATION_TIME_SCALE = 1.0f;
|
||||||
|
|
||||||
} // namespace wg_consts
|
} // namespace wg_consts
|
||||||
|
|
||||||
namespace style
|
namespace style
|
||||||
@@ -173,6 +187,9 @@ constexpr static const Color HOVER_COLOR = ColorConst::YELLOW;
|
|||||||
constexpr static const Color ACTIVE_COLOR = ColorConst::YELLOW4;
|
constexpr static const Color ACTIVE_COLOR = ColorConst::YELLOW4;
|
||||||
constexpr static const Color DISABLED_COLOR = Color(160, 160, 160, 100);
|
constexpr static const Color DISABLED_COLOR = Color(160, 160, 160, 100);
|
||||||
|
|
||||||
|
constexpr static const Color ED_COLOR_PRIMARY = Color(51, 105, 173);
|
||||||
|
constexpr static const Color ED_COLOR_SECONDARY = Color(35, 67, 108);
|
||||||
|
|
||||||
constexpr static const Color NEUTRAL_COLOR_PRIMARY = ColorConst::WHITE;
|
constexpr static const Color NEUTRAL_COLOR_PRIMARY = ColorConst::WHITE;
|
||||||
constexpr static const Color NEUTRAL_COLOR_SECONDARY = ColorConst::WHEAT;
|
constexpr static const Color NEUTRAL_COLOR_SECONDARY = ColorConst::WHEAT;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2025
|
* \date June 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -27,12 +27,14 @@ node positionOffset
|
|||||||
|
|
||||||
node diffuseColor
|
node diffuseColor
|
||||||
{
|
{
|
||||||
return CBE_MAT_SAMPLE_TEXTURE(color, CBE_TEXTURE_COORD());
|
return
|
||||||
|
CBE_MAT_SAMPLE_TEXTURE(color, CBE_TEXTURE_COORD());
|
||||||
}
|
}
|
||||||
|
|
||||||
node normalOffset
|
node normalOffset
|
||||||
{
|
{
|
||||||
return (CBE_MAT_SAMPLE_TEXTURE(normal, CBE_TEXTURE_COORD()).xyz - 0.5) * 2;
|
return (
|
||||||
|
CBE_MAT_SAMPLE_TEXTURE(normal, CBE_TEXTURE_COORD()).xyz - 0.5) * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ set(cpp_target_sources
|
|||||||
Private/AssetEditors/IEngineAssetEditor.hpp
|
Private/AssetEditors/IEngineAssetEditor.hpp
|
||||||
Private/AssetEditors/MaterialInstanceAssetEditor.cpp
|
Private/AssetEditors/MaterialInstanceAssetEditor.cpp
|
||||||
Private/AssetEditors/MaterialInstanceAssetEditor.hpp
|
Private/AssetEditors/MaterialInstanceAssetEditor.hpp
|
||||||
Private/AudioImporter.cpp
|
|
||||||
Private/AudioImporter.hpp
|
|
||||||
Private/CBEEditorModule.cpp
|
Private/CBEEditorModule.cpp
|
||||||
Private/DetailCustomizers/GenericFieldCustomizer.cpp
|
Private/DetailCustomizers/GenericFieldCustomizer.cpp
|
||||||
Private/DetailCustomizers/GenericFieldCustomizer.hpp
|
Private/DetailCustomizers/GenericFieldCustomizer.hpp
|
||||||
@@ -16,10 +14,16 @@ set(cpp_target_sources
|
|||||||
Private/DetailCustomizers/MaterialInstanceCustomizer.hpp
|
Private/DetailCustomizers/MaterialInstanceCustomizer.hpp
|
||||||
Private/DetailCustomizers/TransformComponentCustomizer.cpp
|
Private/DetailCustomizers/TransformComponentCustomizer.cpp
|
||||||
Private/DetailCustomizers/TransformComponentCustomizer.hpp
|
Private/DetailCustomizers/TransformComponentCustomizer.hpp
|
||||||
Private/ObjStaticMeshImporter.cpp
|
Private/Importers/AssimpImporter.cpp
|
||||||
Private/StaticMeshImporter.hpp
|
Private/Importers/AssimpImporter.hpp
|
||||||
Private/Texture2DImporter.cpp
|
Private/Importers/AudioImporter.cpp
|
||||||
Private/Texture2DImporter.hpp
|
Private/Importers/AudioImporter.hpp
|
||||||
|
Private/Importers/ObjStaticMeshImporter.cpp
|
||||||
|
Private/Importers/StaticMeshImporter.hpp
|
||||||
|
Private/Importers/Texture2DImporter.cpp
|
||||||
|
Private/Importers/Texture2DImporter.hpp
|
||||||
|
Private/Payloads/EditorDragPayloads.cpp
|
||||||
|
Private/Payloads/EditorDragPayloads.hpp
|
||||||
Private/ThirdParties/StbWrapper.cpp
|
Private/ThirdParties/StbWrapper.cpp
|
||||||
Private/ThirdParties/StbWrapper.hpp
|
Private/ThirdParties/StbWrapper.hpp
|
||||||
Private/ViewportDrawers/TransformComponentViewportDrawer.cpp
|
Private/ViewportDrawers/TransformComponentViewportDrawer.cpp
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date July 2022
|
* \date July 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -208,7 +208,7 @@ void EditorCoreModule::registerStructDetailsCustomizer(cbe::StructDetailsCustomi
|
|||||||
auto itr = structCustomizers.find(customizer->getCustomizingClass());
|
auto itr = structCustomizers.find(customizer->getCustomizingClass());
|
||||||
if (itr != structCustomizers.cend())
|
if (itr != structCustomizers.cend())
|
||||||
{
|
{
|
||||||
LOG_ERROR(IEditorCore::LOG_CATEGORY, "Customizer exists for struct {}", customizer->getCustomizingClass()->nameString);
|
CBE_LOG_ERROR(IEditorCore::LOG_CATEGORY, "Customizer exists for struct {}", customizer->getCustomizingClass()->nameString);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +243,7 @@ void EditorCoreModule::registerAssetFactory(cbe::ICoreAssetFactory *editorFactor
|
|||||||
auto itr = assetFactories.find(editorFactory->getAssetClass());
|
auto itr = assetFactories.find(editorFactory->getAssetClass());
|
||||||
if (itr != assetFactories.cend())
|
if (itr != assetFactories.cend())
|
||||||
{
|
{
|
||||||
LOG_ERROR(IEditorCore::LOG_CATEGORY, "Asset editor factory exists for asset type {}", editorFactory->getAssetClass()->nameString);
|
CBE_LOG_ERROR(IEditorCore::LOG_CATEGORY, "Asset editor factory exists for asset type {}", editorFactory->getAssetClass()->nameString);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date July 2022
|
* \date July 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <EditorCoreExports.h>
|
#include <EditorCoreExports.h>
|
||||||
|
#include <Types/Delegates/Delegate.h>
|
||||||
#include <Types/Platform/PlatformAssertionErrors.h>
|
#include <Types/Platform/PlatformAssertionErrors.h>
|
||||||
#include <Memory/SmartPointers.h>
|
#include <Memory/SmartPointers.h>
|
||||||
#include <CBEObjectTypes.h>
|
#include <CBEObjectTypes.h>
|
||||||
@@ -31,9 +32,16 @@ struct ImportOption
|
|||||||
/* Path to directory in which to store the package relative to importContentPath. */
|
/* Path to directory in which to store the package relative to importContentPath. */
|
||||||
String relativeDirPath;
|
String relativeDirPath;
|
||||||
|
|
||||||
|
/* Callback with progress count */
|
||||||
|
SingleCastDelegate<void, uint32, StringView> progressCb;
|
||||||
|
|
||||||
|
/* Before prepare */
|
||||||
|
uint32 prepareProgressCount;
|
||||||
void *optionsStruct;
|
void *optionsStruct;
|
||||||
CBEClass optionsStructType;
|
CBEClass optionsStructType;
|
||||||
|
|
||||||
|
/* After prepare */
|
||||||
|
uint32 importProgressCount;
|
||||||
/* This could be null if the importer do not have any parsed options based on the asset itself. */
|
/* This could be null if the importer do not have any parsed options based on the asset itself. */
|
||||||
void *cntxOptionsStruct;
|
void *cntxOptionsStruct;
|
||||||
CBEClass cntxOptionsStructType;
|
CBEClass cntxOptionsStructType;
|
||||||
@@ -42,9 +50,16 @@ struct ImportOption
|
|||||||
class AssetImporterBase
|
class AssetImporterBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static constexpr const TChar *MATERIAL_INST_PREFIX = TCHAR("MI_");
|
||||||
|
static constexpr const TChar *TEXTURE_PREFIX = TCHAR("T_");
|
||||||
|
static constexpr const TChar *AUDIO_SRC_PREFIX = TCHAR("AS_");
|
||||||
|
static constexpr const TChar *STATIC_MESH_PREFIX = TCHAR("SM_");
|
||||||
|
static constexpr const TChar *WORLD_PREFIX = TCHAR("W_");
|
||||||
|
|
||||||
struct ImportContextDtor
|
struct ImportContextDtor
|
||||||
{
|
{
|
||||||
AssetImporterBase *importer;
|
AssetImporterBase *importer;
|
||||||
|
|
||||||
constexpr void operator() (void *ptr) const noexcept
|
constexpr void operator() (void *ptr) const noexcept
|
||||||
{
|
{
|
||||||
if (importer != nullptr)
|
if (importer != nullptr)
|
||||||
@@ -70,7 +85,8 @@ public:
|
|||||||
using ImportResult = std::variant<ImportError, ImportSuccess>;
|
using ImportResult = std::variant<ImportError, ImportSuccess>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr static const uint32 AllocSlotCount = 2;
|
AssetImporterBase() = default;
|
||||||
|
MAKE_TYPE_NONCOPY_NONMOVE(AssetImporterBase)
|
||||||
virtual ~AssetImporterBase() = default;
|
virtual ~AssetImporterBase() = default;
|
||||||
|
|
||||||
virtual bool supportsImporting(ImportOption & /*inOutOptions*/) { return false; }
|
virtual bool supportsImporting(ImportOption & /*inOutOptions*/) { return false; }
|
||||||
@@ -89,9 +105,6 @@ public:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Jeslas) : Remove below once new import apis are ready and UI available.
|
|
||||||
virtual std::optional<std::vector<cbe::Object *>> tryImporting(const ImportOption & /*importOptions*/) const { return {}; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void destructImportContext(void *cntx) = 0;
|
virtual void destructImportContext(void *cntx) = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date September 2025
|
* \date September 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -70,14 +70,81 @@ void ICoreAssetEditor::closeEditor()
|
|||||||
{
|
{
|
||||||
factory->onCloseAssetEditor(this);
|
factory->onCloseAssetEditor(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editingAsset = nullptr;
|
||||||
|
asset = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MAKE_ENUM_ARITHMETIC_OPS(ECoreEdMessageType)
|
MAKE_ENUM_ARITHMETIC_OPS(ECoreEdMessageType)
|
||||||
void ICoreAssetEditor::edTick(float deltaTime)
|
void ICoreAssetEditor::edTick(float deltaTime)
|
||||||
|
{
|
||||||
|
pumpMessages();
|
||||||
|
if (editingAsset != nullptr)
|
||||||
|
{
|
||||||
|
bEditingAssetDirty = cbe::isPackageDirty(editingAsset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bEditingAssetDirty = false;
|
||||||
|
}
|
||||||
|
onEdTick(deltaTime);
|
||||||
|
}
|
||||||
|
void ICoreAssetEditor::pushMessage(MessageData msgDat, ECoreEdMessageType msgType)
|
||||||
|
{
|
||||||
|
msgDat.lastHandleState = MessageResponse::None;
|
||||||
|
|
||||||
|
if (!std::holds_alternative<NullType>(messages[msgType].msg))
|
||||||
|
{
|
||||||
|
const TChar *editObjName = asset != nullptr ? asset->getObjectData().name : TCHAR("Editor");
|
||||||
|
if (messages[msgType].lastHandleState == MessageResponse::NotHandled)
|
||||||
|
{
|
||||||
|
alertOncef(
|
||||||
|
messages[msgType].lastHandleState != MessageResponse::NotHandled, "Message of type {} has no handlers in object {}", msgType,
|
||||||
|
editObjName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bAnyMsgs = true;
|
||||||
|
messages[msgType] = msgDat;
|
||||||
|
}
|
||||||
|
void ICoreAssetEditor::pushMessage(ECoreEdMessageType msgType)
|
||||||
|
{
|
||||||
|
const MessageData msgDat{ .msg = EmptyType{} };
|
||||||
|
pushMessage(msgDat, msgType);
|
||||||
|
}
|
||||||
|
void ICoreAssetEditor::pushMessageOverwrite(MessageData msgDat, ECoreEdMessageType msgType)
|
||||||
|
{
|
||||||
|
msgDat.lastHandleState = MessageResponse::None;
|
||||||
|
|
||||||
|
if (!std::holds_alternative<NullType>(messages[msgType].msg))
|
||||||
|
{
|
||||||
|
const TChar *editObjName = asset != nullptr ? asset->getObjectData().name : TCHAR("Editor");
|
||||||
|
CBE_LOG_WARN_C(
|
||||||
|
messages[msgType].lastHandleState == MessageResponse::None, IEditorCore::LOG_CATEGORY,
|
||||||
|
"Message of type {}[Last state: {}] is being overwritten in object {}", msgType, messages[msgType].lastHandleState, editObjName
|
||||||
|
);
|
||||||
|
|
||||||
|
if (messages[msgType].lastHandleState == MessageResponse::NotHandled)
|
||||||
|
{
|
||||||
|
alertOncef(
|
||||||
|
messages[msgType].lastHandleState != MessageResponse::NotHandled, "Message of type {} has no handlers in object {}", msgType,
|
||||||
|
editObjName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bAnyMsgs = true;
|
||||||
|
messages[msgType] = msgDat;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICoreAssetEditor::pumpMessages()
|
||||||
{
|
{
|
||||||
/* Process until all the messages are cleared. Break when there is sticky message to allow app to be responsive. */
|
/* Process until all the messages are cleared. Break when there is sticky message to allow app to be responsive. */
|
||||||
while (bAnyMsgs)
|
while (bAnyMsgs)
|
||||||
{
|
{
|
||||||
|
/* Immediately clear the any messages flags to allow processing messages sent by another message. */
|
||||||
|
bAnyMsgs = false;
|
||||||
|
|
||||||
bool bAnySticky = false;
|
bool bAnySticky = false;
|
||||||
|
|
||||||
std::array<MessageData, CoreEdMessage_MaxCount> msgsCopy = messages;
|
std::array<MessageData, CoreEdMessage_MaxCount> msgsCopy = messages;
|
||||||
@@ -110,7 +177,7 @@ void ICoreAssetEditor::edTick(float deltaTime)
|
|||||||
/* Since messages will only have msgs enqueued in current frame. */
|
/* Since messages will only have msgs enqueued in current frame. */
|
||||||
debugAssert(messages[msgType].lastHandleState == MessageResponse::None);
|
debugAssert(messages[msgType].lastHandleState == MessageResponse::None);
|
||||||
const TChar *editObjName = asset != nullptr ? asset->getObjectData().name : TCHAR("Editor");
|
const TChar *editObjName = asset != nullptr ? asset->getObjectData().name : TCHAR("Editor");
|
||||||
LOG_WARN(
|
CBE_LOG_WARN(
|
||||||
IEditorCore::LOG_CATEGORY, "Message of type {}[State: {}] is being dropped in object {}", msgType,
|
IEditorCore::LOG_CATEGORY, "Message of type {}[State: {}] is being dropped in object {}", msgType,
|
||||||
msgsCopy[msgType].lastHandleState, editObjName
|
msgsCopy[msgType].lastHandleState, editObjName
|
||||||
);
|
);
|
||||||
@@ -125,49 +192,12 @@ void ICoreAssetEditor::edTick(float deltaTime)
|
|||||||
/* If anything became sticky */
|
/* If anything became sticky */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
bAnyMsgs = false;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (editingAsset != nullptr)
|
|
||||||
{
|
|
||||||
bEditingAssetDirty = cbe::isPackageDirty(editingAsset);
|
|
||||||
}
|
|
||||||
onEdTick(deltaTime);
|
|
||||||
}
|
|
||||||
void ICoreAssetEditor::pushMessage(MessageData msgDat, ECoreEdMessageType msgType)
|
|
||||||
{
|
|
||||||
msgDat.lastHandleState = MessageResponse::None;
|
|
||||||
|
|
||||||
if (!std::holds_alternative<NullType>(messages[msgType].msg))
|
|
||||||
{
|
|
||||||
const TChar *editObjName = asset != nullptr ? asset->getObjectData().name : TCHAR("Editor");
|
|
||||||
LOG_WARN_C(
|
|
||||||
messages[msgType].lastHandleState == MessageResponse::None, IEditorCore::LOG_CATEGORY,
|
|
||||||
"Message of type {}[Last state: {}] is being overwritten in object {}", msgType, messages[msgType].lastHandleState, editObjName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (messages[msgType].lastHandleState == MessageResponse::NotHandled)
|
|
||||||
{
|
|
||||||
alertOncef(
|
|
||||||
messages[msgType].lastHandleState != MessageResponse::NotHandled, "Message of type {} has no handlers in object {}", msgType,
|
|
||||||
editObjName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bAnyMsgs = true;
|
|
||||||
messages[msgType] = msgDat;
|
|
||||||
}
|
|
||||||
void ICoreAssetEditor::pushMessage(ECoreEdMessageType msgType)
|
|
||||||
{
|
|
||||||
const MessageData msgDat{ .msg = EmptyType{} };
|
|
||||||
pushMessage(msgDat, msgType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICoreAssetEditor::clearReferences(ArrayView<cbe::Object *> deletedObjects)
|
void ICoreAssetEditor::clearReferences(ArrayView<cbe::Object *> deletedObjects)
|
||||||
{
|
{
|
||||||
debugAssertf(deletedObjects.empty(), "References clearing after asset load is unhandled!");
|
debugAssertf(editingAsset == nullptr || deletedObjects.empty(), "References clearing after asset load is unhandled!");
|
||||||
}
|
}
|
||||||
void ICoreAssetEditor::collectReferences(std::vector<cbe::Object *> &outObjects) const
|
void ICoreAssetEditor::collectReferences(std::vector<cbe::Object *> &outObjects) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date September 2025
|
* \date September 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -107,7 +107,7 @@ public:
|
|||||||
* @brief If asset factory supports custom additional asset creation menus must be drawn here.
|
* @brief If asset factory supports custom additional asset creation menus must be drawn here.
|
||||||
* @return Callback that must used to create the asset instead of regular create callback.
|
* @return Callback that must used to create the asset instead of regular create callback.
|
||||||
*/
|
*/
|
||||||
virtual AssetCreateCustomCallback drawCustomCreateMenus() const { return {}; }
|
virtual AssetCreateCustomCallback drawCustomCreateMenus() { return {}; }
|
||||||
|
|
||||||
/* Returns true if committed else default handling must be used.
|
/* Returns true if committed else default handling must be used.
|
||||||
* Caller must handle the false case.
|
* Caller must handle the false case.
|
||||||
@@ -154,7 +154,7 @@ enum ECoreEdMessageType : uint8
|
|||||||
CoreEdMessage_SelectionTransformed, ///< When selection gets moved interactively.
|
CoreEdMessage_SelectionTransformed, ///< When selection gets moved interactively.
|
||||||
CoreEdMessage_Custom, ///< Custom messages from this and beyond.
|
CoreEdMessage_Custom, ///< Custom messages from this and beyond.
|
||||||
CoreEdMessage_MaxCount = std::numeric_limits<uint8>::max(),
|
CoreEdMessage_MaxCount = std::numeric_limits<uint8>::max(),
|
||||||
CoreEdMessage_Begin = CoreEdMessage_RefreshEd,
|
CoreEdMessage_Begin = CoreEdMessage_ObjsEdited,
|
||||||
CoreEdMessage_End = CoreEdMessage_MaxCount,
|
CoreEdMessage_End = CoreEdMessage_MaxCount,
|
||||||
};
|
};
|
||||||
struct MessageResponse
|
struct MessageResponse
|
||||||
@@ -169,9 +169,15 @@ struct MessageResponse
|
|||||||
|
|
||||||
EType state;
|
EType state;
|
||||||
};
|
};
|
||||||
|
enum EEdRefreshType
|
||||||
|
{
|
||||||
|
VisualOnly, ///< Refresh does not changes underlying data, only draw data gets refreshed.
|
||||||
|
FullReset, ///< Full refresh where the visualizing data gets fetched and entire draw data gets regenerated.
|
||||||
|
};
|
||||||
|
|
||||||
struct MessageData
|
struct MessageData
|
||||||
{
|
{
|
||||||
using ObjectsList = std::vector<cbe::Object *>;
|
using ObjectsList = std::vector<WeakObjectPtr>;
|
||||||
struct SubSelectionData
|
struct SubSelectionData
|
||||||
{
|
{
|
||||||
std::vector<WeakObjectPtr> objs;
|
std::vector<WeakObjectPtr> objs;
|
||||||
@@ -179,7 +185,7 @@ struct MessageData
|
|||||||
};
|
};
|
||||||
/* EmptyType is if the message has no data associated to it like refresh.
|
/* EmptyType is if the message has no data associated to it like refresh.
|
||||||
* Pointer for Custom messages. */
|
* Pointer for Custom messages. */
|
||||||
using DataType = std::variant<NullType, ObjectsList, SubSelectionData, void *, EmptyType>;
|
using DataType = std::variant<NullType, ObjectsList, SubSelectionData, void *, EEdRefreshType, EmptyType>;
|
||||||
/* Data to identify who the issuer is. This is to ensure messages do not get enqueued cyclically. */
|
/* Data to identify who the issuer is. This is to ensure messages do not get enqueued cyclically. */
|
||||||
uint64 issuerData = 0;
|
uint64 issuerData = 0;
|
||||||
/* If the receiver data is valid the one who handle the data must also remove it. */
|
/* If the receiver data is valid the one who handle the data must also remove it. */
|
||||||
@@ -201,15 +207,29 @@ public:
|
|||||||
|
|
||||||
Object *getAsset() const { return asset; }
|
Object *getAsset() const { return asset; }
|
||||||
Object *getEditingAsset() const { return editingAsset; }
|
Object *getEditingAsset() const { return editingAsset; }
|
||||||
bool isAssetDirt() const { return bEditingAssetDirty; }
|
bool isAssetDirty() const { return bEditingAssetDirty; }
|
||||||
|
|
||||||
ICoreAssetFactory *getOwningFactory() const { return factory; }
|
ICoreAssetFactory *getOwningFactory() const { return factory; }
|
||||||
/* Called when asset editor is about to be closed. */
|
/* Called when asset editor is about to be closed. */
|
||||||
void closeEditor();
|
void closeEditor();
|
||||||
|
|
||||||
void edTick(float deltaTime);
|
void edTick(float deltaTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pushes the message in without overwrite.
|
||||||
|
* Decided to keep using single message per frame rather than queue since we would not need more than that right now.
|
||||||
|
*
|
||||||
|
* @param msgDat Message data
|
||||||
|
* @param msgType Message type
|
||||||
|
*/
|
||||||
void pushMessage(MessageData msgDat, ECoreEdMessageType msgType);
|
void pushMessage(MessageData msgDat, ECoreEdMessageType msgType);
|
||||||
void pushMessage(ECoreEdMessageType msgType);
|
void pushMessage(ECoreEdMessageType msgType);
|
||||||
|
void pushMessageOverwrite(MessageData msgDat, ECoreEdMessageType msgType);
|
||||||
|
/**
|
||||||
|
* @brief Goes through message queue and sends messages. Can be called whenever necessary to empty the msg queue.
|
||||||
|
* Also gets called once every frame.
|
||||||
|
*/
|
||||||
|
void pumpMessages();
|
||||||
|
|
||||||
/* ISelfRegisterReferenceCollector overrides */
|
/* ISelfRegisterReferenceCollector overrides */
|
||||||
void clearReferences(ArrayView<cbe::Object *> deletedObjects) final;
|
void clearReferences(ArrayView<cbe::Object *> deletedObjects) final;
|
||||||
@@ -220,7 +240,7 @@ public:
|
|||||||
/* Interfaces */
|
/* Interfaces */
|
||||||
public:
|
public:
|
||||||
virtual cbe::TransactionsLedger *getTransactionLedger() { return nullptr; }
|
virtual cbe::TransactionsLedger *getTransactionLedger() { return nullptr; }
|
||||||
/* Sends message to the asset editor */
|
/* Sends message to the asset editor. Use this if want to send messages immediately */
|
||||||
virtual MessageResponse sendMessage(MAYBE_UNUSED const MessageData &msgDat, MAYBE_UNUSED ECoreEdMessageType msgType)
|
virtual MessageResponse sendMessage(MAYBE_UNUSED const MessageData &msgDat, MAYBE_UNUSED ECoreEdMessageType msgType)
|
||||||
{
|
{
|
||||||
return MessageResponse{ .state = MessageResponse::NotHandled };
|
return MessageResponse{ .state = MessageResponse::NotHandled };
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date December 2025
|
* \date December 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -267,6 +267,7 @@ class TfGizmoControllableProxy : public ViewportImDebugDrawProxy
|
|||||||
CBE_SIMPLE_RTTI_CLASS(TfGizmoControllableProxy, ViewportImDebugDrawProxy)
|
CBE_SIMPLE_RTTI_CLASS(TfGizmoControllableProxy, ViewportImDebugDrawProxy)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
virtual Transform3D getRelativeTransform() const { return getTransform(); }
|
||||||
/* Absolute in world space */
|
/* Absolute in world space */
|
||||||
virtual Transform3D getTransform() const = 0;
|
virtual Transform3D getTransform() const = 0;
|
||||||
virtual void setTransform(const Transform3D &worldTf) = 0;
|
virtual void setTransform(const Transform3D &worldTf) = 0;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -58,7 +58,7 @@ uint32 ApplicationModule::getThreadingConstraints()
|
|||||||
|
|
||||||
IApplicationModule *IApplicationModule::get()
|
IApplicationModule *IApplicationModule::get()
|
||||||
{
|
{
|
||||||
static WeakModulePtr appModule = ModuleManager::get()->getOrLoadModule(TCHAR("Application"));
|
const static WeakModulePtr appModule = ModuleManager::get()->getOrLoadModule(TCHAR("Application"));
|
||||||
if (appModule.expired())
|
if (appModule.expired())
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -77,22 +77,22 @@ void IApplicationModule::startAndRun(CbeApplication *app, const AppCreateInfo &a
|
|||||||
/* Needs to be parsed asap. Help command line will not reach this point at all. */
|
/* Needs to be parsed asap. Help command line will not reach this point at all. */
|
||||||
if (!ProgramCmdLine::get().parse(app->getCmdLine()))
|
if (!ProgramCmdLine::get().parse(app->getCmdLine()))
|
||||||
{
|
{
|
||||||
LOG_ERROR("Application", "Invalid command line");
|
CBE_LOG_ERROR("Application", "Invalid command line");
|
||||||
ProgramCmdLine::get().printCommandLine();
|
ProgramCmdLine::get().printCommandLine();
|
||||||
}
|
}
|
||||||
ProgramCmdLine::get().setProgramDescription(TCHAR("Cranberry application - ") + appCI.applicationName);
|
ProgramCmdLine::get().setProgramDescription(TCHAR("Cranberry application - ") + appCI.applicationName);
|
||||||
if (ProgramCmdLine::get().hasArg(CMDLINE_PROFILESTARTUP))
|
if (ProgramCmdLine::get().hasArg(CMDLINE_PROFILESTARTUP))
|
||||||
{
|
{
|
||||||
LOG("Application", "Waiting for profiler...");
|
CBE_LOG("Application", "Waiting for profiler...");
|
||||||
|
|
||||||
StopWatch waitTime;
|
StopWatch waitTime;
|
||||||
CBEProfiler::waitForConnection();
|
CBEProfiler::waitForConnection();
|
||||||
waitTime.stop();
|
waitTime.stop();
|
||||||
|
|
||||||
LOG("Application", "Profiler connected in {:.3} secs", waitTime.duration());
|
CBE_LOG("Application", "Profiler connected in {:.3} secs", waitTime.duration());
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::startLoggingTime();
|
cbe::Logger::startLoggingTime();
|
||||||
|
|
||||||
/* Not using stack space as windowing manager and job system tends to become huge and not want to use up much stack space. */
|
/* Not using stack space as windowing manager and job system tends to become huge and not want to use up much stack space. */
|
||||||
static_cast<ApplicationModule *>(this)->app = app;
|
static_cast<ApplicationModule *>(this)->app = app;
|
||||||
@@ -132,40 +132,43 @@ void IApplicationModule::startAndRun(CbeApplication *app, const AppCreateInfo &a
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pre app start initialization */
|
/* Pre-app start initialization */
|
||||||
{
|
{
|
||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("3: App"));
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("3: App"));
|
||||||
|
|
||||||
Logger::flushStream();
|
cbe::Logger::flushStream();
|
||||||
LOG("Application", "{} application start", appCI.applicationName);
|
CBE_LOG("Application", "{} application start", appCI.applicationName);
|
||||||
app->startApp();
|
app->startApp();
|
||||||
}
|
}
|
||||||
/* Post Init */
|
/* Post-Init */
|
||||||
{
|
{
|
||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("4: Post App"));
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("4: Post App"));
|
||||||
|
|
||||||
ModuleManager::get()->progressLoadingStage(ModLoadingStage_PostEngineStart);
|
ModuleManager::get()->progressLoadingStage(ModLoadingStage_PostEngineStart);
|
||||||
Logger::flushStream();
|
cbe::Logger::flushStream();
|
||||||
|
|
||||||
ModuleManager::get()->progressLoadingStage(ModLoadingStage_Last);
|
ModuleManager::get()->progressLoadingStage(ModLoadingStage_Last);
|
||||||
Logger::flushStream();
|
cbe::Logger::flushStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pre app tick initialization */
|
/* Pre-app tick initialization */
|
||||||
app->timeData.tickStart();
|
app->timeData.tickStart();
|
||||||
/* Run the app and wait for it to quit */
|
/* Run the app and wait for it to quit */
|
||||||
app->getJobSystem()->joinMain();
|
app->getJobSystem()->joinMain();
|
||||||
|
|
||||||
LOG("Application", "{} application exit", appCI.applicationName);
|
/* Exit app */
|
||||||
|
{
|
||||||
|
CBE_LOG("Application", "{} application exit", appCI.applicationName);
|
||||||
app->exitApp();
|
app->exitApp();
|
||||||
|
}
|
||||||
|
|
||||||
Logger::flushStream();
|
cbe::Logger::flushStream();
|
||||||
/* Finish all jobs and shutdown */
|
/* Finish all jobs and shutdown */
|
||||||
app->getJobSystem()->shutdown();
|
app->getJobSystem()->shutdown();
|
||||||
delete app->jobSystem;
|
delete app->jobSystem;
|
||||||
|
|
||||||
/* Has to be done after shutdown to complete log flush */
|
/* Has to be done after shutdown to complete log flush */
|
||||||
Logger::stopLoggingTime();
|
cbe::Logger::stopLoggingTime();
|
||||||
|
|
||||||
static_cast<ApplicationModule *>(this)->app = nullptr;
|
static_cast<ApplicationModule *>(this)->app = nullptr;
|
||||||
delete inputSystem;
|
delete inputSystem;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date January 2022
|
* \date January 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -35,13 +35,13 @@ int32 IApplicationModule::startApplication(const AppCreateInfo &appCI)
|
|||||||
CBE_START_PROFILER();
|
CBE_START_PROFILER();
|
||||||
CBE_PROFILER_MESSAGE_LC("Hello Profiler! Cranberry Here!", ColorConst::GREEN);
|
CBE_PROFILER_MESSAGE_LC("Hello Profiler! Cranberry Here!", ColorConst::GREEN);
|
||||||
|
|
||||||
Logger::initialize();
|
cbe::Logger::initialize();
|
||||||
ModuleManager::get()->setAutoLoadEnable(true);
|
ModuleManager::get()->setAutoLoadEnable(true);
|
||||||
|
|
||||||
if (bHelpRun)
|
if (bHelpRun)
|
||||||
{
|
{
|
||||||
/* We do not want to print anything else than help. */
|
/* We do not want to print anything else than help. */
|
||||||
Logger::pushMuteSeverities(Logger::AllServerity);
|
cbe::Logger::pushMuteSeverities(cbe::Logger::AllServerity);
|
||||||
/* Force all the modules to be loaded to register every command line entries. */
|
/* Force all the modules to be loaded to register every command line entries. */
|
||||||
ModuleManager::get()->progressLoadingStage(ModLoadingStage_Manual);
|
ModuleManager::get()->progressLoadingStage(ModLoadingStage_Manual);
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ int32 IApplicationModule::startApplication(const AppCreateInfo &appCI)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_DEBUG("CommandLine", "Command [{}]", appCI.cmdLine);
|
CBE_LOG_DEBUG("CommandLine", "Command [{}]", appCI.cmdLine);
|
||||||
/* App Core */
|
/* App Core */
|
||||||
{
|
{
|
||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("Load Core"));
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("Load Core"));
|
||||||
@@ -78,9 +78,9 @@ int32 IApplicationModule::startApplication(const AppCreateInfo &appCI)
|
|||||||
|
|
||||||
ModuleManager::get()->unloadAll(false);
|
ModuleManager::get()->unloadAll(false);
|
||||||
UnexpectedErrorHandler::getHandler()->unregisterFilter();
|
UnexpectedErrorHandler::getHandler()->unregisterFilter();
|
||||||
Logger::flushStream();
|
cbe::Logger::flushStream();
|
||||||
|
|
||||||
Logger::shutdown();
|
cbe::Logger::shutdown();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -51,7 +51,7 @@ void ApplicationTimeData::tickStart()
|
|||||||
|
|
||||||
initEndTick = Time::timeNow();
|
initEndTick = Time::timeNow();
|
||||||
frameTick = lastFrameTick = initEndTick;
|
frameTick = lastFrameTick = initEndTick;
|
||||||
LOG(CbeApplication::LOG_CATEGORY, "App initialized in {:0.3} seconds", Time::asSeconds(initEndTick - startTick));
|
CBE_LOG(CbeApplication::LOG_CATEGORY, "App initialized in {:0.3} seconds", Time::asSeconds(initEndTick - startTick));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplicationTimeData::progressFrame()
|
void ApplicationTimeData::progressFrame()
|
||||||
@@ -184,7 +184,7 @@ NODISCARD bool CbeApplication::appTick()
|
|||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("Tick"));
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("Tick"));
|
||||||
|
|
||||||
onTick();
|
onTick();
|
||||||
Logger::flushStream();
|
cbe::Logger::flushStream();
|
||||||
|
|
||||||
timeData.progressFrame();
|
timeData.progressFrame();
|
||||||
}
|
}
|
||||||
@@ -198,7 +198,7 @@ void CbeApplication::exitApp()
|
|||||||
clearWidgets();
|
clearWidgets();
|
||||||
onExit();
|
onExit();
|
||||||
|
|
||||||
LOG(LOG_CATEGORY, "{} run time {:.3} minutes", applicationName, Time::asMinutes(Time::timeNow() - timeData.startTick));
|
CBE_LOG(LOG_CATEGORY, "{} run time {:.3} minutes", applicationName, Time::asMinutes(Time::timeNow() - timeData.startTick));
|
||||||
}
|
}
|
||||||
|
|
||||||
static copat::JobSystemFuncAwaiter enqExitApp(CbeApplication *app)
|
static copat::JobSystemFuncAwaiter enqExitApp(CbeApplication *app)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -222,7 +222,7 @@ void CbeApplication::createMainWnd() noexcept
|
|||||||
{ int32(windowBound.minBound.x * dpiScaling), int32(windowBound.minBound.y * dpiScaling) },
|
{ int32(windowBound.minBound.x * dpiScaling), int32(windowBound.minBound.y * dpiScaling) },
|
||||||
{ int32(windowBound.maxBound.x * dpiScaling), int32(windowBound.maxBound.y * dpiScaling) },
|
{ int32(windowBound.maxBound.x * dpiScaling), int32(windowBound.maxBound.y * dpiScaling) },
|
||||||
};
|
};
|
||||||
const WindowCreateInfo mainWndCi{
|
WindowCreateInfo mainWndCi{
|
||||||
.initialName = applicationName.getChar(),
|
.initialName = applicationName.getChar(),
|
||||||
.region = windowBound,
|
.region = windowBound,
|
||||||
.bMaximized = EDITOR_BUILD == 0,
|
.bMaximized = EDITOR_BUILD == 0,
|
||||||
@@ -230,6 +230,11 @@ void CbeApplication::createMainWnd() noexcept
|
|||||||
.bDrawTitlebar = EDITOR_BUILD,
|
.bDrawTitlebar = EDITOR_BUILD,
|
||||||
.bEnableDragDrop = EDITOR_BUILD,
|
.bEnableDragDrop = EDITOR_BUILD,
|
||||||
};
|
};
|
||||||
|
#if DEBUG_BUILD
|
||||||
|
/* Show debug build */
|
||||||
|
const String mainWndName = STR_FORMAT("{}[DEBUG]", applicationName);
|
||||||
|
mainWndCi.initialName = mainWndName.getChar();
|
||||||
|
#endif
|
||||||
windowManager->createWindow(mainWndCi);
|
windowManager->createWindow(mainWndCi);
|
||||||
inputSystem->registerWindow(windowManager->getWindowInfo(windowManager->getMainWindow()));
|
inputSystem->registerWindow(windowManager->getWindowInfo(windowManager->getMainWindow()));
|
||||||
}
|
}
|
||||||
@@ -416,11 +421,11 @@ void CbeApplication::windowExternalDragEnter(CbeWindowHandle wndHndl, Short2 dra
|
|||||||
auto itr = windowWidgets.find(wndHndl);
|
auto itr = windowWidgets.find(wndHndl);
|
||||||
if (itr == windowWidgets.cend())
|
if (itr == windowWidgets.cend())
|
||||||
{
|
{
|
||||||
LOG_WARN(LOG_CATEGORY, "Window not found! Drag enter ignored");
|
CBE_LOG_WARN(LOG_CATEGORY, "Window not found! Drag enter ignored");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Only print first 256 character to keep log short */
|
/* Only print first 256 character to keep log short */
|
||||||
LOG_VERBOSE(LOG_CATEGORY, "External drag entered {}", StringView{ dragData }.substr(0, 256));
|
CBE_LOG_VERBOSE(LOG_CATEGORY, "External drag entered {}", StringView{ dragData }.substr(0, 256));
|
||||||
|
|
||||||
DragInProgress thisDragData{
|
DragInProgress thisDragData{
|
||||||
.dragStart = dragStart,
|
.dragStart = dragStart,
|
||||||
@@ -476,7 +481,7 @@ void CbeApplication::windowExternalDragLeave()
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG(LOG_CATEGORY, "External drag left");
|
CBE_LOG(LOG_CATEGORY, "External drag left");
|
||||||
|
|
||||||
wndIntxnStates.dragsInProgress.erase(itr);
|
wndIntxnStates.dragsInProgress.erase(itr);
|
||||||
debugAssertf(
|
debugAssertf(
|
||||||
@@ -688,7 +693,7 @@ void CbeApplication::tickWindowWidgets() noexcept
|
|||||||
if (overDragResp.state != EInputHandleState::NotHandled && overDragResp.bDragNewPayload)
|
if (overDragResp.state != EInputHandleState::NotHandled && overDragResp.bDragNewPayload)
|
||||||
{
|
{
|
||||||
debugAssert(!bEndDrag);
|
debugAssert(!bEndDrag);
|
||||||
LOG_VERBOSE(LOG_CATEGORY, "Drag payload is updated!");
|
CBE_LOG_VERBOSE(LOG_CATEGORY, "Drag payload is updated!");
|
||||||
itr->payload = overDragResp.payload;
|
itr->payload = overDragResp.payload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -735,7 +740,7 @@ void CbeApplication::tickWindowWidgets() noexcept
|
|||||||
if (enterDragResp.state != EInputHandleState::NotHandled && enterDragResp.bDragNewPayload)
|
if (enterDragResp.state != EInputHandleState::NotHandled && enterDragResp.bDragNewPayload)
|
||||||
{
|
{
|
||||||
debugAssert(!bEndDrag);
|
debugAssert(!bEndDrag);
|
||||||
LOG_VERBOSE(LOG_CATEGORY, "Drag payload is updated!");
|
CBE_LOG_VERBOSE(LOG_CATEGORY, "Drag payload is updated!");
|
||||||
itr->payload = enterDragResp.payload;
|
itr->payload = enterDragResp.payload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -800,7 +805,7 @@ void CbeApplication::tickWindowWidgets() noexcept
|
|||||||
if (enterDragResp.bDragNewPayload)
|
if (enterDragResp.bDragNewPayload)
|
||||||
{
|
{
|
||||||
debugAssert(enterDragResp.state != EInputHandleState::NotHandled);
|
debugAssert(enterDragResp.state != EInputHandleState::NotHandled);
|
||||||
LOG_VERBOSE(LOG_CATEGORY, "Drag payload is updated!");
|
CBE_LOG_VERBOSE(LOG_CATEGORY, "Drag payload is updated!");
|
||||||
startResp.payload = enterDragResp.payload;
|
startResp.payload = enterDragResp.payload;
|
||||||
}
|
}
|
||||||
wndIntxnStates.dragsInProgress.emplace_back(DragInProgress{
|
wndIntxnStates.dragsInProgress.emplace_back(DragInProgress{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date October 2024
|
* \date October 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -15,6 +15,9 @@
|
|||||||
|
|
||||||
namespace cbe
|
namespace cbe
|
||||||
{
|
{
|
||||||
|
/* Yanked from ImGui source */
|
||||||
|
static const float DRAGDROP_HOLD_TO_OPEN_TIMER = 0.70f;
|
||||||
|
|
||||||
bool ImGuiHelpers::isAnyMouseClicked()
|
bool ImGuiHelpers::isAnyMouseClicked()
|
||||||
{
|
{
|
||||||
return ImGui::IsMouseClicked(ImGuiMouseButton_Left) || ImGui::IsMouseClicked(ImGuiMouseButton_Middle)
|
return ImGui::IsMouseClicked(ImGuiMouseButton_Left) || ImGui::IsMouseClicked(ImGuiMouseButton_Middle)
|
||||||
@@ -182,6 +185,21 @@ std::pair<cbe::DragPayloadRef, const ImGuiPayload *> ImGuiHelpers::acceptDragDro
|
|||||||
return { *reinterpret_cast<cbe::DragPayloadRef *>(imguiPayload->Data), imguiPayload };
|
return { *reinterpret_cast<cbe::DragPayloadRef *>(imguiPayload->Data), imguiPayload };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ImGuiHelpers::isItemDragDropPressed()
|
||||||
|
{
|
||||||
|
ImGuiContext &g = *ImGui::GetCurrentContext();
|
||||||
|
if (g.DragDropActive && BIT_NOT_SET(g.DragDropSourceFlags, ImGuiDragDropFlags_SourceNoHoldToOpenOthers)
|
||||||
|
&& ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
|
||||||
|
{
|
||||||
|
ImGui::SetHoveredID(g.LastItemData.ID);
|
||||||
|
if (g.HoveredIdTimer - g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER && g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool ImGuiHelpers::inputText(
|
bool ImGuiHelpers::inputText(
|
||||||
const char *label, String *str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback /*= nullptr*/, void *userData /*= nullptr*/
|
const char *label, String *str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback /*= nullptr*/, void *userData /*= nullptr*/
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date October 2024
|
* \date October 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -58,16 +58,26 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
cbe::DragPayloadRef &payloadRef = *reinterpret_cast<cbe::DragPayloadRef *>(imguiPayload->Data);
|
cbe::DragPayloadRef &payloadRef = *reinterpret_cast<cbe::DragPayloadRef *>(imguiPayload->Data);
|
||||||
|
if constexpr (std::invocable<CondLambda, decltype(payloadRef)>)
|
||||||
|
{
|
||||||
if (!pred(payloadRef))
|
if (!pred(payloadRef))
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<cbe::DragPayloadRef, const ImGuiPayload *> payloadsPair = acceptDragDropPayload(static_cast<const AChar *>(nullptr), flags);
|
std::pair<cbe::DragPayloadRef, const ImGuiPayload *> payloadsPair = acceptDragDropPayload(static_cast<const AChar *>(nullptr), flags);
|
||||||
|
|
||||||
debugAssert(!payloadsPair.first || payloadsPair.first == payloadRef);
|
debugAssert(!payloadsPair.first || payloadsPair.first == payloadRef);
|
||||||
return payloadsPair;
|
return payloadsPair;
|
||||||
}
|
}
|
||||||
|
static std::pair<cbe::DragPayloadRef, const ImGuiPayload *> acceptDragDropPayload(ImGuiDragDropFlags flags = 0)
|
||||||
|
{
|
||||||
|
return acceptDragDropPayload(EmptyType{}, flags);
|
||||||
|
}
|
||||||
|
/* Helper to determine if drag and drop presses the item.
|
||||||
|
* Uses logic obtained from button behavior to find the press on drag and drop */
|
||||||
|
static bool isItemDragDropPressed();
|
||||||
|
|
||||||
template <Box2Dim BoxType>
|
template <Box2Dim BoxType>
|
||||||
static void drawPackedRectangles(
|
static void drawPackedRectangles(
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -205,7 +205,7 @@ void WgImGui::construct(const WgArguments &args)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_ERROR(LOG_CATEGORY, "No font data provided for {}", imguiName);
|
CBE_LOG_ERROR(LOG_CATEGORY, "No font data provided for {}", imguiName);
|
||||||
fontConfig.OversampleH = 3;
|
fontConfig.OversampleH = 3;
|
||||||
fontConfig.OversampleV = 3;
|
fontConfig.OversampleV = 3;
|
||||||
fontConfig.RasterizerMultiply = 2;
|
fontConfig.RasterizerMultiply = 2;
|
||||||
@@ -403,7 +403,7 @@ void WgImGui::drawWidget(ShortRect clipBound, WidgetGeomId thisId, const WidgetG
|
|||||||
const ImDrawCmd &drawCmd = uiCmdList->CmdBuffer[cmdIdx];
|
const ImDrawCmd &drawCmd = uiCmdList->CmdBuffer[cmdIdx];
|
||||||
if (drawCmd.UserCallback != nullptr)
|
if (drawCmd.UserCallback != nullptr)
|
||||||
{
|
{
|
||||||
LOG_WARN(LOG_CATEGORY, "Commands with callback is not supported");
|
CBE_LOG_WARN(LOG_CATEGORY, "Commands with callback is not supported");
|
||||||
debugAssert(drawCmd.UserCallback != nullptr);
|
debugAssert(drawCmd.UserCallback != nullptr);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -190,7 +190,7 @@ bool WindowsKeyboardDevice::sendInRaw(const RAWINPUT &rawInput)
|
|||||||
*/
|
*/
|
||||||
if (rawInput.data.keyboard.VKey == 0xFF)
|
if (rawInput.data.keyboard.VKey == 0xFF)
|
||||||
{
|
{
|
||||||
LOG_WARN(
|
CBE_LOG_WARN(
|
||||||
"WindowsKeyboardDevice", "Possible multibyte key that is not handled properly : {}, Flags : {}", rawInput.data.keyboard.MakeCode,
|
"WindowsKeyboardDevice", "Possible multibyte key that is not handled properly : {}, Flags : {}", rawInput.data.keyboard.MakeCode,
|
||||||
rawInput.data.keyboard.Flags
|
rawInput.data.keyboard.Flags
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -49,7 +49,7 @@ void WindowsInputSystem::registerWindow(const WindowInfo &wndInfo) const
|
|||||||
};
|
};
|
||||||
if (!RegisterRawInputDevices(gamepadDevices.data(), static_cast<uint32>(gamepadDevices.size()), sizeof(decltype(gamepadDevices[0]))))
|
if (!RegisterRawInputDevices(gamepadDevices.data(), static_cast<uint32>(gamepadDevices.size()), sizeof(decltype(gamepadDevices[0]))))
|
||||||
{
|
{
|
||||||
LOG_WARN(LOG_CATEGORY, "Failed registering gamepads for window {}", wndInfo.title);
|
CBE_LOG_WARN(LOG_CATEGORY, "Failed registering gamepads for window {}", wndInfo.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
RAWINPUTDEVICE keyboardDevice;
|
RAWINPUTDEVICE keyboardDevice;
|
||||||
@@ -59,7 +59,7 @@ void WindowsInputSystem::registerWindow(const WindowInfo &wndInfo) const
|
|||||||
keyboardDevice.hwndTarget = (HWND)wndInfo.hndl;
|
keyboardDevice.hwndTarget = (HWND)wndInfo.hndl;
|
||||||
if (!RegisterRawInputDevices(&keyboardDevice, 1, sizeof(decltype(keyboardDevice))))
|
if (!RegisterRawInputDevices(&keyboardDevice, 1, sizeof(decltype(keyboardDevice))))
|
||||||
{
|
{
|
||||||
LOG_WARN(LOG_CATEGORY, "Failed registering keyboard for window {}", wndInfo.title);
|
CBE_LOG_WARN(LOG_CATEGORY, "Failed registering keyboard for window {}", wndInfo.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
RAWINPUTDEVICE mouseDevice;
|
RAWINPUTDEVICE mouseDevice;
|
||||||
@@ -69,7 +69,7 @@ void WindowsInputSystem::registerWindow(const WindowInfo &wndInfo) const
|
|||||||
mouseDevice.hwndTarget = (HWND)wndInfo.hndl;
|
mouseDevice.hwndTarget = (HWND)wndInfo.hndl;
|
||||||
if (!RegisterRawInputDevices(&mouseDevice, 1, sizeof(decltype(mouseDevice))))
|
if (!RegisterRawInputDevices(&mouseDevice, 1, sizeof(decltype(mouseDevice))))
|
||||||
{
|
{
|
||||||
LOG_WARN(LOG_CATEGORY, "Failed registering mouse for window {}", wndInfo.title);
|
CBE_LOG_WARN(LOG_CATEGORY, "Failed registering mouse for window {}", wndInfo.title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ void WindowsInputSystem::updateInputStates()
|
|||||||
int32 currentBlocksNum = ::GetRawInputBuffer(nullptr, &bufferSize, sizeof(RAWINPUTHEADER));
|
int32 currentBlocksNum = ::GetRawInputBuffer(nullptr, &bufferSize, sizeof(RAWINPUTHEADER));
|
||||||
if (currentBlocksNum == -1)
|
if (currentBlocksNum == -1)
|
||||||
{
|
{
|
||||||
LOG_ERROR(LOG_CATEGORY, "Retrieving input buffer size failed");
|
CBE_LOG_ERROR(LOG_CATEGORY, "Retrieving input buffer size failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Must be aligned by sizeof pointer.
|
/* Must be aligned by sizeof pointer.
|
||||||
@@ -114,7 +114,7 @@ void WindowsInputSystem::updateInputStates()
|
|||||||
|
|
||||||
if (currentBlocksNum == -1)
|
if (currentBlocksNum == -1)
|
||||||
{
|
{
|
||||||
LOG_ERROR(LOG_CATEGORY, "Reading buffered raw input failed");
|
CBE_LOG_ERROR(LOG_CATEGORY, "Reading buffered raw input failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ void WindowsInputSystem::updateInputStates()
|
|||||||
|
|
||||||
if (!bProcessed)
|
if (!bProcessed)
|
||||||
{
|
{
|
||||||
LOG_WARN(LOG_CATEGORY, "No device found for processing raw input");
|
CBE_LOG_WARN(LOG_CATEGORY, "No device found for processing raw input");
|
||||||
::DefRawInputProc(&rawInput, 1, sizeof(RAWINPUTHEADER));
|
::DefRawInputProc(&rawInput, 1, sizeof(RAWINPUTHEADER));
|
||||||
}
|
}
|
||||||
using QWORD = uint64;
|
using QWORD = uint64;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -63,7 +63,7 @@ static void fileDialog(HWND hwndOwner, std::variant<cbe::OpenFileDialogInfo, cbe
|
|||||||
const auto [comInitHr, bSuccess] = comInit(false);
|
const auto [comInitHr, bSuccess] = comInit(false);
|
||||||
if (!bSuccess)
|
if (!bSuccess)
|
||||||
{
|
{
|
||||||
LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to setup com threading model!");
|
CBE_LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to setup com threading model!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ static void fileDialog(HWND hwndOwner, std::variant<cbe::OpenFileDialogInfo, cbe
|
|||||||
HRESULT hr = ::CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
|
HRESULT hr = ::CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
|
||||||
if (!SUCCEEDED(hr))
|
if (!SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to create file dialog!");
|
CBE_LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to create file dialog!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,17 +109,17 @@ static void fileDialog(HWND hwndOwner, std::variant<cbe::OpenFileDialogInfo, cbe
|
|||||||
hr = pfd->GetOptions(&dwOptions);
|
hr = pfd->GetOptions(&dwOptions);
|
||||||
if (!SUCCEEDED(hr))
|
if (!SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to get file dialog options!");
|
CBE_LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to get file dialog options!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dwOptions |= optionFlags;
|
dwOptions |= optionFlags;
|
||||||
hr = pfd->SetOptions(dwOptions);
|
hr = pfd->SetOptions(dwOptions);
|
||||||
LOG_ERROR_C(!SUCCEEDED(hr), WindowsWindowingManager::LOG_CATEGORY, "Failed to set file dialog options!");
|
CBE_LOG_ERROR_C(!SUCCEEDED(hr), WindowsWindowingManager::LOG_CATEGORY, "Failed to set file dialog options!");
|
||||||
|
|
||||||
if (title != nullptr)
|
if (title != nullptr)
|
||||||
{
|
{
|
||||||
hr = pfd->SetTitle(TCHAR_TO_WCHAR(title).data());
|
hr = pfd->SetTitle(TCHAR_TO_WCHAR(title).data());
|
||||||
LOG_ERROR_C(!SUCCEEDED(hr), WindowsWindowingManager::LOG_CATEGORY, "Failed to set file dialog title!");
|
CBE_LOG_ERROR_C(!SUCCEEDED(hr), WindowsWindowingManager::LOG_CATEGORY, "Failed to set file dialog title!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bSaving || !std::get<cbe::OpenFileDialogInfo>(info).bAllowFolders)
|
if (bSaving || !std::get<cbe::OpenFileDialogInfo>(info).bAllowFolders)
|
||||||
@@ -154,9 +154,9 @@ static void fileDialog(HWND hwndOwner, std::variant<cbe::OpenFileDialogInfo, cbe
|
|||||||
|
|
||||||
hr = pfd->SetFileTypes(static_cast<uint32>(filterSpecs.size()), filterSpecs.data());
|
hr = pfd->SetFileTypes(static_cast<uint32>(filterSpecs.size()), filterSpecs.data());
|
||||||
}
|
}
|
||||||
LOG_ERROR_C(!SUCCEEDED(hr), WindowsWindowingManager::LOG_CATEGORY, "Failed to set file dialog file types!");
|
CBE_LOG_ERROR_C(!SUCCEEDED(hr), WindowsWindowingManager::LOG_CATEGORY, "Failed to set file dialog file types!");
|
||||||
hr = pfd->SetFileTypeIndex(1);
|
hr = pfd->SetFileTypeIndex(1);
|
||||||
LOG_ERROR_C(!SUCCEEDED(hr), WindowsWindowingManager::LOG_CATEGORY, "Failed to set file dialog file types index!");
|
CBE_LOG_ERROR_C(!SUCCEEDED(hr), WindowsWindowingManager::LOG_CATEGORY, "Failed to set file dialog file types index!");
|
||||||
}
|
}
|
||||||
/* Setup default path */
|
/* Setup default path */
|
||||||
String defPath;
|
String defPath;
|
||||||
@@ -174,7 +174,7 @@ static void fileDialog(HWND hwndOwner, std::variant<cbe::OpenFileDialogInfo, cbe
|
|||||||
if (SUCCEEDED(::SHCreateItemFromParsingName(TCHAR_TO_WCHAR(defPath).data(), nullptr, IID_PPV_ARGS(&defaultPathItem))))
|
if (SUCCEEDED(::SHCreateItemFromParsingName(TCHAR_TO_WCHAR(defPath).data(), nullptr, IID_PPV_ARGS(&defaultPathItem))))
|
||||||
{
|
{
|
||||||
hr = pfd->SetFolder(defaultPathItem);
|
hr = pfd->SetFolder(defaultPathItem);
|
||||||
LOG_ERROR_C(!SUCCEEDED(hr), WindowsWindowingManager::LOG_CATEGORY, "Failed to set file dialog default folder path!");
|
CBE_LOG_ERROR_C(!SUCCEEDED(hr), WindowsWindowingManager::LOG_CATEGORY, "Failed to set file dialog default folder path!");
|
||||||
pfd->SetDefaultFolder(defaultPathItem);
|
pfd->SetDefaultFolder(defaultPathItem);
|
||||||
|
|
||||||
defaultPathItem->Release();
|
defaultPathItem->Release();
|
||||||
@@ -183,12 +183,12 @@ static void fileDialog(HWND hwndOwner, std::variant<cbe::OpenFileDialogInfo, cbe
|
|||||||
hr = pfd->Show(hwndOwner);
|
hr = pfd->Show(hwndOwner);
|
||||||
if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))
|
if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))
|
||||||
{
|
{
|
||||||
LOG_VERBOSE(WindowsWindowingManager::LOG_CATEGORY, "File dialog cancelled!");
|
CBE_LOG_VERBOSE(WindowsWindowingManager::LOG_CATEGORY, "File dialog cancelled!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!SUCCEEDED(hr))
|
if (!SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to show the open file dialog!");
|
CBE_LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to show the open file dialog!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,14 +204,14 @@ static void fileDialog(HWND hwndOwner, std::variant<cbe::OpenFileDialogInfo, cbe
|
|||||||
hr = pfd->GetResult(&pResult);
|
hr = pfd->GetResult(&pResult);
|
||||||
if (!SUCCEEDED(hr))
|
if (!SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to retrieve the result!");
|
CBE_LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to retrieve the result!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
PWSTR pFilePath = nullptr;
|
PWSTR pFilePath = nullptr;
|
||||||
if (!SUCCEEDED(pResult->GetDisplayName(SIGDN_FILESYSPATH, &pFilePath)))
|
if (!SUCCEEDED(pResult->GetDisplayName(SIGDN_FILESYSPATH, &pFilePath)))
|
||||||
{
|
{
|
||||||
LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to retrieve the result file system path!");
|
CBE_LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to retrieve the result file system path!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +265,7 @@ static void fileDialog(HWND hwndOwner, std::variant<cbe::OpenFileDialogInfo, cbe
|
|||||||
while (false);
|
while (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_VERBOSE(WindowsWindowingManager::LOG_CATEGORY, "Save to files {}", filePath);
|
CBE_LOG_VERBOSE(WindowsWindowingManager::LOG_CATEGORY, "Save to files {}", filePath);
|
||||||
/* Right now not doing multithreading so direct call is fine. */
|
/* Right now not doing multithreading so direct call is fine. */
|
||||||
std::get<cbe::SaveFileDialogInfo>(info).callback(filterIdx, filePath);
|
std::get<cbe::SaveFileDialogInfo>(info).callback(filterIdx, filePath);
|
||||||
}
|
}
|
||||||
@@ -274,13 +274,13 @@ static void fileDialog(HWND hwndOwner, std::variant<cbe::OpenFileDialogInfo, cbe
|
|||||||
hr = pfd->GetResults(&pResults);
|
hr = pfd->GetResults(&pResults);
|
||||||
if (!SUCCEEDED(hr))
|
if (!SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to retrieve the results!");
|
CBE_LOG_ERROR(WindowsWindowingManager::LOG_CATEGORY, "Failed to retrieve the results!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
dword count = 0;
|
dword count = 0;
|
||||||
pResults->GetCount(&count);
|
pResults->GetCount(&count);
|
||||||
LOG_VERBOSE(WindowsWindowingManager::LOG_CATEGORY, "Selected {} items", count);
|
CBE_LOG_VERBOSE(WindowsWindowingManager::LOG_CATEGORY, "Selected {} items", count);
|
||||||
selected.reserve(count);
|
selected.reserve(count);
|
||||||
|
|
||||||
for (DWORD i = 0; i < count; i++)
|
for (DWORD i = 0; i < count; i++)
|
||||||
@@ -298,7 +298,7 @@ static void fileDialog(HWND hwndOwner, std::variant<cbe::OpenFileDialogInfo, cbe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_VERBOSE(WindowsWindowingManager::LOG_CATEGORY, "Selected files {}", selected);
|
CBE_LOG_VERBOSE(WindowsWindowingManager::LOG_CATEGORY, "Selected files {}", selected);
|
||||||
|
|
||||||
/* Right now not doing multithreading so direct call is fine. */
|
/* Right now not doing multithreading so direct call is fine. */
|
||||||
std::get<cbe::OpenFileDialogInfo>(info).callback(selected);
|
std::get<cbe::OpenFileDialogInfo>(info).callback(selected);
|
||||||
@@ -425,14 +425,14 @@ WindowsWindowingManager::WindowsWindowingManager(InstanceHandle instHndl)
|
|||||||
const bool bSetDpiAwarness = !!::SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
const bool bSetDpiAwarness = !!::SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||||
if (!bSetDpiAwarness)
|
if (!bSetDpiAwarness)
|
||||||
{
|
{
|
||||||
LOG_WARN(LOG_CATEGORY, "DPI awareness setup failed");
|
CBE_LOG_WARN(LOG_CATEGORY, "DPI awareness setup failed");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG(LOG_CATEGORY, "Set per monitor dpi awareness");
|
CBE_LOG(LOG_CATEGORY, "Set per monitor dpi awareness");
|
||||||
}
|
}
|
||||||
/* Refreshing monitors again to get updated monitor information */
|
/* Refreshing monitors again to get updated monitor information */
|
||||||
LOG(LOG_CATEGORY, "Refresh moniotors post set per monitor dpi awareness");
|
CBE_LOG(LOG_CATEGORY, "Refresh moniotors post set per monitor dpi awareness");
|
||||||
refreshMonitors(true);
|
refreshMonitors(true);
|
||||||
printDisplayDevices();
|
printDisplayDevices();
|
||||||
}
|
}
|
||||||
@@ -457,7 +457,7 @@ void WindowsWindowingManager::printDisplayDevices()
|
|||||||
for (int i = 0; (!!::EnumDisplayDevices(NULL, i, &dispDevice, 0)); i++)
|
for (int i = 0; (!!::EnumDisplayDevices(NULL, i, &dispDevice, 0)); i++)
|
||||||
{
|
{
|
||||||
// clang-format off
|
// clang-format off
|
||||||
LOG(LOG_CATEGORY, "Display device #{}: \n\tname: {} \n\tID: {} \n\treadable name: {} \n\tStates: {}{}{}{}{}", i,
|
CBE_LOG(LOG_CATEGORY, "Display device #{}: \n\tname: {} \n\tID: {} \n\treadable name: {} \n\tStates: {}{}{}{}{}", i,
|
||||||
UTF8_TO_TCHAR(dispDevice.DeviceName),
|
UTF8_TO_TCHAR(dispDevice.DeviceName),
|
||||||
UTF8_TO_TCHAR(dispDevice.DeviceID),
|
UTF8_TO_TCHAR(dispDevice.DeviceID),
|
||||||
UTF8_TO_TCHAR(dispDevice.DeviceString),
|
UTF8_TO_TCHAR(dispDevice.DeviceString),
|
||||||
@@ -524,7 +524,7 @@ void WindowsWindowingManager::dpiChanged(CbeWindowHandle hndl, uint16 newDpi, IR
|
|||||||
WindowInfo &wndInfo = windows[hndl];
|
WindowInfo &wndInfo = windows[hndl];
|
||||||
|
|
||||||
const float dpiScaling = windowsDpiToEngineDpi(newDpi);
|
const float dpiScaling = windowsDpiToEngineDpi(newDpi);
|
||||||
LOG(LOG_CATEGORY, "Window {} dpi changed to {}", wndInfo.title, dpiScaling);
|
CBE_LOG(LOG_CATEGORY, "Window {} dpi changed to {}", wndInfo.title, dpiScaling);
|
||||||
|
|
||||||
wndInfo.dpiScaling = dpiScaling;
|
wndInfo.dpiScaling = dpiScaling;
|
||||||
::SetWindowPos(
|
::SetWindowPos(
|
||||||
@@ -609,7 +609,7 @@ void WindowsWindowingManager::refreshMonitors(bool bLogMonitors)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_C(
|
CBE_LOG_C(
|
||||||
bLogMonitors, LOG_CATEGORY,
|
bLogMonitors, LOG_CATEGORY,
|
||||||
"Monitor name: {}[{}] \n\tID: {} \n\tConnected to: {}, ID: {}, Name: {} \n\tIs primary: {} \n\tRegion :[{}, {}] to [{}, {}] "
|
"Monitor name: {}[{}] \n\tID: {} \n\tConnected to: {}, ID: {}, Name: {} \n\tIs primary: {} \n\tRegion :[{}, {}] to [{}, {}] "
|
||||||
"\n\tWork region :[{}, {}] to [{}, {}]\n\tMonitor resolution :{}x{}\n\tRefresh freq :{}Hz\n\tDPI Scaling :{}",
|
"\n\tWork region :[{}, {}] to [{}, {}]\n\tMonitor resolution :{}x{}\n\tRefresh freq :{}Hz\n\tDPI Scaling :{}",
|
||||||
@@ -666,7 +666,7 @@ CbeWindowHandle WindowsWindowingManager::platformCreateWindow(CbeWindowHandle th
|
|||||||
|
|
||||||
if (wndInfo.hndl == nullptr)
|
if (wndInfo.hndl == nullptr)
|
||||||
{
|
{
|
||||||
LOG_ERROR(LOG_CATEGORY, "Failed creating window {}, Error code {}", windowCi.initialName, GetLastError());
|
CBE_LOG_ERROR(LOG_CATEGORY, "Failed creating window {}, Error code {}", windowCi.initialName, GetLastError());
|
||||||
return WindowTree::InvalidIdx;
|
return WindowTree::InvalidIdx;
|
||||||
}
|
}
|
||||||
/* Since dpi awareness questions the monitor list's legitimacy */
|
/* Since dpi awareness questions the monitor list's legitimacy */
|
||||||
@@ -679,7 +679,7 @@ CbeWindowHandle WindowsWindowingManager::platformCreateWindow(CbeWindowHandle th
|
|||||||
if (windowCi.bEnableDragDrop)
|
if (windowCi.bEnableDragDrop)
|
||||||
{
|
{
|
||||||
const HRESULT dragDropRes = ::RegisterDragDrop(wndInfo.hndl, this);
|
const HRESULT dragDropRes = ::RegisterDragDrop(wndInfo.hndl, this);
|
||||||
LOG_WARN_C(!SUCCEEDED(dragDropRes), LOG_CATEGORY, "Drag drop registration failed for window {}!", windowCi.initialName);
|
CBE_LOG_WARN_C(!SUCCEEDED(dragDropRes), LOG_CATEGORY, "Drag drop registration failed for window {}!", windowCi.initialName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return thisWndHndl;
|
return thisWndHndl;
|
||||||
@@ -851,8 +851,10 @@ BOOL WindowsCallbackProcs::monitorEnum(HMONITOR monitor, HDC devCntx, LPRECT mon
|
|||||||
::MONITORINFOEX info;
|
::MONITORINFOEX info;
|
||||||
info.cbSize = sizeof(MONITORINFOEX);
|
info.cbSize = sizeof(MONITORINFOEX);
|
||||||
|
|
||||||
LOG(WindowsWindowingManager::LOG_CATEGORY, "Monitor callback received! Monitor region [{}, {}] to [{}, {}]", monitorRegion->left,
|
CBE_LOG(
|
||||||
monitorRegion->top, monitorRegion->right, monitorRegion->bottom);
|
WindowsWindowingManager::LOG_CATEGORY, "Monitor callback received! Monitor region [{}, {}] to [{}, {}]", monitorRegion->left,
|
||||||
|
monitorRegion->top, monitorRegion->right, monitorRegion->bottom
|
||||||
|
);
|
||||||
if (::GetMonitorInfo(monitor, &info))
|
if (::GetMonitorInfo(monitor, &info))
|
||||||
{
|
{
|
||||||
MonitorInfo &mInfo = monitors.emplace_back();
|
MonitorInfo &mInfo = monitors.emplace_back();
|
||||||
@@ -916,24 +918,24 @@ LRESULT WindowsCallbackProcs::windowMsgs(HWND hwnd, UINT uMsg, WPARAM wParam, LP
|
|||||||
const uint64 userData = cbeHndl;
|
const uint64 userData = cbeHndl;
|
||||||
::SetWindowLongPtr(hwnd, GWLP_USERDATA, std::bit_cast<int64>(userData));
|
::SetWindowLongPtr(hwnd, GWLP_USERDATA, std::bit_cast<int64>(userData));
|
||||||
|
|
||||||
LOG(WindowingManager::LOG_CATEGORY, "Created window {}", wndManager->getWindowInfo(cbeHndl).title);
|
CBE_LOG(WindowingManager::LOG_CATEGORY, "Created window {}", wndManager->getWindowInfo(cbeHndl).title);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case WM_DESTROY:
|
case WM_DESTROY:
|
||||||
{
|
{
|
||||||
if (wndManager->isValidWnd(cbeHndl))
|
if (wndManager->isValidWnd(cbeHndl))
|
||||||
{
|
{
|
||||||
LOG(WindowingManager::LOG_CATEGORY, "Destroying window {}", wndManager->getWindowInfo(cbeHndl).title);
|
CBE_LOG(WindowingManager::LOG_CATEGORY, "Destroying window {}", wndManager->getWindowInfo(cbeHndl).title);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG(WindowingManager::LOG_CATEGORY, "Destroying window");
|
CBE_LOG(WindowingManager::LOG_CATEGORY, "Destroying window");
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case WM_CLOSE:
|
case WM_CLOSE:
|
||||||
{
|
{
|
||||||
LOG(WindowingManager::LOG_CATEGORY, "Quiting window {}", wndManager->getWindowInfo(cbeHndl).title);
|
CBE_LOG(WindowingManager::LOG_CATEGORY, "Quiting window {}", wndManager->getWindowInfo(cbeHndl).title);
|
||||||
app->destroyWindow(cbeHndl);
|
app->destroyWindow(cbeHndl);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -943,11 +945,11 @@ LRESULT WindowsCallbackProcs::windowMsgs(HWND hwnd, UINT uMsg, WPARAM wParam, LP
|
|||||||
{
|
{
|
||||||
if (wParam == TRUE)
|
if (wParam == TRUE)
|
||||||
{
|
{
|
||||||
LOG(WindowingManager::LOG_CATEGORY, "Enabled window {}", wndManager->getWindowInfo(cbeHndl).title);
|
CBE_LOG(WindowingManager::LOG_CATEGORY, "Enabled window {}", wndManager->getWindowInfo(cbeHndl).title);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG(WindowingManager::LOG_CATEGORY, "Disabled window {}", wndManager->getWindowInfo(cbeHndl).title);
|
CBE_LOG(WindowingManager::LOG_CATEGORY, "Disabled window {}", wndManager->getWindowInfo(cbeHndl).title);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -959,12 +961,12 @@ LRESULT WindowsCallbackProcs::windowMsgs(HWND hwnd, UINT uMsg, WPARAM wParam, LP
|
|||||||
{
|
{
|
||||||
if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE)
|
if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE)
|
||||||
{
|
{
|
||||||
LOG(WindowingManager::LOG_CATEGORY, "Activating window {}", wndManager->getWindowInfo(cbeHndl).title);
|
CBE_LOG(WindowingManager::LOG_CATEGORY, "Activating window {}", wndManager->getWindowInfo(cbeHndl).title);
|
||||||
static_cast<WindowsWindowingManager *>(wndManager)->activateWindow(cbeHndl);
|
static_cast<WindowsWindowingManager *>(wndManager)->activateWindow(cbeHndl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG(WindowingManager::LOG_CATEGORY, "Deactivating window {}", wndManager->getWindowInfo(cbeHndl).title);
|
CBE_LOG(WindowingManager::LOG_CATEGORY, "Deactivating window {}", wndManager->getWindowInfo(cbeHndl).title);
|
||||||
static_cast<WindowsWindowingManager *>(wndManager)->deactivateWindow(cbeHndl);
|
static_cast<WindowsWindowingManager *>(wndManager)->deactivateWindow(cbeHndl);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1002,7 +1004,9 @@ LRESULT WindowsCallbackProcs::windowMsgs(HWND hwnd, UINT uMsg, WPARAM wParam, LP
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(WindowingManager::LOG_CATEGORY, "Window {} resized {}x{} -> {}x{}", wndInfo.title, oldSize.x, oldSize.y, newSize.x, newSize.y);
|
CBE_LOG(
|
||||||
|
WindowingManager::LOG_CATEGORY, "Window {} resized {}x{} -> {}x{}", wndInfo.title, oldSize.x, oldSize.y, newSize.x, newSize.y
|
||||||
|
);
|
||||||
if ((wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED) && LOWORD(lParam) > 0 && HIWORD(lParam) > 0)
|
if ((wParam == SIZE_MAXIMIZED || wParam == SIZE_RESTORED) && LOWORD(lParam) > 0 && HIWORD(lParam) > 0)
|
||||||
{
|
{
|
||||||
wndInfo.deferredMsgs[cbe_app_wnd_msgs::Resize].emplace<cbe_app_wnd_msgs::WndResize>(oldSize, newSize);
|
wndInfo.deferredMsgs[cbe_app_wnd_msgs::Resize].emplace<cbe_app_wnd_msgs::WndResize>(oldSize, newSize);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date November 2024
|
* \date November 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -19,11 +19,13 @@ void CbeAudioModule::init()
|
|||||||
const std::vector devs = getDevices();
|
const std::vector devs = getDevices();
|
||||||
for (const auto &dev : devs)
|
for (const auto &dev : devs)
|
||||||
{
|
{
|
||||||
LOG(LOG_CATEGORY, "Audio device found {}[Is Default: {}]", dev.name, dev.bIsDefault);
|
CBE_LOG(LOG_CATEGORY, "Audio device found {}[Is Default: {}]", dev.name, dev.bIsDefault);
|
||||||
for (const auto &devProp : dev.properties)
|
for (const auto &devProp : dev.properties)
|
||||||
{
|
{
|
||||||
LOG(LOG_CATEGORY, " Sample Rate: {}, Data format {}, Channels: {}, Exclusive access: {}", devProp.sampleRate,
|
CBE_LOG(
|
||||||
static_cast<uint32>(devProp.fmt), devProp.numChannels, devProp.bHasExclusiveAccess);
|
LOG_CATEGORY, " Sample Rate: {}, Data format {}, Channels: {}, Exclusive access: {}", devProp.sampleRate,
|
||||||
|
static_cast<uint32>(devProp.fmt), devProp.numChannels, devProp.bHasExclusiveAccess
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date November 2024
|
* \date November 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -449,7 +449,7 @@ void MiniAudioBackendContext::copyAudioInst(
|
|||||||
if (result != MA_SUCCESS)
|
if (result != MA_SUCCESS)
|
||||||
{
|
{
|
||||||
deinitAudioPlayerDataStream(dstDataSrc.stream, framesCount);
|
deinitAudioPlayerDataStream(dstDataSrc.stream, framesCount);
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_audio_buffer_ref from engine audio data stream");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_audio_buffer_ref from engine audio data stream");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -458,7 +458,7 @@ void MiniAudioBackendContext::copyAudioInst(
|
|||||||
ma_result result = ma_audio_buffer_ref_init(format, numOfChannels, framesData, framesCount, &dstDataSrc.audioRef);
|
ma_result result = ma_audio_buffer_ref_init(format, numOfChannels, framesData, framesCount, &dstDataSrc.audioRef);
|
||||||
if (result != MA_SUCCESS)
|
if (result != MA_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_audio_buffer_ref from engine audio data source");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_audio_buffer_ref from engine audio data source");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dstDataSrc.audioRef.sampleRate = sampleRate;
|
dstDataSrc.audioRef.sampleRate = sampleRate;
|
||||||
@@ -468,7 +468,7 @@ void MiniAudioBackendContext::copyAudioInst(
|
|||||||
ma_result result = ma_sound_init_ex(&dstEng.engine, &soundCfg, &dstDataSrc.sound);
|
ma_result result = ma_sound_init_ex(&dstEng.engine, &soundCfg, &dstDataSrc.sound);
|
||||||
if (result != MA_SUCCESS)
|
if (result != MA_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_sound!");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_sound!");
|
||||||
ma_audio_buffer_ref_uninit(&dstDataSrc.audioRef);
|
ma_audio_buffer_ref_uninit(&dstDataSrc.audioRef);
|
||||||
if (dstDataSrc.bStreaming)
|
if (dstDataSrc.bStreaming)
|
||||||
{
|
{
|
||||||
@@ -534,7 +534,7 @@ bool MiniAudioBackendContext::setSampleRateImpl(uint32 newSampleRate)
|
|||||||
{
|
{
|
||||||
if (dev.audioDevData)
|
if (dev.audioDevData)
|
||||||
{
|
{
|
||||||
LOG_WARN(ICbeAudioModule::LOG_CATEGORY, "Cannot change sample rate after engine/devices are created!");
|
CBE_LOG_WARN(ICbeAudioModule::LOG_CATEGORY, "Cannot change sample rate after engine/devices are created!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -709,7 +709,7 @@ cbe::audio::DecodedOutput MiniAudioBackendContext::decodeAudioInfoImpl(cbe::audi
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to retrieve the audio info from data!");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to retrieve the audio info from data!");
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@@ -739,7 +739,7 @@ cbe::audio::DecodedOutput MiniAudioBackendContext::decodeAudioImpl(cbe::audio::D
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to decode the audio data!");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to decode the audio data!");
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@@ -753,7 +753,7 @@ cbe::audio::EncodedOutput MiniAudioBackendContext::encodeAudioImpl(cbe::audio::E
|
|||||||
encodeCfg.allocationCallbacks = localAllocCallbacks;
|
encodeCfg.allocationCallbacks = localAllocCallbacks;
|
||||||
if (encodeInfo.encoding != cbe::audio::EDataEncoding::Wav)
|
if (encodeInfo.encoding != cbe::audio::EDataEncoding::Wav)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Only WAV encoding is supported right now!");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Only WAV encoding is supported right now!");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -801,7 +801,7 @@ cbe::audio::EncodedOutput MiniAudioBackendContext::encodeAudioImpl(cbe::audio::E
|
|||||||
ma_result result = ma_encoder_init(EncoderLocalData::write, EncoderLocalData::seek, &encoderData, &encodeCfg, &encoder);
|
ma_result result = ma_encoder_init(EncoderLocalData::write, EncoderLocalData::seek, &encoderData, &encodeCfg, &encoder);
|
||||||
if (result != MA_SUCCESS)
|
if (result != MA_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create audio encoder!");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create audio encoder!");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -811,13 +811,13 @@ cbe::audio::EncodedOutput MiniAudioBackendContext::encodeAudioImpl(cbe::audio::E
|
|||||||
ma_encoder_uninit(&encoder);
|
ma_encoder_uninit(&encoder);
|
||||||
if (result != MA_SUCCESS)
|
if (result != MA_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to encode the frames to audio data!");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to encode the frames to audio data!");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (framesWritten != inFramesCount)
|
if (framesWritten != inFramesCount)
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbeAudioModule::LOG_CATEGORY, "Frames written {} does not match the expected frames count {}!", framesWritten, inFramesCount
|
ICbeAudioModule::LOG_CATEGORY, "Frames written {} does not match the expected frames count {}!", framesWritten, inFramesCount
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -844,8 +844,10 @@ cbe::audio::EngineId MiniAudioBackendContext::createUserEngineImpl(cbe::audio::L
|
|||||||
auto [_, devIdx] = deviceIdToIndex(ci.device);
|
auto [_, devIdx] = deviceIdToIndex(ci.device);
|
||||||
if (devices[devIdx].audioDevData->userEngIdx < CBE_AUDIO_MAX_LOCAL_USER)
|
if (devices[devIdx].audioDevData->userEngIdx < CBE_AUDIO_MAX_LOCAL_USER)
|
||||||
{
|
{
|
||||||
LOG(ICbeAudioModule::LOG_CATEGORY, "Cannot create user engine with already linked audio device {}!",
|
CBE_LOG(
|
||||||
devicesInfo[devices[devIdx].devIdx].devInfo.name);
|
ICbeAudioModule::LOG_CATEGORY, "Cannot create user engine with already linked audio device {}!",
|
||||||
|
devicesInfo[devices[devIdx].devIdx].devInfo.name
|
||||||
|
);
|
||||||
return retId;
|
return retId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -875,7 +877,7 @@ cbe::audio::EngineId MiniAudioBackendContext::createUserEngineImpl(cbe::audio::L
|
|||||||
{
|
{
|
||||||
if (result != MA_SUCCESS)
|
if (result != MA_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbeAudioModule::LOG_CATEGORY, "Failed to create engine using device {} for local user index {}",
|
ICbeAudioModule::LOG_CATEGORY, "Failed to create engine using device {} for local user index {}",
|
||||||
devicesInfo[devIdx].devInfo.name, ci.localUserIdx
|
devicesInfo[devIdx].devInfo.name, ci.localUserIdx
|
||||||
);
|
);
|
||||||
@@ -961,8 +963,10 @@ bool MiniAudioBackendContext::switchUserEngineDeviceImpl(cbe::audio::EngineId en
|
|||||||
auto [_, devIdx] = deviceIdToIndex(deviceId);
|
auto [_, devIdx] = deviceIdToIndex(deviceId);
|
||||||
if (devices[devIdx].audioDevData->userEngIdx < CBE_AUDIO_MAX_LOCAL_USER)
|
if (devices[devIdx].audioDevData->userEngIdx < CBE_AUDIO_MAX_LOCAL_USER)
|
||||||
{
|
{
|
||||||
LOG(ICbeAudioModule::LOG_CATEGORY, "Cannot create user engine with already linked audio device {}!",
|
CBE_LOG(
|
||||||
devicesInfo[devices[devIdx].devIdx].devInfo.name);
|
ICbeAudioModule::LOG_CATEGORY, "Cannot create user engine with already linked audio device {}!",
|
||||||
|
devicesInfo[devices[devIdx].devIdx].devInfo.name
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1386,7 +1390,7 @@ bool MiniAudioBackendContext::createAudioPlayerFromDataSrc(
|
|||||||
);
|
);
|
||||||
if (result != MA_SUCCESS)
|
if (result != MA_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_audio_buffer_ref from engine audio data source");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_audio_buffer_ref from engine audio data source");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
playerRef.bStreaming = false;
|
playerRef.bStreaming = false;
|
||||||
@@ -1433,7 +1437,7 @@ bool MiniAudioBackendContext::createAudioPlayerFromDataSrc(
|
|||||||
if (result != MA_SUCCESS)
|
if (result != MA_SUCCESS)
|
||||||
{
|
{
|
||||||
deinitAudioPlayerDataStream(playerRef.stream, framesCount);
|
deinitAudioPlayerDataStream(playerRef.stream, framesCount);
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_audio_buffer_ref from engine audio data stream");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_audio_buffer_ref from engine audio data stream");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1489,7 +1493,7 @@ bool MiniAudioBackendContext::createAudioPlayerFromDataSrc(
|
|||||||
ma_result result = ma_sound_init_ex(&eng.engine, &soundCfg, &playerRef.sound);
|
ma_result result = ma_sound_init_ex(&eng.engine, &soundCfg, &playerRef.sound);
|
||||||
if (result != MA_SUCCESS)
|
if (result != MA_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_sound!");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_sound!");
|
||||||
ma_audio_buffer_ref_uninit(&playerRef.audioRef);
|
ma_audio_buffer_ref_uninit(&playerRef.audioRef);
|
||||||
if (playerRef.bStreaming)
|
if (playerRef.bStreaming)
|
||||||
{
|
{
|
||||||
@@ -1547,20 +1551,20 @@ bool MiniAudioBackendContext::initAudioPlayerDataStream(
|
|||||||
dataStream.pageBackingFile = PlatformMemory::openPhysicalMemorySection(bufferingBlockSizeInBytes);
|
dataStream.pageBackingFile = PlatformMemory::openPhysicalMemorySection(bufferingBlockSizeInBytes);
|
||||||
if (dataStream.pageBackingFile == nullptr)
|
if (dataStream.pageBackingFile == nullptr)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to allocate system page backing file!");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to allocate system page backing file!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Need virtual address space mappable to each audio page chunk */
|
/* Need virtual address space mappable to each audio page chunk */
|
||||||
dataStream.pcmFramesAddrs = PlatformMemory::virtualAllocUnmapped(bufferingBlockSizeInBytes, totalMappableBlocks);
|
dataStream.pcmFramesAddrs = PlatformMemory::virtualAllocUnmapped(bufferingBlockSizeInBytes, totalMappableBlocks);
|
||||||
if (dataStream.pcmFramesAddrs == nullptr)
|
if (dataStream.pcmFramesAddrs == nullptr)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to allocate PCM frames read virual addresses!");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to allocate PCM frames read virual addresses!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dataStream.pagesAddr = PlatformMemory::virtualAllocUnmapped(bufferingBlockSizeInBytes, 1);
|
dataStream.pagesAddr = PlatformMemory::virtualAllocUnmapped(bufferingBlockSizeInBytes, 1);
|
||||||
if (dataStream.pcmFramesAddrs == nullptr)
|
if (dataStream.pcmFramesAddrs == nullptr)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to allocate PCM frames write virual addresses!");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to allocate PCM frames write virual addresses!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Now map the virtual addresses */
|
/* Now map the virtual addresses */
|
||||||
@@ -1572,7 +1576,7 @@ bool MiniAudioBackendContext::initAudioPlayerDataStream(
|
|||||||
true
|
true
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbeAudioModule::LOG_CATEGORY, "Failed to map section to PCM read frames at {}(offset {})!", mappedFrameAddrNum,
|
ICbeAudioModule::LOG_CATEGORY, "Failed to map section to PCM read frames at {}(offset {})!", mappedFrameAddrNum,
|
||||||
bufferingBlockSizeInBytes * mappedFrameAddrNum
|
bufferingBlockSizeInBytes * mappedFrameAddrNum
|
||||||
);
|
);
|
||||||
@@ -1585,7 +1589,7 @@ bool MiniAudioBackendContext::initAudioPlayerDataStream(
|
|||||||
}
|
}
|
||||||
if (!PlatformMemory::mapPhysicalMemorySection(dataStream.pageBackingFile, 0, dataStream.pagesAddr, bufferingBlockSizeInBytes, false))
|
if (!PlatformMemory::mapPhysicalMemorySection(dataStream.pageBackingFile, 0, dataStream.pagesAddr, bufferingBlockSizeInBytes, false))
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to map section at page idx to PCM write frames!");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to map section at page idx to PCM write frames!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1595,7 +1599,7 @@ bool MiniAudioBackendContext::initAudioPlayerDataStream(
|
|||||||
ma_result result = ma_decoder_init_vfs(&dataStream.maVfs, dataStream.filePath, &decodeCfg, &dataStream.decoder);
|
ma_result result = ma_decoder_init_vfs(&dataStream.maVfs, dataStream.filePath, &decodeCfg, &dataStream.decoder);
|
||||||
if (result != MA_SUCCESS)
|
if (result != MA_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create stream decoder!");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create stream decoder!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1706,7 +1710,7 @@ bool MiniAudioBackendContext::createAudioPlayerInstancesImpl(
|
|||||||
|
|
||||||
if (!isValidUserEngine(engIdxStart))
|
if (!isValidUserEngine(engIdxStart))
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbeAudioModule::LOG_CATEGORY, "Engine not initialized. At least one engine associated with a device is required to play sound!"
|
ICbeAudioModule::LOG_CATEGORY, "Engine not initialized. At least one engine associated with a device is required to play sound!"
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
@@ -1799,7 +1803,7 @@ bool MiniAudioBackendContext::destroyAudioPlayerInstancesImpl(ArrayView<cbe::aud
|
|||||||
|
|
||||||
if (!isValidUserEngine(firstEngIdx))
|
if (!isValidUserEngine(firstEngIdx))
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Engine not initialized. At least one engine associated with a device is required!");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Engine not initialized. At least one engine associated with a device is required!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const SizeT engCountToCheck = static_cast<SizeT>(CBE_AUDIO_MAX_LOCAL_USER - firstEngIdx);
|
const SizeT engCountToCheck = static_cast<SizeT>(CBE_AUDIO_MAX_LOCAL_USER - firstEngIdx);
|
||||||
@@ -2068,7 +2072,7 @@ bool MiniAudioBackendContext::initSoundGroup(MaEngineSoundGroup &engSoundGrp, Ma
|
|||||||
ma_result result = ma_sound_group_init_ex(&eng.engine, &soundGrpCfg, &engSoundGrp.soundGrp);
|
ma_result result = ma_sound_group_init_ex(&eng.engine, &soundGrpCfg, &engSoundGrp.soundGrp);
|
||||||
if (result != MA_SUCCESS)
|
if (result != MA_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_sound_group for engine");
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "Failed to create ma_sound_group for engine");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3194,7 +3198,7 @@ void MiniAudioBackendContext::destroyLocalDevice(const LocalDevice &dev) const
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_WARN_C(
|
CBE_LOG_WARN_C(
|
||||||
isValidUserEngine(dev.audioDevData->userEngIdx), ICbeAudioModule::LOG_CATEGORY,
|
isValidUserEngine(dev.audioDevData->userEngIdx), ICbeAudioModule::LOG_CATEGORY,
|
||||||
"All user engines({}) using device {} must be cleared before destroying device!", dev.audioDevData->userEngIdx,
|
"All user engines({}) using device {} must be cleared before destroying device!", dev.audioDevData->userEngIdx,
|
||||||
devicesInfo[dev.devIdx].devInfo.name
|
devicesInfo[dev.devIdx].devInfo.name
|
||||||
@@ -3336,17 +3340,17 @@ void MiniAudioBackendContext::miniaudioLog(void *pUserData, ma_uint32 level, con
|
|||||||
switch (level)
|
switch (level)
|
||||||
{
|
{
|
||||||
case MA_LOG_LEVEL_DEBUG:
|
case MA_LOG_LEVEL_DEBUG:
|
||||||
LOG_DEBUG(ICbeAudioModule::LOG_CATEGORY, "{}", msg);
|
CBE_LOG_DEBUG(ICbeAudioModule::LOG_CATEGORY, "{}", msg);
|
||||||
break;
|
break;
|
||||||
case MA_LOG_LEVEL_INFO:
|
case MA_LOG_LEVEL_INFO:
|
||||||
LOG(ICbeAudioModule::LOG_CATEGORY, "{}", msg);
|
CBE_LOG(ICbeAudioModule::LOG_CATEGORY, "{}", msg);
|
||||||
break;
|
break;
|
||||||
case MA_LOG_LEVEL_WARNING:
|
case MA_LOG_LEVEL_WARNING:
|
||||||
LOG_WARN(ICbeAudioModule::LOG_CATEGORY, "{}", msg);
|
CBE_LOG_WARN(ICbeAudioModule::LOG_CATEGORY, "{}", msg);
|
||||||
break;
|
break;
|
||||||
case MA_LOG_LEVEL_ERROR:
|
case MA_LOG_LEVEL_ERROR:
|
||||||
default:
|
default:
|
||||||
LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "{}", msg);
|
CBE_LOG_ERROR(ICbeAudioModule::LOG_CATEGORY, "{}", msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date November 2024
|
* \date November 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -160,7 +160,7 @@ private:
|
|||||||
{
|
{
|
||||||
ma_device device;
|
ma_device device;
|
||||||
/* Connects the device to engine index this device received input from */
|
/* Connects the device to engine index this device received input from */
|
||||||
CBESpinLock dataLock;
|
cbe::SpinLock dataLock;
|
||||||
/* User engine always maps 1:1 to Device */
|
/* User engine always maps 1:1 to Device */
|
||||||
cbe::audio::EngineId userEngIdx = cbe::audio::INVALID_ENGINE_ID;
|
cbe::audio::EngineId userEngIdx = cbe::audio::INVALID_ENGINE_ID;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date November 2024
|
* \date November 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -106,7 +106,7 @@ struct StreamingTask
|
|||||||
Task task = { nullptr };
|
Task task = { nullptr };
|
||||||
/* Required to be sure that correct data is present in the streamed in address */
|
/* Required to be sure that correct data is present in the streamed in address */
|
||||||
uint64 startFrameIdx = 0;
|
uint64 startFrameIdx = 0;
|
||||||
CBESpinLock lock;
|
cbe::SpinLock lock;
|
||||||
bool bInProgress;
|
bool bInProgress;
|
||||||
};
|
};
|
||||||
struct MaAudioPlayerAudioSrcRef
|
struct MaAudioPlayerAudioSrcRef
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date April 2025
|
* \date April 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -85,7 +85,7 @@ private:
|
|||||||
/* If empty, verts are unique per index */
|
/* If empty, verts are unique per index */
|
||||||
std::vector<uint32> idxs;
|
std::vector<uint32> idxs;
|
||||||
|
|
||||||
CBESpinLock meshHndsLock;
|
cbe::SpinLock meshHndsLock;
|
||||||
std::unordered_map<cbe::physics::World, cbe::physics::MeshBatchHnd> worldToMeshHnd;
|
std::unordered_map<cbe::physics::World, cbe::physics::MeshBatchHnd> worldToMeshHnd;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ private:
|
|||||||
BatchAllocator triangleBatchesAlloc;
|
BatchAllocator triangleBatchesAlloc;
|
||||||
/* No reference will be taken here. */
|
/* No reference will be taken here. */
|
||||||
JoltMeshBatch *allBatches = nullptr;
|
JoltMeshBatch *allBatches = nullptr;
|
||||||
CBESpinLock batchesLock;
|
cbe::SpinLock batchesLock;
|
||||||
|
|
||||||
cbe::physics::IDebugDraw *debugDrawInterface = nullptr;
|
cbe::physics::IDebugDraw *debugDrawInterface = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date April 2025
|
* \date April 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -139,12 +139,12 @@ private:
|
|||||||
static_assert(sizeof(AllocHnd) == sizeof(JobPacketPoolAllocator::AllocHandle), "Invalid Allocation handle size for JobPacket!");
|
static_assert(sizeof(AllocHnd) == sizeof(JobPacketPoolAllocator::AllocHandle), "Invalid Allocation handle size for JobPacket!");
|
||||||
static_assert(sizeof(AllocHnd) == sizeof(JobPacketPoolAllocator::AllocHandle), "Invalid Allocation handle size for Barrier!");
|
static_assert(sizeof(AllocHnd) == sizeof(JobPacketPoolAllocator::AllocHandle), "Invalid Allocation handle size for Barrier!");
|
||||||
|
|
||||||
CBESpinLock jobsAllocatorLock;
|
cbe::SpinLock jobsAllocatorLock;
|
||||||
JobPacketPoolAllocator jobsAllocator;
|
JobPacketPoolAllocator jobsAllocator;
|
||||||
RingBufferLockFree<AllocHnd> freeJobAllocs;
|
RingBufferLockFree<AllocHnd> freeJobAllocs;
|
||||||
|
|
||||||
BarrierPoolAllocator barrierAllocator;
|
BarrierPoolAllocator barrierAllocator;
|
||||||
CBESpinLock barrierAllocatorLock;
|
cbe::SpinLock barrierAllocatorLock;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static JoltCopatTask queueOneJob(copat::JobSystem *jobSystem, JobPacket *job);
|
static JoltCopatTask queueOneJob(copat::JobSystem *jobSystem, JobPacket *job);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date April 2025
|
* \date April 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -66,7 +66,7 @@ static void joltLog(const AChar *fmt, ...)
|
|||||||
|
|
||||||
va_end(list);
|
va_end(list);
|
||||||
|
|
||||||
LOG(ICbePhysicsModule::LOG_CATEGORY, "{}", UTF8_TO_TCHAR(fmted));
|
CBE_LOG(ICbePhysicsModule::LOG_CATEGORY, "{}", UTF8_TO_TCHAR(fmted));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (defined JPH_EXTERNAL_PROFILE) & PROFILING_ENABLED
|
#if (defined JPH_EXTERNAL_PROFILE) & PROFILING_ENABLED
|
||||||
@@ -276,7 +276,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::B
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating box shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating box shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -376,7 +376,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::C
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating capsule shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating capsule shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -386,7 +386,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::C
|
|||||||
JPH::Ref<JPH::Shape> newRotatedShape = createDefaultOrientedShape(result.Get().GetPtr());
|
JPH::Ref<JPH::Shape> newRotatedShape = createDefaultOrientedShape(result.Get().GetPtr());
|
||||||
if (newRotatedShape.GetPtr() == nullptr)
|
if (newRotatedShape.GetPtr() == nullptr)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbePhysicsModule::LOG_CATEGORY, "Failed creating capsule orientation shape");
|
CBE_LOG_ERROR(ICbePhysicsModule::LOG_CATEGORY, "Failed creating capsule orientation shape");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,7 +441,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::C
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating convex hull shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating convex hull shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -495,7 +495,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::C
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating cylinder shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating cylinder shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -505,7 +505,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::C
|
|||||||
JPH::Ref<JPH::Shape> newRotatedShape = createDefaultOrientedShape(result.Get().GetPtr());
|
JPH::Ref<JPH::Shape> newRotatedShape = createDefaultOrientedShape(result.Get().GetPtr());
|
||||||
if (newRotatedShape.GetPtr() == nullptr)
|
if (newRotatedShape.GetPtr() == nullptr)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbePhysicsModule::LOG_CATEGORY, "Failed creating cylinder orientation shape");
|
CBE_LOG_ERROR(ICbePhysicsModule::LOG_CATEGORY, "Failed creating cylinder orientation shape");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,7 +557,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::S
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating sphere shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating sphere shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -611,7 +611,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::T
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating tapered capsule shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating tapered capsule shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -621,7 +621,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::T
|
|||||||
JPH::Ref<JPH::Shape> newRotatedShape = createDefaultOrientedShape(result.Get().GetPtr());
|
JPH::Ref<JPH::Shape> newRotatedShape = createDefaultOrientedShape(result.Get().GetPtr());
|
||||||
if (newRotatedShape.GetPtr() == nullptr)
|
if (newRotatedShape.GetPtr() == nullptr)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbePhysicsModule::LOG_CATEGORY, "Failed creating tapered capsule orientation shape");
|
CBE_LOG_ERROR(ICbePhysicsModule::LOG_CATEGORY, "Failed creating tapered capsule orientation shape");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -675,7 +675,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::T
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating tapered cylinder shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating tapered cylinder shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -685,7 +685,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::T
|
|||||||
JPH::Ref<JPH::Shape> newRotatedShape = createDefaultOrientedShape(result.Get().GetPtr());
|
JPH::Ref<JPH::Shape> newRotatedShape = createDefaultOrientedShape(result.Get().GetPtr());
|
||||||
if (newRotatedShape.GetPtr() == nullptr)
|
if (newRotatedShape.GetPtr() == nullptr)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbePhysicsModule::LOG_CATEGORY, "Failed creating tapered cylinder orientation shape");
|
CBE_LOG_ERROR(ICbePhysicsModule::LOG_CATEGORY, "Failed creating tapered cylinder orientation shape");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -740,7 +740,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::T
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating triangle shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating triangle shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -790,7 +790,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::C
|
|||||||
const JoltShapeData *subShape = getShape(subShapeSetting.shape, subShapeSetting.shapeType);
|
const JoltShapeData *subShape = getShape(subShapeSetting.shape, subShapeSetting.shapeType);
|
||||||
if (subShape == nullptr)
|
if (subShape == nullptr)
|
||||||
{
|
{
|
||||||
LOG_WARN(ICbePhysicsModule::LOG_CATEGORY, "Invalid sub shape when creating static compound shape!");
|
CBE_LOG_WARN(ICbePhysicsModule::LOG_CATEGORY, "Invalid sub shape when creating static compound shape!");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -802,7 +802,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::C
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating static compound shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating static compound shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -852,7 +852,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::M
|
|||||||
const JoltShapeData *subShape = getShape(subShapeSetting.shape, subShapeSetting.shapeType);
|
const JoltShapeData *subShape = getShape(subShapeSetting.shape, subShapeSetting.shapeType);
|
||||||
if (subShape == nullptr) [[unlikely]]
|
if (subShape == nullptr) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_WARN(ICbePhysicsModule::LOG_CATEGORY, "Invalid sub shape when creating mutable compound shape!");
|
CBE_LOG_WARN(ICbePhysicsModule::LOG_CATEGORY, "Invalid sub shape when creating mutable compound shape!");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -864,7 +864,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::M
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating mutable compound shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating mutable compound shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -909,7 +909,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::T
|
|||||||
const JoltShapeData *subShape = getShape(settings.decoratedSettings.innerShape, settings.decoratedSettings.innerShapeType);
|
const JoltShapeData *subShape = getShape(settings.decoratedSettings.innerShape, settings.decoratedSettings.innerShapeType);
|
||||||
if (subShape == nullptr) [[unlikely]]
|
if (subShape == nullptr) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbePhysicsModule::LOG_CATEGORY, "Invalid sub shape when creating transformed shape!");
|
CBE_LOG_ERROR(ICbePhysicsModule::LOG_CATEGORY, "Invalid sub shape when creating transformed shape!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -922,7 +922,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::T
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating transformed shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating transformed shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -968,7 +968,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::O
|
|||||||
const JoltShapeData *subShape = getShape(settings.decoratedSettings.innerShape, settings.decoratedSettings.innerShapeType);
|
const JoltShapeData *subShape = getShape(settings.decoratedSettings.innerShape, settings.decoratedSettings.innerShapeType);
|
||||||
if (subShape == nullptr) [[unlikely]]
|
if (subShape == nullptr) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbePhysicsModule::LOG_CATEGORY, "Invalid sub shape when creating offset center of mass shape!");
|
CBE_LOG_ERROR(ICbePhysicsModule::LOG_CATEGORY, "Invalid sub shape when creating offset center of mass shape!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -980,7 +980,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::O
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating offset center of mass shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating offset center of mass shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -1028,7 +1028,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::E
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating empty shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating empty shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -1138,7 +1138,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::H
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating height field shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating height field shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -1154,7 +1154,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::H
|
|||||||
const JPH::ShapeSettings::ShapeResult rotationResult = joltRotationSettings.Create();
|
const JPH::ShapeSettings::ShapeResult rotationResult = joltRotationSettings.Create();
|
||||||
if (!rotationResult.IsValid())
|
if (!rotationResult.IsValid())
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating height field orientation shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating height field orientation shape with error: {}",
|
||||||
rotationResult.HasError() ? UTF8_TO_TCHAR(rotationResult.GetError().c_str()) : StringView(TCHAR("None"))
|
rotationResult.HasError() ? UTF8_TO_TCHAR(rotationResult.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -1232,7 +1232,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::M
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating mesh shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating mesh shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -1285,7 +1285,7 @@ bool JoltBackend::recreateShape(cbe::physics::Shape shape, const cbe::physics::P
|
|||||||
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
const JPH::ShapeSettings::ShapeResult result = joltSettings.Create();
|
||||||
if (!result.IsValid()) [[unlikely]]
|
if (!result.IsValid()) [[unlikely]]
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating empty shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating empty shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
@@ -1438,7 +1438,7 @@ JPH::Ref<JPH::Shape> JoltBackend::createDefaultOrientedShape(const JPH::Shape *i
|
|||||||
return result.Get();
|
return result.Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbePhysicsModule::LOG_CATEGORY, "Failed creating default oriented shape wrapping a shape with error: {}",
|
ICbePhysicsModule::LOG_CATEGORY, "Failed creating default oriented shape wrapping a shape with error: {}",
|
||||||
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
result.HasError() ? UTF8_TO_TCHAR(result.GetError().c_str()) : StringView(TCHAR("None"))
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date April 2025
|
* \date April 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -233,7 +233,7 @@ bool JoltPhysicsWorld::createBodies(ArrayRange<cbe::physics::Body> outBodies, Ar
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_WARN(ICbePhysicsModule::LOG_CATEGORY, "Failed to create scaled shape for body.");
|
CBE_LOG_WARN(ICbePhysicsModule::LOG_CATEGORY, "Failed to create scaled shape for body.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date April 2025
|
* \date April 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -39,7 +39,7 @@ public:
|
|||||||
for (uint32 broadphaseLayerIdx = 0; broadphaseLayerIdx < broadphaseLayersCount; ++broadphaseLayerIdx)
|
for (uint32 broadphaseLayerIdx = 0; broadphaseLayerIdx < broadphaseLayersCount; ++broadphaseLayerIdx)
|
||||||
{
|
{
|
||||||
const cbe::physics::BroadPhaseLayerInfo &info = broadphaseLayers[broadphaseLayerIdx];
|
const cbe::physics::BroadPhaseLayerInfo &info = broadphaseLayers[broadphaseLayerIdx];
|
||||||
LOG_WARN_C(
|
CBE_LOG_WARN_C(
|
||||||
info.name.length() >= cbe::physics::MAX_LAYER_NAME_LEN, ICbePhysicsModule::LOG_CATEGORY,
|
info.name.length() >= cbe::physics::MAX_LAYER_NAME_LEN, ICbePhysicsModule::LOG_CATEGORY,
|
||||||
"Broadphase layer name {}[{}] is exceeding the allowed limit of {}", info.name, info.name.length(),
|
"Broadphase layer name {}[{}] is exceeding the allowed limit of {}", info.name, info.name.length(),
|
||||||
cbe::physics::MAX_LAYER_NAME_LEN - 1
|
cbe::physics::MAX_LAYER_NAME_LEN - 1
|
||||||
@@ -76,7 +76,7 @@ public:
|
|||||||
for (uint32 broadphaseLayerIdx = 0; broadphaseLayerIdx < broadphaseLayers.size(); ++broadphaseLayerIdx)
|
for (uint32 broadphaseLayerIdx = 0; broadphaseLayerIdx < broadphaseLayers.size(); ++broadphaseLayerIdx)
|
||||||
{
|
{
|
||||||
const cbe::physics::BroadPhaseLayerInfo &info = broadphaseLayers[broadphaseLayerIdx];
|
const cbe::physics::BroadPhaseLayerInfo &info = broadphaseLayers[broadphaseLayerIdx];
|
||||||
LOG_WARN_C(
|
CBE_LOG_WARN_C(
|
||||||
PlatformFunctions::countOnes(info.collidesWith) == 0, ICbePhysicsModule::LOG_CATEGORY,
|
PlatformFunctions::countOnes(info.collidesWith) == 0, ICbePhysicsModule::LOG_CATEGORY,
|
||||||
"Broadphase layer name {} does not collide with any ObjectLayer", info.name
|
"Broadphase layer name {} does not collide with any ObjectLayer", info.name
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Subity
|
* \author Subity
|
||||||
* \date May 2025
|
* \date May 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -48,7 +48,9 @@ void FixedTicker::tick(float deltaTime)
|
|||||||
/* If delta time goes above certain threshold just drop the iterations */
|
/* If delta time goes above certain threshold just drop the iterations */
|
||||||
if (deltaTime > FRAME_TIME_THRESHOLD)
|
if (deltaTime > FRAME_TIME_THRESHOLD)
|
||||||
{
|
{
|
||||||
LOG_VERBOSE(ICbePhysicsModule::LOG_CATEGORY, "Fixed ticker iterations dropped {}->{}", numOfIteration, DROP_TO_ITERATIONS_NUM);
|
CBE_LOG_VERBOSE(
|
||||||
|
ICbePhysicsModule::LOG_CATEGORY, "Fixed ticker iterations dropped {}->{}", numOfIteration, DROP_TO_ITERATIONS_NUM
|
||||||
|
);
|
||||||
numOfIteration = DROP_TO_ITERATIONS_NUM;
|
numOfIteration = DROP_TO_ITERATIONS_NUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -197,10 +197,24 @@ String ICbeRendererModule::generateUniformStructCodes(const gal::GPUStructLayout
|
|||||||
{
|
{
|
||||||
retType = compilerInst->getShaderTypeName(gal::EShaderDataFormat::Float4);
|
retType = compilerInst->getShaderTypeName(gal::EShaderDataFormat::Float4);
|
||||||
}
|
}
|
||||||
else if (!field.metaData.bStructType)
|
else if (field.metaData.bStructType)
|
||||||
|
{
|
||||||
|
auto structLayoutItr = std::find_if(
|
||||||
|
rendererMod->getGpuStructs().cbegin(), rendererMod->getGpuStructs().cend(),
|
||||||
|
[field](const gal::GPUStructLayout &layout)
|
||||||
|
{
|
||||||
|
return TCharStr::isEqual(layout.name, field.metaData.structTypeName);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
debugAssert(structLayoutItr != rendererMod->getGpuStructs().cend());
|
||||||
|
CBE_LOG_DEBUG_C(
|
||||||
|
structLayoutItr->textureFieldsCount == 0, ICbeRendererModule::LOG_CATEGORY,
|
||||||
|
"Inner struct with textures are not supported with material param accessor macros."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
retType = compilerInst->getShaderTypeName(field.metaData.dataType);
|
retType = compilerInst->getShaderTypeName(field.metaData.dataType);
|
||||||
auto typeInfo = gal::EShaderDataFormat::typeToInfo(field.metaData.dataType);
|
|
||||||
}
|
}
|
||||||
FormatArgsMap argsMap{
|
FormatArgsMap argsMap{
|
||||||
{ TCHAR(MAT_TMPL_ACCESSOR_IS_ARRAY), (field.elemCount > 1) },
|
{ TCHAR(MAT_TMPL_ACCESSOR_IS_ARRAY), (field.elemCount > 1) },
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2024
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -58,7 +58,7 @@ CommandPools::CommandPools(gal::Context *galContext, uint32 threadCount, const T
|
|||||||
}
|
}
|
||||||
if (!galCtx->createCommandPools(uniqPools, poolCis))
|
if (!galCtx->createCommandPools(uniqPools, poolCis))
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed creating all command pools for {}", name);
|
CBE_LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed creating all command pools for {}", name);
|
||||||
}
|
}
|
||||||
for (uint8 uniqQIdx = 0, qIdx = 0; qIdx < gal::EQueue::MaxQs; qIdx++)
|
for (uint8 uniqQIdx = 0, qIdx = 0; qIdx < gal::EQueue::MaxQs; qIdx++)
|
||||||
{
|
{
|
||||||
@@ -110,14 +110,13 @@ CommandPools::allocateAllThreads(const TChar *baseName, gal::EQueue::Type queue,
|
|||||||
|
|
||||||
if (!galCtx->allocateCommandBuffers(retVals, cmdBufferAis))
|
if (!galCtx->allocateCommandBuffers(retVals, cmdBufferAis))
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed allocating all command buffers for {}", baseName);
|
CBE_LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed allocating all command buffers for {}", baseName);
|
||||||
retVals.clear();
|
retVals.clear();
|
||||||
}
|
}
|
||||||
return retVals;
|
return retVals;
|
||||||
}
|
}
|
||||||
|
|
||||||
gal::GAL_RESOURCE_HND(CommandBuffer
|
gal::GAL_RESOURCE_HND(CommandBuffer) CommandPools::allocate(const TChar *cmdName, uint32 threadIdx, gal::EQueue::Type queue, bool bSecondary /*= false*/) const
|
||||||
) CommandPools::allocate(const TChar *cmdName, uint32 threadIdx, gal::EQueue::Type queue, bool bSecondary /*= false*/) const
|
|
||||||
{
|
{
|
||||||
gal::GAL_RESOURCE_HND(CommandBuffer) retVal;
|
gal::GAL_RESOURCE_HND(CommandBuffer) retVal;
|
||||||
|
|
||||||
@@ -128,7 +127,7 @@ gal::GAL_RESOURCE_HND(CommandBuffer
|
|||||||
gal::CommandBufferAllocInfo{ .name = cmdName, .pool = pools[poolOffset + threadIdx], .count = 1, .bSecondary = bSecondary }
|
gal::CommandBufferAllocInfo{ .name = cmdName, .pool = pools[poolOffset + threadIdx], .count = 1, .bSecondary = bSecondary }
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed allocating command buffer {}", cmdName);
|
CBE_LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed allocating command buffer {}", cmdName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -96,7 +96,7 @@ void RendererContext::initialize(RenderContextInitInfo initInfo) noexcept
|
|||||||
.genericMem = { .allocPtr = galGenericAlloc, .reallocPtr = galGenericRealloc, .freePtr = galGenericFree } },
|
.genericMem = { .allocPtr = galGenericAlloc, .reallocPtr = galGenericRealloc, .freePtr = galGenericFree } },
|
||||||
.bEnableValidation = cmdLine.hasArg(GAL_VALIDATION_ENABLE_NAME),
|
.bEnableValidation = cmdLine.hasArg(GAL_VALIDATION_ENABLE_NAME),
|
||||||
.bExtendedValidations = true,
|
.bExtendedValidations = true,
|
||||||
.bDebugGpuVaBinding = true,
|
.bDebugGpuVaBinding = false, ///< Too verbose right now
|
||||||
.bOffscreenOnly = false,
|
.bOffscreenOnly = false,
|
||||||
.driverApi = initInfo.driverApi,
|
.driverApi = initInfo.driverApi,
|
||||||
.enableFs = initInfo.enableFs,
|
.enableFs = initInfo.enableFs,
|
||||||
@@ -115,13 +115,13 @@ void RendererContext::initialize(RenderContextInitInfo initInfo) noexcept
|
|||||||
|
|
||||||
void RendererContext::prepareRelease() noexcept
|
void RendererContext::prepareRelease() noexcept
|
||||||
{
|
{
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Preparing RendererContext release!");
|
CBE_LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Preparing RendererContext release!");
|
||||||
/* Wait clear all deletes */
|
/* Wait clear all deletes */
|
||||||
resourceDeleter.clear();
|
resourceDeleter.clear();
|
||||||
}
|
}
|
||||||
void RendererContext::release() noexcept
|
void RendererContext::release() noexcept
|
||||||
{
|
{
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Releasing RendererContext!");
|
CBE_LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Releasing RendererContext!");
|
||||||
|
|
||||||
copat::waitOnAwaitable(std::move(lastCompileTask));
|
copat::waitOnAwaitable(std::move(lastCompileTask));
|
||||||
|
|
||||||
@@ -326,7 +326,7 @@ RendererContext::CompileTask RendererContext::recompileShadersAsync(bool bLoadFr
|
|||||||
|
|
||||||
if (bFailure)
|
if (bFailure)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed to compile and reflect shaders");
|
CBE_LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed to compile and reflect shaders");
|
||||||
co_return ECompileState::Failure;
|
co_return ECompileState::Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +358,7 @@ RendererContext::CompileTask RendererContext::recompileShadersAsync(bool bLoadFr
|
|||||||
std::erase_if(generalDirs, erasePred);
|
std::erase_if(generalDirs, erasePred);
|
||||||
if (basicDirs.size() + editorDirs.size() + generalDirs.size() == 0)
|
if (basicDirs.size() + editorDirs.size() + generalDirs.size() == 0)
|
||||||
{
|
{
|
||||||
LOG_WARN(ICbeRendererModule::LOG_CATEGORY, "No shader directory found. Skipping shader compile from source!");
|
CBE_LOG_WARN(ICbeRendererModule::LOG_CATEGORY, "No shader directory found. Skipping shader compile from source!");
|
||||||
bLoadFromDirs = false;
|
bLoadFromDirs = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,7 +371,7 @@ RendererContext::CompileTask RendererContext::recompileShadersAsync(bool bLoadFr
|
|||||||
const SCompShaderBlob basicSrcs = co_await basicSrcsAwaitable;
|
const SCompShaderBlob basicSrcs = co_await basicSrcsAwaitable;
|
||||||
const SCompShaderBlob editorSrcs = co_await editorSrcsAwaitable;
|
const SCompShaderBlob editorSrcs = co_await editorSrcsAwaitable;
|
||||||
const SCompShaderBlob generalSrcs = co_await generalSrcsAwaitable;
|
const SCompShaderBlob generalSrcs = co_await generalSrcsAwaitable;
|
||||||
LOG(ICbeRendererModule::LOG_CATEGORY, "All shader sources loaded in {}s", s.currentLap());
|
CBE_LOG(ICbeRendererModule::LOG_CATEGORY, "All shader sources loaded in {}s", s.currentLap());
|
||||||
s.lap();
|
s.lap();
|
||||||
|
|
||||||
/* Now wait and load the new blobs */
|
/* Now wait and load the new blobs */
|
||||||
@@ -381,13 +381,13 @@ RendererContext::CompileTask RendererContext::recompileShadersAsync(bool bLoadFr
|
|||||||
if (cState != ECompileState::Failure)
|
if (cState != ECompileState::Failure)
|
||||||
{
|
{
|
||||||
basicShadersCompiled.test_and_set(std::memory_order::relaxed);
|
basicShadersCompiled.test_and_set(std::memory_order::relaxed);
|
||||||
LOG(ICbeRendererModule::LOG_CATEGORY, "Fundamental shaders compiled in {}s", s.currentLap());
|
CBE_LOG(ICbeRendererModule::LOG_CATEGORY, "Fundamental shaders compiled in {}s", s.currentLap());
|
||||||
bAnyNewShaders = cState == ECompileState::Success;
|
bAnyNewShaders = cState == ECompileState::Success;
|
||||||
bSerializedShaders = false;
|
bSerializedShaders = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Fundamental shaders compile failed!");
|
CBE_LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Fundamental shaders compile failed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cState == ECompileState::Success && !bSerializedShaders)
|
if (cState == ECompileState::Success && !bSerializedShaders)
|
||||||
@@ -409,13 +409,13 @@ RendererContext::CompileTask RendererContext::recompileShadersAsync(bool bLoadFr
|
|||||||
{
|
{
|
||||||
basicShadersCompiled.test_and_set(std::memory_order::relaxed);
|
basicShadersCompiled.test_and_set(std::memory_order::relaxed);
|
||||||
editorShadersCompiled.test_and_set(std::memory_order::relaxed);
|
editorShadersCompiled.test_and_set(std::memory_order::relaxed);
|
||||||
LOG(ICbeRendererModule::LOG_CATEGORY, "Editor shaders compiled in {}s", s.currentLap());
|
CBE_LOG(ICbeRendererModule::LOG_CATEGORY, "Editor shaders compiled in {}s", s.currentLap());
|
||||||
bAnyNewShaders = (cState == ECompileState::Success) || bAnyNewShaders;
|
bAnyNewShaders = (cState == ECompileState::Success) || bAnyNewShaders;
|
||||||
bSerializedShaders = false;
|
bSerializedShaders = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Editor shaders compile failed!");
|
CBE_LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Editor shaders compile failed!");
|
||||||
}
|
}
|
||||||
if (cState == ECompileState::Success && !bSerializedShaders)
|
if (cState == ECompileState::Success && !bSerializedShaders)
|
||||||
{
|
{
|
||||||
@@ -438,13 +438,13 @@ RendererContext::CompileTask RendererContext::recompileShadersAsync(bool bLoadFr
|
|||||||
basicShadersCompiled.test_and_set(std::memory_order::relaxed);
|
basicShadersCompiled.test_and_set(std::memory_order::relaxed);
|
||||||
editorShadersCompiled.test_and_set(std::memory_order::relaxed);
|
editorShadersCompiled.test_and_set(std::memory_order::relaxed);
|
||||||
generalShadersCompiled.test_and_set(std::memory_order::release);
|
generalShadersCompiled.test_and_set(std::memory_order::release);
|
||||||
LOG(ICbeRendererModule::LOG_CATEGORY, "General shaders compiled in {}s", s.currentLap());
|
CBE_LOG(ICbeRendererModule::LOG_CATEGORY, "General shaders compiled in {}s", s.currentLap());
|
||||||
bAnyNewShaders = (cState == ECompileState::Success) || bAnyNewShaders;
|
bAnyNewShaders = (cState == ECompileState::Success) || bAnyNewShaders;
|
||||||
bSerializedShaders = false;
|
bSerializedShaders = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "General shaders compile failed!");
|
CBE_LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "General shaders compile failed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cState == ECompileState::Success && !bSerializedShaders)
|
if (cState == ECompileState::Success && !bSerializedShaders)
|
||||||
@@ -474,7 +474,7 @@ RendererContext::CompileTask RendererContext::recompileShadersAsync(bool bLoadFr
|
|||||||
bSerializedShaders = false;
|
bSerializedShaders = false;
|
||||||
}
|
}
|
||||||
s.stop();
|
s.stop();
|
||||||
LOG(ICbeRendererModule::LOG_CATEGORY, "All shaders compiled in {}s", s.duration());
|
CBE_LOG(ICbeRendererModule::LOG_CATEGORY, "All shaders compiled in {}s", s.duration());
|
||||||
|
|
||||||
/* If shaders are updated or compiled, store and cache then immediately */
|
/* If shaders are updated or compiled, store and cache then immediately */
|
||||||
if (bAnyNewShaders)
|
if (bAnyNewShaders)
|
||||||
@@ -528,7 +528,7 @@ RendererContext::CompileTask RendererContext::recompileShadersAsync()
|
|||||||
bool bFailure = !co_await compiler->compileShadersAsync(newCompiledResults, compileInfo);
|
bool bFailure = !co_await compiler->compileShadersAsync(newCompiledResults, compileInfo);
|
||||||
if (bFailure)
|
if (bFailure)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed to compile and reflect shaders");
|
CBE_LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed to compile and reflect shaders");
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -543,7 +543,7 @@ RendererContext::CompileTask RendererContext::recompileShadersAsync()
|
|||||||
onShadersUpdated(updates);
|
onShadersUpdated(updates);
|
||||||
|
|
||||||
s.stop();
|
s.stop();
|
||||||
LOG(ICbeRendererModule::LOG_CATEGORY, "All shaders compiled in {}s", s.duration());
|
CBE_LOG(ICbeRendererModule::LOG_CATEGORY, "All shaders compiled in {}s", s.duration());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -574,7 +574,7 @@ void RendererContext::updateCompiledResultsStructLayout()
|
|||||||
auto itr = vertName2Layout.find(std::get<NameString>(vertEntry.typeNameOrLayout));
|
auto itr = vertName2Layout.find(std::get<NameString>(vertEntry.typeNameOrLayout));
|
||||||
if (itr == vertName2Layout.cend())
|
if (itr == vertName2Layout.cend())
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Vertex layout {} missing for shader {}!",
|
ICbeRendererModule::LOG_CATEGORY, "Vertex layout {} missing for shader {}!",
|
||||||
std::get<NameString>(vertEntry.typeNameOrLayout), r.first
|
std::get<NameString>(vertEntry.typeNameOrLayout), r.first
|
||||||
);
|
);
|
||||||
@@ -609,7 +609,7 @@ void RendererContext::updateCompiledResultsStructLayout()
|
|||||||
auto itr = vertName2Layout.find(std::get<NameString>(vertEntry.typeNameOrLayout));
|
auto itr = vertName2Layout.find(std::get<NameString>(vertEntry.typeNameOrLayout));
|
||||||
if (itr == vertName2Layout.cend())
|
if (itr == vertName2Layout.cend())
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Vertex layout {} missing for material {}!",
|
ICbeRendererModule::LOG_CATEGORY, "Vertex layout {} missing for material {}!",
|
||||||
std::get<NameString>(vertEntry.typeNameOrLayout), matVariant.second.name
|
std::get<NameString>(vertEntry.typeNameOrLayout), matVariant.second.name
|
||||||
);
|
);
|
||||||
@@ -698,7 +698,7 @@ bool RendererContext::storeCompiledResults(ShadersUpdate &outUpdates, const SCom
|
|||||||
recompiledCount += newCompiledRes.materials.size();
|
recompiledCount += newCompiledRes.materials.size();
|
||||||
if (!bFromCache)
|
if (!bFromCache)
|
||||||
{
|
{
|
||||||
LOG(ICbeRendererModule::LOG_CATEGORY, "Compiled {} new shaders!", recompiledCount);
|
CBE_LOG(ICbeRendererModule::LOG_CATEGORY, "Compiled {} new shaders!", recompiledCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update the old compiled results now */
|
/* Update the old compiled results now */
|
||||||
@@ -1166,7 +1166,7 @@ void RendererContext::createBasicPipelines()
|
|||||||
const bool bSuccess = galContext()->createGraphicsPipelines(wgGPipeline, gpCi);
|
const bool bSuccess = galContext()->createGraphicsPipelines(wgGPipeline, gpCi);
|
||||||
if (!bSuccess)
|
if (!bSuccess)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed to create \"{}\" Graphics pipeline!", WG_SHADER_NAME);
|
CBE_LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed to create \"{}\" Graphics pipeline!", WG_SHADER_NAME);
|
||||||
wgGPipeline = {};
|
wgGPipeline = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1204,7 +1204,7 @@ void RendererContext::createBasicPipelines()
|
|||||||
const bool bSuccess = galContext()->createGraphicsPipelines(defaultGBufferPipeline, gpCi);
|
const bool bSuccess = galContext()->createGraphicsPipelines(defaultGBufferPipeline, gpCi);
|
||||||
if (!bSuccess)
|
if (!bSuccess)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed to create \"Default\" Graphics pipeline!");
|
CBE_LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed to create \"Default\" Graphics pipeline!");
|
||||||
defaultGBufferPipeline = {};
|
defaultGBufferPipeline = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1222,7 +1222,7 @@ void RendererContext::createBasicPipelines()
|
|||||||
const bool bSuccess = galContext()->createComputePipelines(fillBufferWithPcPipeline, fillBufferCompCi);
|
const bool bSuccess = galContext()->createComputePipelines(fillBufferWithPcPipeline, fillBufferCompCi);
|
||||||
if (!bSuccess)
|
if (!bSuccess)
|
||||||
{
|
{
|
||||||
LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed to create \"FillBufferFromPC\" Compute pipeline!");
|
CBE_LOG_ERROR(ICbeRendererModule::LOG_CATEGORY, "Failed to create \"FillBufferFromPC\" Compute pipeline!");
|
||||||
fillBufferWithPcPipeline = {};
|
fillBufferWithPcPipeline = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1801,6 +1801,8 @@ CpuBuffersPool::Allocation CpuBuffersPool::getTempBuffer(uint32 minSize) noexcep
|
|||||||
}
|
}
|
||||||
|
|
||||||
framesToFree[bestFitIdx] = static_cast<uint8>(rCtx->getSwapchainImgCount());
|
framesToFree[bestFitIdx] = static_cast<uint8>(rCtx->getSwapchainImgCount());
|
||||||
|
|
||||||
|
debugAssert(rCtx->galContext()->isValid(buffers[bestFitIdx]));
|
||||||
return Allocation{ .hndl = buffers[bestFitIdx], .idx = bestFitIdx, .offset = 0, .size = bestFitSize };
|
return Allocation{ .hndl = buffers[bestFitIdx], .idx = bestFitIdx, .offset = 0, .size = bestFitSize };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1822,10 +1824,10 @@ void GpGpuBindlessDataBuffers::setup(gal::PipelineHndlVariants newPipeline, uint
|
|||||||
#endif
|
#endif
|
||||||
if (pipeline == newPipeline && descTableIdx == tblIdx)
|
if (pipeline == newPipeline && descTableIdx == tblIdx)
|
||||||
{
|
{
|
||||||
LOG(ICbeRendererModule::LOG_CATEGORY, "Cannot reset same pipeline and table pairs!");
|
CBE_LOG(ICbeRendererModule::LOG_CATEGORY, "Cannot reset same pipeline and table pairs!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG(ICbeRendererModule::LOG_CATEGORY, "Setting up bindless data buffers using new pipeline for {}!", baseName);
|
CBE_LOG(ICbeRendererModule::LOG_CATEGORY, "Setting up bindless data buffers using new pipeline for {}!", baseName);
|
||||||
|
|
||||||
resetDesc(false);
|
resetDesc(false);
|
||||||
pipeline = newPipeline;
|
pipeline = newPipeline;
|
||||||
@@ -2069,10 +2071,10 @@ void GpGpuBindlessTextures::setup(
|
|||||||
#endif
|
#endif
|
||||||
if (pipeline == newPipeline && descTableIdx == tblIdx && descBindingIdx == bindIdx)
|
if (pipeline == newPipeline && descTableIdx == tblIdx && descBindingIdx == bindIdx)
|
||||||
{
|
{
|
||||||
LOG(ICbeRendererModule::LOG_CATEGORY, "Cannot reset same pipeline and table pairs!");
|
CBE_LOG(ICbeRendererModule::LOG_CATEGORY, "Cannot reset same pipeline and table pairs!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOG(ICbeRendererModule::LOG_CATEGORY, "Setting up bindless textures using new pipeline for {}!", baseName);
|
CBE_LOG(ICbeRendererModule::LOG_CATEGORY, "Setting up bindless textures using new pipeline for {}!", baseName);
|
||||||
|
|
||||||
defTexSlotClear.resize(rCtx.getSwapchainImgCount());
|
defTexSlotClear.resize(rCtx.getSwapchainImgCount());
|
||||||
|
|
||||||
@@ -2342,6 +2344,10 @@ std::vector<uint64> GpGpuBindlessTextures::addImages(ArrayView<gal::GAL_RESOURCE
|
|||||||
std::vector<uint64> GpGpuBindlessTextures::addImageViewsToTable(ArrayView<gal::GAL_RESOURCE_HND(ImageView)> imgViews)
|
std::vector<uint64> GpGpuBindlessTextures::addImageViewsToTable(ArrayView<gal::GAL_RESOURCE_HND(ImageView)> imgViews)
|
||||||
{
|
{
|
||||||
debugAssert(!imgViews.empty());
|
debugAssert(!imgViews.empty());
|
||||||
|
fatalAssertf(
|
||||||
|
imgViews.size() < descsCountPerTable, "Cannot support {} textures in same descriptor table. Max {} is allowed in this HW",
|
||||||
|
imgViews.size(), descsCountPerTable
|
||||||
|
);
|
||||||
|
|
||||||
std::vector<uint64> retVals;
|
std::vector<uint64> retVals;
|
||||||
retVals.resize(imgViews.size());
|
retVals.resize(imgViews.size());
|
||||||
@@ -2382,7 +2388,7 @@ std::vector<uint64> GpGpuBindlessTextures::addImageViewsToTable(ArrayView<gal::G
|
|||||||
bool bNoneReachedEnd = true;
|
bool bNoneReachedEnd = true;
|
||||||
while (bNoneReachedEnd)
|
while (bNoneReachedEnd)
|
||||||
{
|
{
|
||||||
uint32 tableIdx = entryIdxToTable(perImgFoundEntries[0][perImgFoundEntryIdx[0]]);
|
const uint32 tableIdx = entryIdxToTable(perImgFoundEntries[0][perImgFoundEntryIdx[0]]);
|
||||||
|
|
||||||
bool bAllMatched = true;
|
bool bAllMatched = true;
|
||||||
/* Necessary to increment the image entry index until it reaches the max table index */
|
/* Necessary to increment the image entry index until it reaches the max table index */
|
||||||
@@ -2429,7 +2435,7 @@ std::vector<uint64> GpGpuBindlessTextures::addImageViewsToTable(ArrayView<gal::G
|
|||||||
uint64 descsCountLeftToCheck = allocator.size();
|
uint64 descsCountLeftToCheck = allocator.size();
|
||||||
for (; tableIdx < descsTables.size(); ++tableIdx)
|
for (; tableIdx < descsTables.size(); ++tableIdx)
|
||||||
{
|
{
|
||||||
const SizeT count = Math::min(descsCountLeftToCheck, descsCountLeftToCheck);
|
const SizeT count = Math::min(descsCountLeftToCheck, descsCountPerTable);
|
||||||
const SizeT freeCount = allocator.countZeroesInRange(tableToEntryIdx(tableIdx), count);
|
const SizeT freeCount = allocator.countZeroesInRange(tableToEntryIdx(tableIdx), count);
|
||||||
descsCountLeftToCheck -= count;
|
descsCountLeftToCheck -= count;
|
||||||
|
|
||||||
@@ -2439,18 +2445,74 @@ std::vector<uint64> GpGpuBindlessTextures::addImageViewsToTable(ArrayView<gal::G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reset so that we only allocate new descriptor entry if view does not exist in current selected tableIdx. */
|
||||||
|
viewsExists.resetRange(0, imgViews.size());
|
||||||
if (tableIdx >= descsTables.size())
|
if (tableIdx >= descsTables.size())
|
||||||
{
|
{
|
||||||
/* Resize and allocate from the end table index */
|
/* Resize and allocate from the end table index */
|
||||||
resizeDescs(allocator.size() + imgViews.size());
|
resizeDescs(allocator.size() + imgViews.size());
|
||||||
tableIdx = static_cast<uint32>(descsTables.size() - 1);
|
tableIdx = static_cast<uint32>(descsTables.size() - 1);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Find if img view entry is found in selected table and reuse it.
|
||||||
|
* For new but repeatedly used textures the slot reuse happens in next step. */
|
||||||
|
for (SizeT i = 0; i < imgViews.size(); ++i)
|
||||||
|
{
|
||||||
|
auto foundEntriesItr = std::find_if(
|
||||||
|
perImgFoundEntries[i].cbegin(), perImgFoundEntries[i].cend(),
|
||||||
|
[tableIdx, this](uint64 descEntryIdx)
|
||||||
|
{
|
||||||
|
return entryIdxToTable(descEntryIdx) == tableIdx;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (foundEntriesItr == perImgFoundEntries[i].cend())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
viewsExists[i] = true;
|
||||||
|
retVals[i] = *foundEntriesItr;
|
||||||
|
++descEntries[retVals[i]].refCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Allocate from descriptors in this table's range */
|
/* Allocate from descriptors in this table's range */
|
||||||
const SizeT descEntryStartIndex = tableToEntryIdx(tableIdx);
|
const SizeT descEntryStartIndex = tableToEntryIdx(tableIdx);
|
||||||
const SizeT descEntryEndIndex = Math::min(descEntryStartIndex + descsCountPerTable, allocator.size());
|
const SizeT descEntryEndIndex = Math::min(descEntryStartIndex + descsCountPerTable, allocator.size());
|
||||||
for (SizeT i = 0; i < imgViews.size(); ++i)
|
for (SizeT i = 0; i < imgViews.size(); ++i)
|
||||||
{
|
{
|
||||||
|
if (viewsExists[i])
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResKey resKey{ gal::EResourceType::ImageView, GAL_RES_TO_UNTYPE_HNDL(imgViews[i]) };
|
||||||
|
auto resToEntryItr = imgResToEntryIdx.find(resKey);
|
||||||
|
/* If the same texture is reused in same request? Try to reuse slot here. */
|
||||||
|
if (resToEntryItr != imgResToEntryIdx.end())
|
||||||
|
{
|
||||||
|
/* Find if texture is already inserted in this table. */
|
||||||
|
uint64 entryIdx = resToEntryItr->second;
|
||||||
|
while (isValidTexAt(entryIdx))
|
||||||
|
{
|
||||||
|
if (entryIdxToTable(entryIdx) == tableIdx)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
entryIdx = descEntries[entryIdx].nextEntryIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValidTexAt(entryIdx))
|
||||||
|
{
|
||||||
|
/* Found an entry try to reuse and mark view as exists to skip writing descriptors again */
|
||||||
|
viewsExists[i] = true;
|
||||||
|
retVals[i] = entryIdx;
|
||||||
|
++descEntries[retVals[i]].refCount;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SizeT descIdx = 0;
|
SizeT descIdx = 0;
|
||||||
const bool bAllocated = allocator.findFirstUnsetInRange(descIdx, descEntryStartIndex, descEntryEndIndex - descEntryStartIndex);
|
const bool bAllocated = allocator.findFirstUnsetInRange(descIdx, descEntryStartIndex, descEntryEndIndex - descEntryStartIndex);
|
||||||
debugAssert(bAllocated);
|
debugAssert(bAllocated);
|
||||||
@@ -2462,8 +2524,6 @@ std::vector<uint64> GpGpuBindlessTextures::addImageViewsToTable(ArrayView<gal::G
|
|||||||
.bOwningView = false,
|
.bOwningView = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ResKey resKey{ gal::EResourceType::ImageView, GAL_RES_TO_UNTYPE_HNDL(imgViews[i]) };
|
|
||||||
auto resToEntryItr = imgResToEntryIdx.find(resKey);
|
|
||||||
if (resToEntryItr == imgResToEntryIdx.end())
|
if (resToEntryItr == imgResToEntryIdx.end())
|
||||||
{
|
{
|
||||||
/* First entry */
|
/* First entry */
|
||||||
@@ -2475,6 +2535,7 @@ std::vector<uint64> GpGpuBindlessTextures::addImageViewsToTable(ArrayView<gal::G
|
|||||||
TextureData &thisEntry = descEntries[descIdx];
|
TextureData &thisEntry = descEntries[descIdx];
|
||||||
TextureData &prevEntry = descEntries[resToEntryItr->second];
|
TextureData &prevEntry = descEntries[resToEntryItr->second];
|
||||||
|
|
||||||
|
thisEntry.bFirstEntry = false;
|
||||||
thisEntry.prevEntryIdx = resToEntryItr->second;
|
thisEntry.prevEntryIdx = resToEntryItr->second;
|
||||||
if (isValidTexAt(prevEntry.nextEntryIdx))
|
if (isValidTexAt(prevEntry.nextEntryIdx))
|
||||||
{
|
{
|
||||||
@@ -2488,7 +2549,7 @@ std::vector<uint64> GpGpuBindlessTextures::addImageViewsToTable(ArrayView<gal::G
|
|||||||
retVals[i] = descIdx;
|
retVals[i] = descIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write only if pipeline is available */
|
/* Write only if pipeline is available. */
|
||||||
if (rCtx.galContext()->isValid(pipeline))
|
if (rCtx.galContext()->isValid(pipeline))
|
||||||
{
|
{
|
||||||
std::vector<gal::UpdateImageDescriptors> writeDescs;
|
std::vector<gal::UpdateImageDescriptors> writeDescs;
|
||||||
@@ -2497,6 +2558,12 @@ std::vector<uint64> GpGpuBindlessTextures::addImageViewsToTable(ArrayView<gal::G
|
|||||||
writeDescInfo.resize(imgViews.size());
|
writeDescInfo.resize(imgViews.size());
|
||||||
for (SizeT i = 0; i < imgViews.size(); ++i)
|
for (SizeT i = 0; i < imgViews.size(); ++i)
|
||||||
{
|
{
|
||||||
|
/* If view already exists no need to update descriptor again */
|
||||||
|
if (viewsExists[i])
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
writeDescInfo[i] = {
|
writeDescInfo[i] = {
|
||||||
.hndl = descEntries[retVals[i]].viewHndl,
|
.hndl = descEntries[retVals[i]].viewHndl,
|
||||||
.layout = gal::EImageLayout::ShaderReadOnly,
|
.layout = gal::EImageLayout::ShaderReadOnly,
|
||||||
@@ -2516,6 +2583,10 @@ std::vector<uint64> GpGpuBindlessTextures::addImageViewsToTable(ArrayView<gal::G
|
|||||||
std::vector<uint64> GpGpuBindlessTextures::addImagesToTable(ArrayView<gal::GAL_RESOURCE_HND(Image)> imgs)
|
std::vector<uint64> GpGpuBindlessTextures::addImagesToTable(ArrayView<gal::GAL_RESOURCE_HND(Image)> imgs)
|
||||||
{
|
{
|
||||||
debugAssert(!imgs.empty());
|
debugAssert(!imgs.empty());
|
||||||
|
fatalAssertf(
|
||||||
|
imgs.size() < descsCountPerTable, "Cannot support {} textures in same descriptor table. Max {} is allowed in this HW", imgs.size(),
|
||||||
|
descsCountPerTable
|
||||||
|
);
|
||||||
|
|
||||||
std::vector<uint64> retVals;
|
std::vector<uint64> retVals;
|
||||||
retVals.resize(imgs.size());
|
retVals.resize(imgs.size());
|
||||||
@@ -2556,7 +2627,7 @@ std::vector<uint64> GpGpuBindlessTextures::addImagesToTable(ArrayView<gal::GAL_R
|
|||||||
bool bNoneReachedEnd = true;
|
bool bNoneReachedEnd = true;
|
||||||
while (bNoneReachedEnd)
|
while (bNoneReachedEnd)
|
||||||
{
|
{
|
||||||
uint32 tableIdx = entryIdxToTable(perImgFoundEntries[0][perImgFoundEntryIdx[0]]);
|
const uint32 tableIdx = entryIdxToTable(perImgFoundEntries[0][perImgFoundEntryIdx[0]]);
|
||||||
|
|
||||||
bool bAllMatched = true;
|
bool bAllMatched = true;
|
||||||
/* Necessary to increment the image entry index until it reaches the max table index */
|
/* Necessary to increment the image entry index until it reaches the max table index */
|
||||||
@@ -2603,7 +2674,7 @@ std::vector<uint64> GpGpuBindlessTextures::addImagesToTable(ArrayView<gal::GAL_R
|
|||||||
uint64 descsCountLeftToCheck = allocator.size();
|
uint64 descsCountLeftToCheck = allocator.size();
|
||||||
for (; tableIdx < descsTables.size(); ++tableIdx)
|
for (; tableIdx < descsTables.size(); ++tableIdx)
|
||||||
{
|
{
|
||||||
const SizeT count = Math::min(descsCountLeftToCheck, descsCountLeftToCheck);
|
const SizeT count = Math::min(descsCountLeftToCheck, descsCountPerTable);
|
||||||
const SizeT freeCount = allocator.countZeroesInRange(tableToEntryIdx(tableIdx), count);
|
const SizeT freeCount = allocator.countZeroesInRange(tableToEntryIdx(tableIdx), count);
|
||||||
descsCountLeftToCheck -= count;
|
descsCountLeftToCheck -= count;
|
||||||
|
|
||||||
@@ -2613,18 +2684,75 @@ std::vector<uint64> GpGpuBindlessTextures::addImagesToTable(ArrayView<gal::GAL_R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reset so that we only allocate new descriptor entry if img does not exist in current selected tableIdx. */
|
||||||
|
viewsExists.resetRange(0, imgs.size());
|
||||||
if (tableIdx >= descsTables.size())
|
if (tableIdx >= descsTables.size())
|
||||||
{
|
{
|
||||||
/* Resize and allocate from the end table index */
|
/* Resize and allocate from the end table index */
|
||||||
resizeDescs(allocator.size() + imgs.size());
|
resizeDescs(allocator.size() + imgs.size());
|
||||||
tableIdx = static_cast<uint32>(descsTables.size() - 1);
|
tableIdx = static_cast<uint32>(descsTables.size() - 1);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Find if img entry is found in selected table and reuse it.
|
||||||
|
* For new but repeatedly used textures the slot reuse happens in next step. */
|
||||||
|
for (SizeT i = 0; i < imgs.size(); ++i)
|
||||||
|
{
|
||||||
|
auto foundEntriesItr = std::find_if(
|
||||||
|
perImgFoundEntries[i].cbegin(), perImgFoundEntries[i].cend(),
|
||||||
|
[tableIdx, this](uint64 descEntryIdx)
|
||||||
|
{
|
||||||
|
return entryIdxToTable(descEntryIdx) == tableIdx;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (foundEntriesItr == perImgFoundEntries[i].cend())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
viewsExists[i] = true;
|
||||||
|
retVals[i] = *foundEntriesItr;
|
||||||
|
++descEntries[retVals[i]].refCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Allocate from descriptors in this table's range */
|
/* Allocate from descriptors in this table's range */
|
||||||
const SizeT descEntryStartIndex = tableToEntryIdx(tableIdx);
|
const SizeT descEntryStartIndex = tableToEntryIdx(tableIdx);
|
||||||
const SizeT descEntryEndIndex = Math::min(descEntryStartIndex + descsCountPerTable, allocator.size());
|
const SizeT descEntryEndIndex = Math::min(descEntryStartIndex + descsCountPerTable, allocator.size());
|
||||||
for (SizeT i = 0; i < imgs.size(); ++i)
|
for (SizeT i = 0; i < imgs.size(); ++i)
|
||||||
{
|
{
|
||||||
|
if (viewsExists[i])
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResKey resKey{ gal::EResourceType::Image, GAL_RES_TO_UNTYPE_HNDL(imgs[i]) };
|
||||||
|
auto resToEntryItr = imgResToEntryIdx.find(resKey);
|
||||||
|
|
||||||
|
/* If the same texture is reused in same request? Try to reuse slot here. */
|
||||||
|
if (resToEntryItr != imgResToEntryIdx.end())
|
||||||
|
{
|
||||||
|
/* Find if texture is already inserted in this table. */
|
||||||
|
uint64 entryIdx = resToEntryItr->second;
|
||||||
|
while (isValidTexAt(entryIdx))
|
||||||
|
{
|
||||||
|
if (entryIdxToTable(entryIdx) == tableIdx)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
entryIdx = descEntries[entryIdx].nextEntryIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValidTexAt(entryIdx))
|
||||||
|
{
|
||||||
|
/* Found an entry try to reuse and mark view as exists to skip writing descriptors again */
|
||||||
|
viewsExists[i] = true;
|
||||||
|
retVals[i] = entryIdx;
|
||||||
|
++descEntries[retVals[i]].refCount;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SizeT descIdx = 0;
|
SizeT descIdx = 0;
|
||||||
const bool bAllocated = allocator.findFirstUnsetInRange(descIdx, descEntryStartIndex, descEntryEndIndex - descEntryStartIndex);
|
const bool bAllocated = allocator.findFirstUnsetInRange(descIdx, descEntryStartIndex, descEntryEndIndex - descEntryStartIndex);
|
||||||
debugAssert(bAllocated);
|
debugAssert(bAllocated);
|
||||||
@@ -2651,8 +2779,6 @@ std::vector<uint64> GpGpuBindlessTextures::addImagesToTable(ArrayView<gal::GAL_R
|
|||||||
.bOwningView = true,
|
.bOwningView = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ResKey resKey{ gal::EResourceType::Image, GAL_RES_TO_UNTYPE_HNDL(imgs[i]) };
|
|
||||||
auto resToEntryItr = imgResToEntryIdx.find(resKey);
|
|
||||||
if (resToEntryItr == imgResToEntryIdx.end())
|
if (resToEntryItr == imgResToEntryIdx.end())
|
||||||
{
|
{
|
||||||
/* First entry */
|
/* First entry */
|
||||||
@@ -2686,6 +2812,12 @@ std::vector<uint64> GpGpuBindlessTextures::addImagesToTable(ArrayView<gal::GAL_R
|
|||||||
writeDescInfo.resize(imgs.size());
|
writeDescInfo.resize(imgs.size());
|
||||||
for (SizeT i = 0; i < imgs.size(); ++i)
|
for (SizeT i = 0; i < imgs.size(); ++i)
|
||||||
{
|
{
|
||||||
|
/* If image already exists no need to update descriptor again */
|
||||||
|
if (viewsExists[i])
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
writeDescInfo[i] = {
|
writeDescInfo[i] = {
|
||||||
.hndl = descEntries[retVals[i]].viewHndl,
|
.hndl = descEntries[retVals[i]].viewHndl,
|
||||||
.layout = gal::EImageLayout::ShaderReadOnly,
|
.layout = gal::EImageLayout::ShaderReadOnly,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -113,7 +113,6 @@ public:
|
|||||||
|
|
||||||
void updateRenderThread() noexcept;
|
void updateRenderThread() noexcept;
|
||||||
void clear() noexcept;
|
void clear() noexcept;
|
||||||
|
|
||||||
/* Below functions are preferred to be called from render thread */
|
/* Below functions are preferred to be called from render thread */
|
||||||
CBERENDERER_EXPORT void addToDelete(ResourceVariant res, EDeferredDelStrategy deleteStrategy, TickRep durOrFrameCount = 1);
|
CBERENDERER_EXPORT void addToDelete(ResourceVariant res, EDeferredDelStrategy deleteStrategy, TickRep durOrFrameCount = 1);
|
||||||
void addPipelineToDelete(gal::PipelineHndlVariants res, EDeferredDelStrategy deleteStrategy, TickRep durOrFrameCount = 1)
|
void addPipelineToDelete(gal::PipelineHndlVariants res, EDeferredDelStrategy deleteStrategy, TickRep durOrFrameCount = 1)
|
||||||
@@ -524,7 +523,7 @@ private:
|
|||||||
SCompShaderBlob shaderSrcBlob;
|
SCompShaderBlob shaderSrcBlob;
|
||||||
|
|
||||||
/* Necessary as we provide queries for compiled shaders */
|
/* Necessary as we provide queries for compiled shaders */
|
||||||
CBESpinLock compiledShadersLock;
|
cbe::SpinLock compiledShadersLock;
|
||||||
SCompilationResults compiledResults;
|
SCompilationResults compiledResults;
|
||||||
/* Shaders */
|
/* Shaders */
|
||||||
std::unordered_map<NameString, RasterShaderResults> rasterShaders;
|
std::unordered_map<NameString, RasterShaderResults> rasterShaders;
|
||||||
@@ -534,11 +533,11 @@ private:
|
|||||||
std::array<std::unordered_map<NameString, ShardPermutationMap>, gal::EFeatureSet::MaxFS * MAX_PASS_VARIANTS> materialShaders;
|
std::array<std::unordered_map<NameString, ShardPermutationMap>, gal::EFeatureSet::MaxFS * MAX_PASS_VARIANTS> materialShaders;
|
||||||
|
|
||||||
CompileTask lastCompileTask{ nullptr };
|
CompileTask lastCompileTask{ nullptr };
|
||||||
CBESpinLock shaderCompileEventsLock;
|
cbe::SpinLock shaderCompileEventsLock;
|
||||||
ShadersUpdateEvent shadersCompiledEvent;
|
ShadersUpdateEvent shadersCompiledEvent;
|
||||||
std::unordered_map<NameString, NamedShaderUpdateEvent> namedShadersCompiledEvent;
|
std::unordered_map<NameString, NamedShaderUpdateEvent> namedShadersCompiledEvent;
|
||||||
/* Doing separate deregistration queue to allow deregistration from callback. */
|
/* Doing separate deregistration queue to allow deregistration from callback. */
|
||||||
CBESpinLock shaderCompileEventDeregLock;
|
cbe::SpinLock shaderCompileEventDeregLock;
|
||||||
std::vector<DelegateHandle> shadersCompiledEventDeregQueue;
|
std::vector<DelegateHandle> shadersCompiledEventDeregQueue;
|
||||||
std::vector<std::pair<NameString, DelegateHandle>> namedShadersCompiledEventDeregQueue;
|
std::vector<std::pair<NameString, DelegateHandle>> namedShadersCompiledEventDeregQueue;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date September 2024
|
* \date September 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2024
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -67,5 +67,6 @@ CBE_MATERIAL_STRUCT_BEGIN(CbeMatStruct1, false)
|
|||||||
CBE_MATERIAL_STRUCT_END(CbeMatStruct1)
|
CBE_MATERIAL_STRUCT_END(CbeMatStruct1)
|
||||||
|
|
||||||
REGISTER_MATERIAL_PARAM_DESC(CbeMatStruct1);
|
REGISTER_MATERIAL_PARAM_DESC(CbeMatStruct1);
|
||||||
|
REGISTER_GPU_BUFFER_LAYOUT(CbeMatSInnerStruct);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -53,6 +53,7 @@ constexpr gal::ESamplerFiltering::Type WG_IMG_FILTER = gal::ESamplerFiltering::N
|
|||||||
constexpr static const TChar *COMPUTE_WARP_SIZE_DEF_NAME = TCHAR("G_COMPUTE_WARP_SIZE");
|
constexpr static const TChar *COMPUTE_WARP_SIZE_DEF_NAME = TCHAR("G_COMPUTE_WARP_SIZE");
|
||||||
constexpr uint32 COMPUTE_WARP_SIZE = 16;
|
constexpr uint32 COMPUTE_WARP_SIZE = 16;
|
||||||
|
|
||||||
|
/* This is the max loop unroll count in shader */
|
||||||
constexpr static const TChar *MAX_BATCH_COUNT_DEF_NAME = TCHAR("G_MAX_BATCH_COUNT");
|
constexpr static const TChar *MAX_BATCH_COUNT_DEF_NAME = TCHAR("G_MAX_BATCH_COUNT");
|
||||||
constexpr uint32 MAX_BATCH_COUNT = 8;
|
constexpr uint32 MAX_BATCH_COUNT = 8;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date April 2025
|
* \date April 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -214,7 +214,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
using SMList = SparseVector<SMStat, BitArraySparsityPolicy, false>;
|
using SMList = SparseVector<SMStat, BitArraySparsityPolicy, false>;
|
||||||
CBESpinLock meshesLock;
|
cbe::SpinLock meshesLock;
|
||||||
SMList meshes;
|
SMList meshes;
|
||||||
AddMeshBatch *addMeshList = nullptr;
|
AddMeshBatch *addMeshList = nullptr;
|
||||||
RemoveMeshData *removeMeshList = nullptr;
|
RemoveMeshData *removeMeshList = nullptr;
|
||||||
@@ -223,14 +223,14 @@ private:
|
|||||||
AllocationInUseCounter addMeshAllocTracker;
|
AllocationInUseCounter addMeshAllocTracker;
|
||||||
AllocationInUseCounter rmMeshAllocTracker;
|
AllocationInUseCounter rmMeshAllocTracker;
|
||||||
|
|
||||||
CBESpinLock drawLayerLock;
|
cbe::SpinLock drawLayerLock;
|
||||||
std::array<DrawLayer, DrawLayerMaxCount> drawLayers;
|
std::array<DrawLayer, DrawLayerMaxCount> drawLayers;
|
||||||
Vector2 orthoSize;
|
Vector2 orthoSize;
|
||||||
/* Draws in the screen space using ortho projection for meshes. */
|
/* Draws in the screen space using ortho projection for meshes. */
|
||||||
std::array<DrawLayer, DrawLayerMaxCount> drawOrthoLayers;
|
std::array<DrawLayer, DrawLayerMaxCount> drawOrthoLayers;
|
||||||
std::array<DrawLayerAllocTrackers, DrawLayerMaxCount> drawAllocTrackers;
|
std::array<DrawLayerAllocTrackers, DrawLayerMaxCount> drawAllocTrackers;
|
||||||
|
|
||||||
CBESpinLock selectionProxyLock;
|
cbe::SpinLock selectionProxyLock;
|
||||||
/* Sometimes selection proxy might be valid.
|
/* Sometimes selection proxy might be valid.
|
||||||
* That is because end will not actually clear the selection proxy to reuse the proxy ID
|
* That is because end will not actually clear the selection proxy to reuse the proxy ID
|
||||||
* if same proxy gets used immediately. */
|
* if same proxy gets used immediately. */
|
||||||
@@ -238,7 +238,7 @@ private:
|
|||||||
/* Only used by the renderer to determine upper limit of proxies count to allocate. */
|
/* Only used by the renderer to determine upper limit of proxies count to allocate. */
|
||||||
uint32 maxProxiesCount = 0;
|
uint32 maxProxiesCount = 0;
|
||||||
|
|
||||||
CBESpinLock allocatorLock;
|
cbe::SpinLock allocatorLock;
|
||||||
ArenaAllocator<CBEMemory::CBEAllocator> allocator = { DEFAULT_PAGE_SIZE };
|
ArenaAllocator<CBEMemory::CBEAllocator> allocator = { DEFAULT_PAGE_SIZE };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date July 2024
|
* \date July 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -68,12 +68,12 @@ void WorldDataTransferManager::resetRenderData()
|
|||||||
tempAllocator.reset();
|
tempAllocator.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldDataTransferManager::addLargeTransfer(LargeTransfer transfer)
|
void WorldDataTransferManager::addLargeTransfer(LargeTransfer transfer, MAYBE_UNUSED std::source_location srcLoc)
|
||||||
{
|
{
|
||||||
CBE_ASSERT_INSIDE_RENDERTHREAD();
|
CBE_ASSERT_INSIDE_RENDERTHREAD();
|
||||||
debugAssert(transfer.continuation);
|
debugAssert(transfer.continuation);
|
||||||
|
|
||||||
#if DEBUG_BUILD
|
#if DEBUG_VALIDATIONS_ENABLED
|
||||||
for (const GpuBufferTransfer &gpuTransfer : transfer.bufferGpu2Gpu)
|
for (const GpuBufferTransfer &gpuTransfer : transfer.bufferGpu2Gpu)
|
||||||
{
|
{
|
||||||
debugAssert(rCtx.galContext()->isValid(gpuTransfer.srcHndl));
|
debugAssert(rCtx.galContext()->isValid(gpuTransfer.srcHndl));
|
||||||
@@ -94,6 +94,9 @@ void WorldDataTransferManager::addLargeTransfer(LargeTransfer transfer)
|
|||||||
LargeTransfer *thisTransfer = getLargeTransferAllocator().allocateAligned<LargeTransfer>();
|
LargeTransfer *thisTransfer = getLargeTransferAllocator().allocateAligned<LargeTransfer>();
|
||||||
new (thisTransfer) LargeTransfer(transfer);
|
new (thisTransfer) LargeTransfer(transfer);
|
||||||
thisTransfer->next = nullptr;
|
thisTransfer->next = nullptr;
|
||||||
|
#if DEBUG_BUILD
|
||||||
|
thisTransfer->from = srcLoc.function_name();
|
||||||
|
#endif
|
||||||
|
|
||||||
if (largeTransfersHead == nullptr)
|
if (largeTransfersHead == nullptr)
|
||||||
{
|
{
|
||||||
@@ -111,11 +114,11 @@ void WorldDataTransferManager::addLargeTransfer(LargeTransfer transfer)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldDataTransferManager::addSmallTransfer(SmallTransfer transfer)
|
void WorldDataTransferManager::addSmallTransfer(SmallTransfer transfer, MAYBE_UNUSED std::source_location srcLoc)
|
||||||
{
|
{
|
||||||
CBE_ASSERT_INSIDE_RENDERTHREAD();
|
CBE_ASSERT_INSIDE_RENDERTHREAD();
|
||||||
|
|
||||||
#if DEBUG_BUILD
|
#if DEBUG_VALIDATIONS_ENABLED
|
||||||
SizeT calcTotalTransferSize = 0;
|
SizeT calcTotalTransferSize = 0;
|
||||||
for (const CpuBufferTransfer &cpuTransfer : transfer.bufferCpu2Gpu)
|
for (const CpuBufferTransfer &cpuTransfer : transfer.bufferCpu2Gpu)
|
||||||
{
|
{
|
||||||
@@ -130,6 +133,9 @@ void WorldDataTransferManager::addSmallTransfer(SmallTransfer transfer)
|
|||||||
SmallTransfer *thisTransfer = getSmallTransferAllocator().allocateAligned<SmallTransfer>();
|
SmallTransfer *thisTransfer = getSmallTransferAllocator().allocateAligned<SmallTransfer>();
|
||||||
new (thisTransfer) SmallTransfer(transfer);
|
new (thisTransfer) SmallTransfer(transfer);
|
||||||
thisTransfer->next = nullptr;
|
thisTransfer->next = nullptr;
|
||||||
|
#if DEBUG_BUILD
|
||||||
|
thisTransfer->from = srcLoc.function_name();
|
||||||
|
#endif
|
||||||
|
|
||||||
if (smallTransfersHead == nullptr)
|
if (smallTransfersHead == nullptr)
|
||||||
{
|
{
|
||||||
@@ -747,6 +753,14 @@ void WorldDataTransferManager::consumeGenerateLargeTransfers(
|
|||||||
SizeT bytesOffset = bytesTransferred;
|
SizeT bytesOffset = bytesTransferred;
|
||||||
while (lBufferGpuTransfersPending != nullptr)
|
while (lBufferGpuTransfersPending != nullptr)
|
||||||
{
|
{
|
||||||
|
/* Early out if nothing to transfer */
|
||||||
|
if (lBufferGpuTransfersPending->bufferGpu2Gpu.empty())
|
||||||
|
{
|
||||||
|
debugAssert(transferIndexOffset == 0 && regionIndexOffset == 0 && bytesTransferred == 0);
|
||||||
|
lBufferGpuTransfersPending = LinkedListHelpers::next(lBufferGpuTransfersPending);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("GenBufferGpuTransfer"));
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("GenBufferGpuTransfer"));
|
||||||
|
|
||||||
SizeT tIdx = 0;
|
SizeT tIdx = 0;
|
||||||
@@ -829,6 +843,14 @@ void WorldDataTransferManager::consumeGenerateLargeTransfers(
|
|||||||
uint32 layerOffset = static_cast<uint32>(bytesTransferred);
|
uint32 layerOffset = static_cast<uint32>(bytesTransferred);
|
||||||
while (lImageGpuTransfersPending != nullptr)
|
while (lImageGpuTransfersPending != nullptr)
|
||||||
{
|
{
|
||||||
|
/* Early out if nothing to transfer */
|
||||||
|
if (lImageGpuTransfersPending->imageGpu2Gpu.empty())
|
||||||
|
{
|
||||||
|
debugAssert(transferIndexOffset == 0 && regionIndexOffset == 0 && bytesTransferred == 0);
|
||||||
|
lImageGpuTransfersPending = LinkedListHelpers::next(lImageGpuTransfersPending);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("GenImageGpuTransfer"));
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("GenImageGpuTransfer"));
|
||||||
|
|
||||||
SizeT tIdx = 0;
|
SizeT tIdx = 0;
|
||||||
@@ -980,7 +1002,7 @@ void WorldDataTransferManager::consumeGenerateLargeTransfers(
|
|||||||
rIdx += regionIndexStart;
|
rIdx += regionIndexStart;
|
||||||
regionIndexStart = 0;
|
regionIndexStart = 0;
|
||||||
}
|
}
|
||||||
debugAssert(lImageGpuTransfersPending->imageGpu2Gpu.empty() || (tIdx > 0 && lastLayerOffset > 0));
|
debugAssert((tIdx > 0 && lastLayerOffset > 0));
|
||||||
imageGpuCopiesIdx += tIdx;
|
imageGpuCopiesIdx += tIdx;
|
||||||
/* Add to transfer index which is used in case terminated in middle */
|
/* Add to transfer index which is used in case terminated in middle */
|
||||||
tIdx += transferStart;
|
tIdx += transferStart;
|
||||||
@@ -1024,6 +1046,14 @@ void WorldDataTransferManager::consumeGenerateLargeTransfers(
|
|||||||
SizeT bytesOffset = bytesTransferred;
|
SizeT bytesOffset = bytesTransferred;
|
||||||
while (lBufferCpuTransfersPending != nullptr)
|
while (lBufferCpuTransfersPending != nullptr)
|
||||||
{
|
{
|
||||||
|
/* Early out if nothing to transfer */
|
||||||
|
if (lBufferCpuTransfersPending->bufferCpu2Gpu.empty())
|
||||||
|
{
|
||||||
|
debugAssert(transferIndexOffset == 0 && bytesTransferred == 0);
|
||||||
|
lBufferCpuTransfersPending = LinkedListHelpers::next(lBufferCpuTransfersPending);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("GenBufferCpuTransfer"));
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("GenBufferCpuTransfer"));
|
||||||
|
|
||||||
SizeT tIdx = 0;
|
SizeT tIdx = 0;
|
||||||
@@ -1127,6 +1157,19 @@ void WorldDataTransferManager::consumeGenerateLargeTransfers(
|
|||||||
uint32 layerOffset = static_cast<uint32>(bytesTransferred);
|
uint32 layerOffset = static_cast<uint32>(bytesTransferred);
|
||||||
while (lImageCpuTransfersPending != nullptr)
|
while (lImageCpuTransfersPending != nullptr)
|
||||||
{
|
{
|
||||||
|
/* Early out if nothing to transfer */
|
||||||
|
if (lImageCpuTransfersPending->imageCpu2Gpu.empty())
|
||||||
|
{
|
||||||
|
debugAssert(transferIndexOffset == 0 && regionIndexOffset == 0 && bytesTransferred == 0);
|
||||||
|
|
||||||
|
/* Since this is the last transfer must setup the continuation */
|
||||||
|
outTransferCmds.continuations.emplace_back(lImageCpuTransfersPending->continuation);
|
||||||
|
lImageCpuTransfersPending->continuation = nullptr;
|
||||||
|
lImageCpuTransfersPending = LinkedListHelpers::next(lImageCpuTransfersPending);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("GenImageCpuTransfer"));
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("GenImageCpuTransfer"));
|
||||||
|
|
||||||
SizeT tIdx = 0;
|
SizeT tIdx = 0;
|
||||||
@@ -1928,7 +1971,7 @@ WorldRenderer::DeferredAddTask<std::vector<renderer::MeshId>> WorldRenderer::add
|
|||||||
allSms.allMeshInsts.entries.totalCount(), allSms.allMeshInsts.entries.size() + sms.size(), STATIC_MESHES_BASE_COUNT,
|
allSms.allMeshInsts.entries.totalCount(), allSms.allMeshInsts.entries.size() + sms.size(), STATIC_MESHES_BASE_COUNT,
|
||||||
STATIC_MESHES_RESIZE_FACTOR
|
STATIC_MESHES_RESIZE_FACTOR
|
||||||
);
|
);
|
||||||
LOG_VERBOSE(
|
CBE_LOG_VERBOSE(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Resizing max static meshes count {} to {}", allSms.allMeshInsts.entries.totalCount(),
|
ICbeRendererModule::LOG_CATEGORY, "Resizing max static meshes count {} to {}", allSms.allMeshInsts.entries.totalCount(),
|
||||||
newSmsCount
|
newSmsCount
|
||||||
);
|
);
|
||||||
@@ -1975,7 +2018,7 @@ WorldRenderer::DeferredAddTask<std::vector<renderer::MeshId>> WorldRenderer::add
|
|||||||
),
|
),
|
||||||
iAlignment
|
iAlignment
|
||||||
);
|
);
|
||||||
LOG_VERBOSE(
|
CBE_LOG_VERBOSE(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Resizing max static mesh indices count {} to {}", allSms.idxsAllocator.capacity(), newCount
|
ICbeRendererModule::LOG_CATEGORY, "Resizing max static mesh indices count {} to {}", allSms.idxsAllocator.capacity(), newCount
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1997,7 +2040,7 @@ WorldRenderer::DeferredAddTask<std::vector<renderer::MeshId>> WorldRenderer::add
|
|||||||
),
|
),
|
||||||
vAlignment
|
vAlignment
|
||||||
);
|
);
|
||||||
LOG_VERBOSE(
|
CBE_LOG_VERBOSE(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Resizing max static mesh vertex count {} to {}", allSms.verts.allocator.capacity(),
|
ICbeRendererModule::LOG_CATEGORY, "Resizing max static mesh vertex count {} to {}", allSms.verts.allocator.capacity(),
|
||||||
newCount
|
newCount
|
||||||
);
|
);
|
||||||
@@ -2285,7 +2328,7 @@ WorldRenderer::DeferredAddTask<std::vector<renderer::MeshId>> WorldRenderer::add
|
|||||||
allSms.verts.usableCount = allSms.verts.allocator.capacity();
|
allSms.verts.usableCount = allSms.verts.allocator.capacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Added {} static meshes", sms.size());
|
CBE_LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Added {} static meshes", sms.size());
|
||||||
|
|
||||||
co_return retIds;
|
co_return retIds;
|
||||||
}
|
}
|
||||||
@@ -2306,7 +2349,7 @@ void WorldRenderer::removeStaticMeshes(ArrayView<renderer::MeshId> sms)
|
|||||||
if (const uint32 batchCount = std::get<"batchCount">(smTable); batchCount > constants::SM_BATCHES_INLINE_LIMIT)
|
if (const uint32 batchCount = std::get<"batchCount">(smTable); batchCount > constants::SM_BATCHES_INLINE_LIMIT)
|
||||||
{
|
{
|
||||||
sceneGpData.free(
|
sceneGpData.free(
|
||||||
std::get<"batchBufferIndex">(smTable), std::get<"batchCount">(smTable),
|
std::get<"batchBufferIndex">(smTable), std::get<"batchByteOffset">(smTable),
|
||||||
static_cast<uint32>(sizeof(GalStaticMeshBatchEntry) * batchCount)
|
static_cast<uint32>(sizeof(GalStaticMeshBatchEntry) * batchCount)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2365,7 +2408,7 @@ WorldRenderer::addStaticMeshComps(ArrayView<renderer::StaticMeshRenderableDesc>
|
|||||||
),
|
),
|
||||||
constants::GPU_MIN_BITS_PER_ELEMENT
|
constants::GPU_MIN_BITS_PER_ELEMENT
|
||||||
);
|
);
|
||||||
LOG_VERBOSE(
|
CBE_LOG_VERBOSE(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Resizing max static mesh components count {} to {}", allSmComps.entries.totalCount(),
|
ICbeRendererModule::LOG_CATEGORY, "Resizing max static mesh components count {} to {}", allSmComps.entries.totalCount(),
|
||||||
newSmCompsCount
|
newSmCompsCount
|
||||||
);
|
);
|
||||||
@@ -2529,7 +2572,7 @@ WorldRenderer::addStaticMeshComps(ArrayView<renderer::StaticMeshRenderableDesc>
|
|||||||
|
|
||||||
addMaterialInstRefs(renderer::Mesh_Static, referencedMats);
|
addMaterialInstRefs(renderer::Mesh_Static, referencedMats);
|
||||||
|
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Added {} static mesh components", smComps.size());
|
CBE_LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Added {} static mesh components", smComps.size());
|
||||||
|
|
||||||
co_return retVals;
|
co_return retVals;
|
||||||
}
|
}
|
||||||
@@ -2648,7 +2691,7 @@ WorldRenderer::addTextures2dRaw(ArrayView<renderer::TextureDescRaw2d> texDescs)
|
|||||||
const SizeT newCount = RendererContext::resizeBuffer(
|
const SizeT newCount = RendererContext::resizeBuffer(
|
||||||
allTextures.totalCount(), allTextures.size() + texDescs.size(), TEXTURES_BASE_COUNT, TEXTURES_RESIZE_FACTOR
|
allTextures.totalCount(), allTextures.size() + texDescs.size(), TEXTURES_BASE_COUNT, TEXTURES_RESIZE_FACTOR
|
||||||
);
|
);
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Resizing max textures count {} to {}", allTextures.totalCount(), newCount);
|
CBE_LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Resizing max textures count {} to {}", allTextures.totalCount(), newCount);
|
||||||
|
|
||||||
allTextures.resize(newCount);
|
allTextures.resize(newCount);
|
||||||
}
|
}
|
||||||
@@ -2819,7 +2862,7 @@ std::vector<renderer::MaterialId> WorldRenderer::addMaterials(ArrayView<NameStri
|
|||||||
/* Resize allMats */
|
/* Resize allMats */
|
||||||
const SizeT newCount
|
const SizeT newCount
|
||||||
= RendererContext::resizeBuffer(allMats.totalCount(), mats.size() + allMats.size(), MATS_BASE_COUNT, MATS_RESIZE_FACTOR);
|
= RendererContext::resizeBuffer(allMats.totalCount(), mats.size() + allMats.size(), MATS_BASE_COUNT, MATS_RESIZE_FACTOR);
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Resizing max materials count {} to {}", allMats.totalCount(), newCount);
|
CBE_LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Resizing max materials count {} to {}", allMats.totalCount(), newCount);
|
||||||
|
|
||||||
allMats.resize(newCount);
|
allMats.resize(newCount);
|
||||||
}
|
}
|
||||||
@@ -2831,7 +2874,7 @@ std::vector<renderer::MaterialId> WorldRenderer::addMaterials(ArrayView<NameStri
|
|||||||
})));
|
})));
|
||||||
recreateShardMaterial(matId);
|
recreateShardMaterial(matId);
|
||||||
}
|
}
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Added {} materials to world!", mats.size());
|
CBE_LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Added {} materials to world!", mats.size());
|
||||||
return retVals;
|
return retVals;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2860,7 +2903,7 @@ void WorldRenderer::removeMaterials(ArrayView<renderer::MaterialId> mats)
|
|||||||
|
|
||||||
for (renderer::EMeshType meshT = renderer::Mesh_Begin; meshT < renderer::Mesh_End; ++meshT)
|
for (renderer::EMeshType meshT = renderer::Mesh_Begin; meshT < renderer::Mesh_End; ++meshT)
|
||||||
{
|
{
|
||||||
LOG_WARN_C(
|
CBE_LOG_WARN_C(
|
||||||
allMats[matId].maxReservedMeshRefs[meshT] != 0, ICbeRendererModule::LOG_CATEGORY,
|
allMats[matId].maxReservedMeshRefs[meshT] != 0, ICbeRendererModule::LOG_CATEGORY,
|
||||||
"Material {} cleared before all mesh(Type {}) derefs", allMats[matId].materialName, meshT
|
"Material {} cleared before all mesh(Type {}) derefs", allMats[matId].materialName, meshT
|
||||||
);
|
);
|
||||||
@@ -3084,7 +3127,7 @@ WorldRenderer::addMaterialInstances(ArrayRange<renderer::MaterialDataDesc> matIn
|
|||||||
newMatInstsCount = RendererContext::resizeBuffer(
|
newMatInstsCount = RendererContext::resizeBuffer(
|
||||||
oldMatInstsCount, matInsts.size() + oldMatInstsCount, MAT_INSTS_BASE_COUNT, MAT_INSTS_RESIZE_FACTOR
|
oldMatInstsCount, matInsts.size() + oldMatInstsCount, MAT_INSTS_BASE_COUNT, MAT_INSTS_RESIZE_FACTOR
|
||||||
);
|
);
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Resizing material instances count {} to {}", oldMatInstsCount, newMatInstsCount);
|
CBE_LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Resizing material instances count {} to {}", oldMatInstsCount, newMatInstsCount);
|
||||||
|
|
||||||
const bool bCreated = rCtx.galContext()->createBuffers(
|
const bool bCreated = rCtx.galContext()->createBuffers(
|
||||||
thisMatInstsBuffer,
|
thisMatInstsBuffer,
|
||||||
@@ -3270,7 +3313,7 @@ WorldRenderer::addMaterialInstances(ArrayRange<renderer::MaterialDataDesc> matIn
|
|||||||
std::sort(material.shardInsts.begin(), material.shardInsts.end());
|
std::sort(material.shardInsts.begin(), material.shardInsts.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Added {} material instances", matInsts.size());
|
CBE_LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Added {} material instances", matInsts.size());
|
||||||
|
|
||||||
co_return retVals;
|
co_return retVals;
|
||||||
}
|
}
|
||||||
@@ -3397,7 +3440,7 @@ void WorldRenderer::addMaterialInstRefs(renderer::EMeshType meshType, ArrayView<
|
|||||||
}
|
}
|
||||||
const uint64 oldTotalRefsCount = meshRefsTotalCount[meshType];
|
const uint64 oldTotalRefsCount = meshRefsTotalCount[meshType];
|
||||||
meshRefsTotalCount[meshType] += newTotalRefsAdded;
|
meshRefsTotalCount[meshType] += newTotalRefsAdded;
|
||||||
LOG_VERBOSE(
|
CBE_LOG_VERBOSE(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Added {} material instances reference. Total resized refs count {}(+{})", matInsts.size(),
|
ICbeRendererModule::LOG_CATEGORY, "Added {} material instances reference. Total resized refs count {}(+{})", matInsts.size(),
|
||||||
meshRefsTotalCount[meshType], newTotalRefsAdded
|
meshRefsTotalCount[meshType], newTotalRefsAdded
|
||||||
);
|
);
|
||||||
@@ -3513,7 +3556,7 @@ void WorldRenderer::removeMaterialInstances(ArrayView<renderer::MaterialDataId>
|
|||||||
* Then mark the material for offset recalculation. */
|
* Then mark the material for offset recalculation. */
|
||||||
for (renderer::EMeshType meshType = renderer::Mesh_Begin; meshType < renderer::Mesh_End; ++meshType)
|
for (renderer::EMeshType meshType = renderer::Mesh_Begin; meshType < renderer::Mesh_End; ++meshType)
|
||||||
{
|
{
|
||||||
LOG_WARN_C(
|
CBE_LOG_WARN_C(
|
||||||
worldMatInst.meshRefs[meshType] != 0, ICbeRendererModule::LOG_CATEGORY,
|
worldMatInst.meshRefs[meshType] != 0, ICbeRendererModule::LOG_CATEGORY,
|
||||||
"Material Instance {} cleared before all mesh(Type {}) derefs", matInstId, meshType
|
"Material Instance {} cleared before all mesh(Type {}) derefs", matInstId, meshType
|
||||||
);
|
);
|
||||||
@@ -4343,7 +4386,7 @@ bool WorldRenderer::isRendererReady() const
|
|||||||
|
|
||||||
void WorldRenderer::onNewShaderCompiled(const ShadersUpdate &updates)
|
void WorldRenderer::onNewShaderCompiled(const ShadersUpdate &updates)
|
||||||
{
|
{
|
||||||
LOG(ICbeRendererModule::LOG_CATEGORY, "New/Update shaders compiled!");
|
CBE_LOG(ICbeRendererModule::LOG_CATEGORY, "New/Update shaders compiled!");
|
||||||
CBE_ENQUEUE_RENDER_COMMAND(ShadersCompiled)
|
CBE_ENQUEUE_RENDER_COMMAND(ShadersCompiled)
|
||||||
(
|
(
|
||||||
[updates, this]
|
[updates, this]
|
||||||
@@ -4412,11 +4455,13 @@ void WorldRenderer::onDdShadersCompiled(const NamedShaderUpdate & /*ddShaderUpda
|
|||||||
{
|
{
|
||||||
rCtx.resourceDeleter.addPipelineToDelete(oldHndl, EDeferredDelStrategy::NextSwapchainSync);
|
rCtx.resourceDeleter.addPipelineToDelete(oldHndl, EDeferredDelStrategy::NextSwapchainSync);
|
||||||
}
|
}
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Recreated shader {}", shader_consts::frustum_cull::IM_DD_SHADER_NAME);
|
CBE_LOG_VERBOSE(
|
||||||
|
ICbeRendererModule::LOG_CATEGORY, "Recreated shader {}", shader_consts::frustum_cull::IM_DD_SHADER_NAME
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Compute pipeline({}) create failed!",
|
ICbeRendererModule::LOG_CATEGORY, "Compute pipeline({}) create failed!",
|
||||||
shader_consts::frustum_cull::IM_DD_SHADER_NAME
|
shader_consts::frustum_cull::IM_DD_SHADER_NAME
|
||||||
);
|
);
|
||||||
@@ -4465,14 +4510,14 @@ void WorldRenderer::onDdShadersCompiled(const NamedShaderUpdate & /*ddShaderUpda
|
|||||||
{
|
{
|
||||||
rCtx.resourceDeleter.addPipelineToDelete(oldHndl, EDeferredDelStrategy::NextSwapchainSync);
|
rCtx.resourceDeleter.addPipelineToDelete(oldHndl, EDeferredDelStrategy::NextSwapchainSync);
|
||||||
}
|
}
|
||||||
LOG_VERBOSE(
|
CBE_LOG_VERBOSE(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Recreated shader {}",
|
ICbeRendererModule::LOG_CATEGORY, "Recreated shader {}",
|
||||||
shader_consts::frustum_cull::IM_DD_GEN_DRAWLIST_SHADER_NAME
|
shader_consts::frustum_cull::IM_DD_GEN_DRAWLIST_SHADER_NAME
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Compute pipeline({}) create failed!",
|
ICbeRendererModule::LOG_CATEGORY, "Compute pipeline({}) create failed!",
|
||||||
shader_consts::frustum_cull::IM_DD_GEN_DRAWLIST_SHADER_NAME
|
shader_consts::frustum_cull::IM_DD_GEN_DRAWLIST_SHADER_NAME
|
||||||
);
|
);
|
||||||
@@ -4568,7 +4613,7 @@ void WorldRenderer::onDdShadersCompiled(const NamedShaderUpdate & /*ddShaderUpda
|
|||||||
}
|
}
|
||||||
if (bPipelineCreated)
|
if (bPipelineCreated)
|
||||||
{
|
{
|
||||||
LOG_VERBOSE(
|
CBE_LOG_VERBOSE(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Recreated shader {} and {} for Triangle Draws",
|
ICbeRendererModule::LOG_CATEGORY, "Recreated shader {} and {} for Triangle Draws",
|
||||||
shader_consts::direct_draw::DRAW_PRIMITIVES_SHADER_NAME,
|
shader_consts::direct_draw::DRAW_PRIMITIVES_SHADER_NAME,
|
||||||
shader_consts::direct_draw::DRAW_PRIMITIVES_SEL_PROXY_SHADER_NAME
|
shader_consts::direct_draw::DRAW_PRIMITIVES_SEL_PROXY_SHADER_NAME
|
||||||
@@ -4576,7 +4621,7 @@ void WorldRenderer::onDdShadersCompiled(const NamedShaderUpdate & /*ddShaderUpda
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Graphics pipeline({}/{}) for Triangle Draws create failed!",
|
ICbeRendererModule::LOG_CATEGORY, "Graphics pipeline({}/{}) for Triangle Draws create failed!",
|
||||||
shader_consts::direct_draw::DRAW_PRIMITIVES_SHADER_NAME,
|
shader_consts::direct_draw::DRAW_PRIMITIVES_SHADER_NAME,
|
||||||
shader_consts::direct_draw::DRAW_PRIMITIVES_SEL_PROXY_SHADER_NAME
|
shader_consts::direct_draw::DRAW_PRIMITIVES_SEL_PROXY_SHADER_NAME
|
||||||
@@ -4616,7 +4661,7 @@ void WorldRenderer::onDdShadersCompiled(const NamedShaderUpdate & /*ddShaderUpda
|
|||||||
}
|
}
|
||||||
if (bPipelineCreated)
|
if (bPipelineCreated)
|
||||||
{
|
{
|
||||||
LOG_VERBOSE(
|
CBE_LOG_VERBOSE(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Recreated shader {} and {} for Line Draws",
|
ICbeRendererModule::LOG_CATEGORY, "Recreated shader {} and {} for Line Draws",
|
||||||
shader_consts::direct_draw::DRAW_PRIMITIVES_SHADER_NAME,
|
shader_consts::direct_draw::DRAW_PRIMITIVES_SHADER_NAME,
|
||||||
shader_consts::direct_draw::DRAW_PRIMITIVES_SEL_PROXY_SHADER_NAME
|
shader_consts::direct_draw::DRAW_PRIMITIVES_SEL_PROXY_SHADER_NAME
|
||||||
@@ -4624,7 +4669,7 @@ void WorldRenderer::onDdShadersCompiled(const NamedShaderUpdate & /*ddShaderUpda
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Graphics pipeline({}/{}) for Line Draws create failed!",
|
ICbeRendererModule::LOG_CATEGORY, "Graphics pipeline({}/{}) for Line Draws create failed!",
|
||||||
shader_consts::direct_draw::DRAW_PRIMITIVES_SHADER_NAME,
|
shader_consts::direct_draw::DRAW_PRIMITIVES_SHADER_NAME,
|
||||||
shader_consts::direct_draw::DRAW_PRIMITIVES_SEL_PROXY_SHADER_NAME
|
shader_consts::direct_draw::DRAW_PRIMITIVES_SEL_PROXY_SHADER_NAME
|
||||||
@@ -4712,7 +4757,7 @@ void WorldRenderer::onDdShadersCompiled(const NamedShaderUpdate & /*ddShaderUpda
|
|||||||
}
|
}
|
||||||
if (bPipelineCreated)
|
if (bPipelineCreated)
|
||||||
{
|
{
|
||||||
LOG_VERBOSE(
|
CBE_LOG_VERBOSE(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Recreated shader {} and {}",
|
ICbeRendererModule::LOG_CATEGORY, "Recreated shader {} and {}",
|
||||||
shader_consts::direct_draw::DRAW_INSTANCES_SHADER_NAME,
|
shader_consts::direct_draw::DRAW_INSTANCES_SHADER_NAME,
|
||||||
shader_consts::direct_draw::DRAW_INSTANCES_SEL_PROXY_SHADER_NAME
|
shader_consts::direct_draw::DRAW_INSTANCES_SEL_PROXY_SHADER_NAME
|
||||||
@@ -4720,7 +4765,7 @@ void WorldRenderer::onDdShadersCompiled(const NamedShaderUpdate & /*ddShaderUpda
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Graphics pipeline({}/{}) create failed!",
|
ICbeRendererModule::LOG_CATEGORY, "Graphics pipeline({}/{}) create failed!",
|
||||||
shader_consts::direct_draw::DRAW_INSTANCES_SHADER_NAME,
|
shader_consts::direct_draw::DRAW_INSTANCES_SHADER_NAME,
|
||||||
shader_consts::direct_draw::DRAW_INSTANCES_SEL_PROXY_SHADER_NAME
|
shader_consts::direct_draw::DRAW_INSTANCES_SEL_PROXY_SHADER_NAME
|
||||||
@@ -5317,7 +5362,7 @@ void WorldRenderer::recreateSmFrustumCull(WorldRenderer *self, ShaderPipeline *)
|
|||||||
rCtx.resourceDeleter.addPipelineToDelete(oldHndl, EDeferredDelStrategy::NextSwapchainSync);
|
rCtx.resourceDeleter.addPipelineToDelete(oldHndl, EDeferredDelStrategy::NextSwapchainSync);
|
||||||
}
|
}
|
||||||
self->sceneGpData.setup(p.hndl, shader_consts::GP_SCENE_DATA_ENTRY.first);
|
self->sceneGpData.setup(p.hndl, shader_consts::GP_SCENE_DATA_ENTRY.first);
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Recreated shader {}", shader_consts::frustum_cull::SM_SHADER_NAME);
|
CBE_LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Recreated shader {}", shader_consts::frustum_cull::SM_SHADER_NAME);
|
||||||
}
|
}
|
||||||
/* There is possibility for table to be outdated before pipeline is even created */
|
/* There is possibility for table to be outdated before pipeline is even created */
|
||||||
if (!rCtx.galContext()->isValid(p.hndl))
|
if (!rCtx.galContext()->isValid(p.hndl))
|
||||||
@@ -5471,7 +5516,7 @@ void WorldRenderer::recreateLitModelResolve(WorldRenderer *self, ShaderPipeline
|
|||||||
{
|
{
|
||||||
rCtx.resourceDeleter.addPipelineToDelete(oldHndl, EDeferredDelStrategy::NextSwapchainSync);
|
rCtx.resourceDeleter.addPipelineToDelete(oldHndl, EDeferredDelStrategy::NextSwapchainSync);
|
||||||
}
|
}
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Recreated shader {}", shader_consts::model_lit::COLOR_RESOLVE_SHADER_NAME);
|
CBE_LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Recreated shader {}", shader_consts::model_lit::COLOR_RESOLVE_SHADER_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There is possibility for table to be outdated before pipeline is even created */
|
/* There is possibility for table to be outdated before pipeline is even created */
|
||||||
@@ -5551,7 +5596,7 @@ void WorldRenderer::recreateDepthResolve(WorldRenderer *self, ShaderPipeline *)
|
|||||||
{
|
{
|
||||||
rCtx.resourceDeleter.addPipelineToDelete(oldHndl, EDeferredDelStrategy::NextSwapchainSync);
|
rCtx.resourceDeleter.addPipelineToDelete(oldHndl, EDeferredDelStrategy::NextSwapchainSync);
|
||||||
}
|
}
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Recreated shader {}", shader_consts::debugging::DEPTH_RESOLVE_SHADER_NAME);
|
CBE_LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Recreated shader {}", shader_consts::debugging::DEPTH_RESOLVE_SHADER_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There is possibility for table to be outdated before pipeline is even created */
|
/* There is possibility for table to be outdated before pipeline is even created */
|
||||||
@@ -7619,7 +7664,7 @@ copat::JobSystemFuncAwaiter WorldRenderer::issueImDrawTransfers(SizeT fDatIdx) n
|
|||||||
allImDdMeshes.allMeshInsts.entries.totalCount(), allImDdMeshes.allMeshInsts.entries.size() + addedMeshCount, IM_MESHES_BASE_COUNT,
|
allImDdMeshes.allMeshInsts.entries.totalCount(), allImDdMeshes.allMeshInsts.entries.size() + addedMeshCount, IM_MESHES_BASE_COUNT,
|
||||||
IM_MESHES_RESIZE_FACTOR
|
IM_MESHES_RESIZE_FACTOR
|
||||||
);
|
);
|
||||||
LOG_VERBOSE(
|
CBE_LOG_VERBOSE(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Resizing max immediate draw meshes count {} to {}",
|
ICbeRendererModule::LOG_CATEGORY, "Resizing max immediate draw meshes count {} to {}",
|
||||||
allImDdMeshes.allMeshInsts.entries.totalCount(), newImDdMeshCount
|
allImDdMeshes.allMeshInsts.entries.totalCount(), newImDdMeshCount
|
||||||
);
|
);
|
||||||
@@ -7680,7 +7725,7 @@ copat::JobSystemFuncAwaiter WorldRenderer::issueImDrawTransfers(SizeT fDatIdx) n
|
|||||||
),
|
),
|
||||||
iAlignment
|
iAlignment
|
||||||
);
|
);
|
||||||
LOG_VERBOSE(
|
CBE_LOG_VERBOSE(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Resizing max immediate debug draw mesh indices count {} to {}",
|
ICbeRendererModule::LOG_CATEGORY, "Resizing max immediate debug draw mesh indices count {} to {}",
|
||||||
allImDdMeshes.idxsAllocator.capacity(), newCount
|
allImDdMeshes.idxsAllocator.capacity(), newCount
|
||||||
);
|
);
|
||||||
@@ -7703,7 +7748,7 @@ copat::JobSystemFuncAwaiter WorldRenderer::issueImDrawTransfers(SizeT fDatIdx) n
|
|||||||
),
|
),
|
||||||
vAlignment
|
vAlignment
|
||||||
);
|
);
|
||||||
LOG_VERBOSE(
|
CBE_LOG_VERBOSE(
|
||||||
ICbeRendererModule::LOG_CATEGORY, "Resizing max immediate debug draw mesh vertex count {} to {}",
|
ICbeRendererModule::LOG_CATEGORY, "Resizing max immediate debug draw mesh vertex count {} to {}",
|
||||||
allImDdMeshes.verts.allocator.capacity(), newCount
|
allImDdMeshes.verts.allocator.capacity(), newCount
|
||||||
);
|
);
|
||||||
@@ -8088,7 +8133,7 @@ copat::JobSystemFuncAwaiter WorldRenderer::issueImDrawTransfers(SizeT fDatIdx) n
|
|||||||
|
|
||||||
/* Complete the add in draw context */
|
/* Complete the add in draw context */
|
||||||
imDdDrawContext.INTERNAL_completeAddMeshes(meshHndToMeshId);
|
imDdDrawContext.INTERNAL_completeAddMeshes(meshHndToMeshId);
|
||||||
LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Added {} immediate debug draw meshes", addedMeshCount);
|
CBE_LOG_VERBOSE(ICbeRendererModule::LOG_CATEGORY, "Added {} immediate debug draw meshes", addedMeshCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bImDdMeshResized)
|
if (bImDdMeshResized)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date July 2024
|
* \date July 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -126,6 +126,11 @@ public:
|
|||||||
|
|
||||||
std::coroutine_handle<void> continuation;
|
std::coroutine_handle<void> continuation;
|
||||||
|
|
||||||
|
/* To help with debugging */
|
||||||
|
#if DEBUG_BUILD
|
||||||
|
const char *from;
|
||||||
|
#endif
|
||||||
|
|
||||||
LargeTransfer *next = nullptr;
|
LargeTransfer *next = nullptr;
|
||||||
};
|
};
|
||||||
struct SmallTransfer
|
struct SmallTransfer
|
||||||
@@ -133,6 +138,11 @@ public:
|
|||||||
ArrayView<CpuBufferTransfer> bufferCpu2Gpu;
|
ArrayView<CpuBufferTransfer> bufferCpu2Gpu;
|
||||||
SizeT totalCpu2GpuSize = 0;
|
SizeT totalCpu2GpuSize = 0;
|
||||||
|
|
||||||
|
/* To help with debugging */
|
||||||
|
#if DEBUG_BUILD
|
||||||
|
const char *from;
|
||||||
|
#endif
|
||||||
|
|
||||||
SmallTransfer *next = nullptr;
|
SmallTransfer *next = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -182,8 +192,8 @@ public:
|
|||||||
return frameData[fDatIdx].smallTransferComplete;
|
return frameData[fDatIdx].smallTransferComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addLargeTransfer(LargeTransfer transfer);
|
void addLargeTransfer(LargeTransfer transfer, std::source_location srcLoc = std::source_location::current());
|
||||||
void addSmallTransfer(SmallTransfer transfer);
|
void addSmallTransfer(SmallTransfer transfer, std::source_location srcLoc = std::source_location::current());
|
||||||
|
|
||||||
/* Setting up all frame commands pools after every update as CommandPools gets copied on resize */
|
/* Setting up all frame commands pools after every update as CommandPools gets copied on resize */
|
||||||
void setupNewFrameData(ArrayView<CommandPools *> frameCmdsPools, SizeT fDatIdx);
|
void setupNewFrameData(ArrayView<CommandPools *> frameCmdsPools, SizeT fDatIdx);
|
||||||
@@ -320,6 +330,8 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO(Jeslas) : Default initialize GAL Resources = {} if ever it gets dynamically recreated, or garbage might point to valid one.
|
||||||
|
|
||||||
class CBERENDERER_EXPORT WorldRenderer
|
class CBERENDERER_EXPORT WorldRenderer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -525,11 +537,11 @@ private:
|
|||||||
* Gets resized on components list resize and material instance references gets added.
|
* Gets resized on components list resize and material instance references gets added.
|
||||||
* Offsets gets calculated from material instance references count.
|
* Offsets gets calculated from material instance references count.
|
||||||
* Gets filled from the culling shader. */
|
* Gets filled from the culling shader. */
|
||||||
gal::GAL_RESOURCE_HND(Buffer) drawListAndCompInstList;
|
gal::GAL_RESOURCE_HND(Buffer) drawListAndCompInstList = {};
|
||||||
/* One counter and offset for each material instance/data.
|
/* One counter and offset for each material instance/data.
|
||||||
* Gets resized on material instances getting added before.
|
* Gets resized on material instances getting added before.
|
||||||
* Offsets gets filled when material instance references gets added. */
|
* Offsets gets filled when material instance references gets added. */
|
||||||
gal::GAL_RESOURCE_HND(Buffer) perMatInstCounterAndOffset;
|
gal::GAL_RESOURCE_HND(Buffer) perMatInstCounterAndOffset = {};
|
||||||
|
|
||||||
static SizeT drawListBufferByteSize(SizeT matRefsCount, uint32 bufferOffsetAlignment)
|
static SizeT drawListBufferByteSize(SizeT matRefsCount, uint32 bufferOffsetAlignment)
|
||||||
{
|
{
|
||||||
@@ -581,8 +593,8 @@ private:
|
|||||||
#pragma region Textures
|
#pragma region Textures
|
||||||
struct WorldTexture
|
struct WorldTexture
|
||||||
{
|
{
|
||||||
gal::GAL_RESOURCE_HND(Image) img;
|
gal::GAL_RESOURCE_HND(Image) img = {};
|
||||||
gal::GAL_RESOURCE_HND(ImageView) imgView;
|
gal::GAL_RESOURCE_HND(ImageView) imgView = {};
|
||||||
|
|
||||||
bool bOwnImg:1 = false;
|
bool bOwnImg:1 = false;
|
||||||
bool bOwnImgView:1 = false;
|
bool bOwnImgView:1 = false;
|
||||||
@@ -668,7 +680,7 @@ private:
|
|||||||
* 2. Triangles */
|
* 2. Triangles */
|
||||||
struct ImDdPrimitive
|
struct ImDdPrimitive
|
||||||
{
|
{
|
||||||
gal::GAL_RESOURCE_HND(Buffer) buffer;
|
gal::GAL_RESOURCE_HND(Buffer) buffer = {};
|
||||||
/* Total count across layers */
|
/* Total count across layers */
|
||||||
uint64 linesCount = 0;
|
uint64 linesCount = 0;
|
||||||
uint64 trisCount = 0;
|
uint64 trisCount = 0;
|
||||||
@@ -700,7 +712,7 @@ private:
|
|||||||
/* First half is Mesh Instances buffer that is filled from CPU and are not ordered.
|
/* First half is Mesh Instances buffer that is filled from CPU and are not ordered.
|
||||||
* Second half is Mesh Instances buffer that gets filled in order based on Instance offsets in Cull shader.
|
* Second half is Mesh Instances buffer that gets filled in order based on Instance offsets in Cull shader.
|
||||||
* No need to memory zero the instances buffer. */
|
* No need to memory zero the instances buffer. */
|
||||||
gal::GAL_RESOURCE_HND(Buffer) instsBuffer;
|
gal::GAL_RESOURCE_HND(Buffer) instsBuffer = {};
|
||||||
/* Total count Necessary to resize instsBuffer */
|
/* Total count Necessary to resize instsBuffer */
|
||||||
uint64 maxInstsCount = 0;
|
uint64 maxInstsCount = 0;
|
||||||
/* Below offsets are necessary to setup per layer, per mode culling descriptor sets with ease. */
|
/* Below offsets are necessary to setup per layer, per mode culling descriptor sets with ease. */
|
||||||
@@ -712,18 +724,18 @@ private:
|
|||||||
* Will be prefilled by CPU and offsets points to region in instsBuffer where the visible instance data gets filled in.
|
* Will be prefilled by CPU and offsets points to region in instsBuffer where the visible instance data gets filled in.
|
||||||
* Will only be read by Cull and Draw list fill shaders.
|
* Will only be read by Cull and Draw list fill shaders.
|
||||||
* 1 for each usable ImDdMesh in allImDdMeshes. Gets resized when allImDdMeshes resizes. */
|
* 1 for each usable ImDdMesh in allImDdMeshes. Gets resized when allImDdMeshes resizes. */
|
||||||
gal::GAL_RESOURCE_HND(Buffer) instsOffsetsBuffer;
|
gal::GAL_RESOURCE_HND(Buffer) instsOffsetsBuffer = {};
|
||||||
/* Will be filled by Cull shader and read by Vertex shader. This counter will be used to skip instances in draw calls.
|
/* Will be filled by Cull shader and read by Vertex shader. This counter will be used to skip instances in draw calls.
|
||||||
* [0 to meshCount) will be the instances that pass the culling counter per mesh.
|
* [0 to meshCount) will be the instances that pass the culling counter per mesh.
|
||||||
* At meshCount it will have draw call counter which gets filled by Fill draw calls shader.
|
* At meshCount it will have draw call counter which gets filled by Fill draw calls shader.
|
||||||
* 1 for each usable ImDdMesh in allImDdMeshes. Gets resized when allImDdMeshes resizes.
|
* 1 for each usable ImDdMesh in allImDdMeshes. Gets resized when allImDdMeshes resizes.
|
||||||
* **NOTE** Must be zero before culling. */
|
* **NOTE** Must be zero before culling. */
|
||||||
gal::GAL_RESOURCE_HND(Buffer) instsDrawsCountersBuffer;
|
gal::GAL_RESOURCE_HND(Buffer) instsDrawsCountersBuffer = {};
|
||||||
/* Will be prefilled by CPU and offsets points to final draw offsets. Will only be read by command buffer.
|
/* Will be prefilled by CPU and offsets points to final draw offsets. Will only be read by command buffer.
|
||||||
* Filling draw list requires instance offset in instsBuffer(instsOffsets), instsDrawCount(instsDrawsCounters) and mesh table data.
|
* Filling draw list requires instance offset in instsBuffer(instsOffsets), instsDrawCount(instsDrawsCounters) and mesh table data.
|
||||||
* 1 for each usable ImDdMesh in allImDdMeshes. Gets resized when allImDdMeshes resizes.
|
* 1 for each usable ImDdMesh in allImDdMeshes. Gets resized when allImDdMeshes resizes.
|
||||||
* **NOTE** Must be zero before culling. */
|
* **NOTE** Must be zero before culling. */
|
||||||
gal::GAL_RESOURCE_HND(Buffer) indirectDrawListBuffer;
|
gal::GAL_RESOURCE_HND(Buffer) indirectDrawListBuffer = {};
|
||||||
/* Number of unique meshes drawn per layer and mode */
|
/* Number of unique meshes drawn per layer and mode */
|
||||||
using DrawsPerDrawMode = std::array<uint32, WorldImDrawContext::DrawModeMaxCount>;
|
using DrawsPerDrawMode = std::array<uint32, WorldImDrawContext::DrawModeMaxCount>;
|
||||||
std::array<DrawsPerDrawMode, WorldImDrawContext::DrawLayerMaxCount> drawsCountPerLayer;
|
std::array<DrawsPerDrawMode, WorldImDrawContext::DrawLayerMaxCount> drawsCountPerLayer;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -176,7 +176,9 @@ void GalWgWindowRenderer::renderDrawCmds(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
fdat.geomBytes = totalGeomBytes;
|
fdat.geomBytes = totalGeomBytes;
|
||||||
LOG_ERROR_C(!bGeomBufferRecreated, ICbeRendererModule::LOG_CATEGORY, "Failed to create widget geometry buffer for {}", name);
|
CBE_LOG_ERROR_C(
|
||||||
|
!bGeomBufferRecreated, ICbeRendererModule::LOG_CATEGORY, "Failed to create widget geometry buffer for {}", name
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!galCtx->isValid(fdat.geomBuffer) || fdat.geomBytes == 0)
|
if (!galCtx->isValid(fdat.geomBuffer) || fdat.geomBytes == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date June 2024
|
* \date June 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -138,7 +138,7 @@ private:
|
|||||||
uint32 descTblIdx;
|
uint32 descTblIdx;
|
||||||
};
|
};
|
||||||
/* resBatches will be accessed by main thread occasionally with WgImGui widget or any widget that uses dynamic textures */
|
/* resBatches will be accessed by main thread occasionally with WgImGui widget or any widget that uses dynamic textures */
|
||||||
CBESpinLock mainThreadLock;
|
cbe::SpinLock mainThreadLock;
|
||||||
/* Thread shared read and write */
|
/* Thread shared read and write */
|
||||||
SparseVector<TextureResourceData, BitArraySparsityPolicy> texEntries;
|
SparseVector<TextureResourceData, BitArraySparsityPolicy> texEntries;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas Pravin
|
* \author Jeslas Pravin
|
||||||
* \date September 2024
|
* \date September 2024
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -134,7 +134,7 @@ T getMaterialBuffer(uint baseInstanceIndex)
|
|||||||
}
|
}
|
||||||
uint16_t getAnisoSetting()
|
uint16_t getAnisoSetting()
|
||||||
{
|
{
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CBE_MAT_SEL_PROXY_PASS
|
#if CBE_MAT_SEL_PROXY_PASS
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date March 2022
|
* \date March 2022
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -21,8 +21,8 @@ class CoreObjectsModule final : public ICoreObjectsModule
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
CoreObjectsDB objsDb;
|
CoreObjectsDB objsDb;
|
||||||
CoreObjectGC gc;
|
|
||||||
CBEPackageManager packMan;
|
CBEPackageManager packMan;
|
||||||
|
CoreObjectGC gc;
|
||||||
|
|
||||||
static CoreObjectsDB *objsDbPtr;
|
static CoreObjectsDB *objsDbPtr;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* \author Jeslas
|
* \author Jeslas
|
||||||
* \date July 2025
|
* \date July 2025
|
||||||
* \copyright
|
* \copyright
|
||||||
* Copyright (C) Jeslas Pravin, 2022-2025
|
* Copyright (C) Jeslas Pravin, 2022-2026
|
||||||
* @jeslaspravin pravinjeslas@gmail.com
|
* @jeslaspravin pravinjeslas@gmail.com
|
||||||
* License can be read in LICENSE file at this repository's root
|
* License can be read in LICENSE file at this repository's root
|
||||||
*/
|
*/
|
||||||
@@ -29,6 +29,7 @@ AssetManager::AssetManager()
|
|||||||
, contentDirMountEventHnd(CoreObjectDelegates::onContentDirectoryAdded.bindObject(this, &AssetManager::onContentDirMount))
|
, contentDirMountEventHnd(CoreObjectDelegates::onContentDirectoryAdded.bindObject(this, &AssetManager::onContentDirMount))
|
||||||
, contentDirUnmountEventHnd(CoreObjectDelegates::onContentDirectoryRemoved.bindObject(this, &AssetManager::onContentDirUnmount))
|
, contentDirUnmountEventHnd(CoreObjectDelegates::onContentDirectoryRemoved.bindObject(this, &AssetManager::onContentDirUnmount))
|
||||||
#endif
|
#endif
|
||||||
|
, assetManLock(std::make_unique<std::shared_mutex>())
|
||||||
{}
|
{}
|
||||||
AssetManager::~AssetManager()
|
AssetManager::~AssetManager()
|
||||||
{
|
{
|
||||||
@@ -59,10 +60,14 @@ AssetManager::~AssetManager()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
classToPackages.clear();
|
classToPackages.clear();
|
||||||
|
|
||||||
|
assetManLock.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetManager::TreeNodeIdx AssetManager::findAssetUnder(StringView assetName, TreeNodeIdx folderTreeIdx, bool bRecurse) const
|
AssetManager::TreeNodeIdx AssetManager::findAssetUnder(StringView assetName, TreeNodeIdx folderTreeIdx, bool bRecurse) const
|
||||||
{
|
{
|
||||||
|
const std::shared_lock readLock{ *assetManLock.get() };
|
||||||
|
|
||||||
debugAssert(folderTree.isValid(folderTreeIdx));
|
debugAssert(folderTree.isValid(folderTreeIdx));
|
||||||
std::vector<TreeNodeIdx> folderTreeIdxs;
|
std::vector<TreeNodeIdx> folderTreeIdxs;
|
||||||
folderTree.getChildren(folderTreeIdxs, folderTreeIdx, bRecurse);
|
folderTree.getChildren(folderTreeIdxs, folderTreeIdx, bRecurse);
|
||||||
@@ -89,6 +94,7 @@ void AssetManager::getClassPackages(std::vector<PackageAllocator::AllocHandle> &
|
|||||||
children.emplace_back(baseClass);
|
children.emplace_back(baseClass);
|
||||||
IReflectionRuntimeModule::get()->getChildsOf(baseClass, children, bRecurse);
|
IReflectionRuntimeModule::get()->getChildsOf(baseClass, children, bRecurse);
|
||||||
|
|
||||||
|
const std::shared_lock readLock{ *assetManLock.get() };
|
||||||
for (CBEClass subclass : children)
|
for (CBEClass subclass : children)
|
||||||
{
|
{
|
||||||
auto itr = classToPackages.find(subclass);
|
auto itr = classToPackages.find(subclass);
|
||||||
@@ -107,8 +113,10 @@ void AssetManager::getClassPackages(std::vector<PackageAllocator::AllocHandle> &
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if CBE_ENABLE_PACKAGE_REFERENCE_TREE
|
#if CBE_ENABLE_PACKAGE_REFERENCE_TREE
|
||||||
void AssetManager::getPackageReferrers(std::vector<PackageAllocator::AllocHandle> &outPackageHnds, TreeNodeIdx packageIdx) const
|
void AssetManager::getPackageReferrersFromRefTree(std::vector<PackageAllocator::AllocHandle> &outPackageHnds, TreeNodeIdx packageIdx) const
|
||||||
{
|
{
|
||||||
|
const std::shared_lock readLock{ *assetManLock.get() };
|
||||||
|
|
||||||
debugAssert(folderTree.isValid(packageIdx) && folderTree[packageIdx].nodeData.index() == FolderTree_Package);
|
debugAssert(folderTree.isValid(packageIdx) && folderTree[packageIdx].nodeData.index() == FolderTree_Package);
|
||||||
|
|
||||||
const PackageAllocator::AllocHandle packageHnd = std::get<FolderTree_Package>(folderTree[packageIdx].nodeData);
|
const PackageAllocator::AllocHandle packageHnd = std::get<FolderTree_Package>(folderTree[packageIdx].nodeData);
|
||||||
@@ -120,6 +128,21 @@ void AssetManager::getPackageReferrers(std::vector<PackageAllocator::AllocHandle
|
|||||||
referrerLink = LinkedListHelpers::next(referrerLink);
|
referrerLink = LinkedListHelpers::next(referrerLink);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void AssetManager::getPackageDepsFromRefTree(std::vector<PackageAllocator::AllocHandle> &outPackageHnds, TreeNodeIdx packageIdx) const
|
||||||
|
{
|
||||||
|
const std::shared_lock readLock{ *assetManLock.get() };
|
||||||
|
|
||||||
|
debugAssert(folderTree.isValid(packageIdx) && folderTree[packageIdx].nodeData.index() == FolderTree_Package);
|
||||||
|
|
||||||
|
const PackageAllocator::AllocHandle packageHnd = std::get<FolderTree_Package>(folderTree[packageIdx].nodeData);
|
||||||
|
const AssetPackage *assetPack = packages.getAllocAt(packageHnd);
|
||||||
|
AssetPackageLinkNode *depLink = assetPack->dependencies;
|
||||||
|
while (depLink != nullptr)
|
||||||
|
{
|
||||||
|
outPackageHnds.emplace_back(depLink->assetAllocHnd);
|
||||||
|
depLink = LinkedListHelpers::next(depLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -135,7 +158,7 @@ AssetManager::TreeNodeIdx AssetManager::createFolder(TreeNodeIdx folderIdx, Stri
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &treeNodeData = getFolderInfo(subFolderTreeIdx)->nodeData;
|
const auto &treeNodeData = getFolderInfoNoLock(subFolderTreeIdx)->nodeData;
|
||||||
if (TCharStr::isEqual(std::get<FolderTree_Folder>(treeNodeData).folderName, folderName))
|
if (TCharStr::isEqual(std::get<FolderTree_Folder>(treeNodeData).folderName, folderName))
|
||||||
{
|
{
|
||||||
return subFolderTreeIdx;
|
return subFolderTreeIdx;
|
||||||
@@ -149,13 +172,13 @@ AssetManager::TreeNodeIdx AssetManager::createFolder(TreeNodeIdx folderIdx, Stri
|
|||||||
pathElems.resize(pathElemIdxs.size() + 2);
|
pathElems.resize(pathElemIdxs.size() + 2);
|
||||||
|
|
||||||
debugAssert(
|
debugAssert(
|
||||||
!getFolderInfo(pathElemIdxs.front())->contentDir.empty()
|
!getFolderInfoNoLock(pathElemIdxs.front())->contentDir.empty()
|
||||||
&& FileSystemFunctions::dirExists(getFolderInfo(pathElemIdxs.front())->contentDir.getChar())
|
&& FileSystemFunctions::dirExists(getFolderInfoNoLock(pathElemIdxs.front())->contentDir.getChar())
|
||||||
);
|
);
|
||||||
pathElems.front() = getFolderInfo(pathElemIdxs.front())->contentDir;
|
pathElems.front() = getFolderInfoNoLock(pathElemIdxs.front())->contentDir;
|
||||||
for (uint32 i = 0; i < pathElemIdxs.size(); ++i)
|
for (uint32 i = 0; i < pathElemIdxs.size(); ++i)
|
||||||
{
|
{
|
||||||
pathElems[i + 1] = std::get<FolderTree_Folder>(getFolderInfo(pathElemIdxs[i])->nodeData).folderName;
|
pathElems[i + 1] = std::get<FolderTree_Folder>(getFolderInfoNoLock(pathElemIdxs[i])->nodeData).folderName;
|
||||||
}
|
}
|
||||||
pathElems.back() = folderName;
|
pathElems.back() = folderName;
|
||||||
const String newDirPath = String::join(pathElems.cbegin(), pathElems.cend(), FS_PATH_SEPARATOR);
|
const String newDirPath = String::join(pathElems.cbegin(), pathElems.cend(), FS_PATH_SEPARATOR);
|
||||||
@@ -166,6 +189,7 @@ AssetManager::TreeNodeIdx AssetManager::createFolder(TreeNodeIdx folderIdx, Stri
|
|||||||
FileHelper::makeDir(newDirPath);
|
FileHelper::makeDir(newDirPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::unique_lock writeLock{ *assetManLock.get() };
|
||||||
FolderInfo folderInf{
|
FolderInfo folderInf{
|
||||||
.folderName = folderName,
|
.folderName = folderName,
|
||||||
.folderId = StringID(folderName),
|
.folderId = StringID(folderName),
|
||||||
@@ -176,10 +200,18 @@ AssetManager::TreeNodeIdx AssetManager::createFolder(TreeNodeIdx folderIdx, Stri
|
|||||||
void AssetManager::onPackageDeleted(StringView packagePath)
|
void AssetManager::onPackageDeleted(StringView packagePath)
|
||||||
{
|
{
|
||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("AssetManager"));
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("AssetManager"));
|
||||||
|
AssetManager::TreeNodeIdx folderTreeIdx;
|
||||||
|
AssetManager::PackageAllocator::AllocHandle packageHnd;
|
||||||
|
const AssetPackage *assetPack;
|
||||||
|
{
|
||||||
|
const std::shared_lock readLock{ *assetManLock.get() };
|
||||||
|
|
||||||
auto [folderTreeIdx, packageHnd] = packagePathToPackage(packagePath);
|
std::tie(folderTreeIdx, packageHnd) = packagePathToPackage(packagePath);
|
||||||
const AssetPackage *assetPack = packages.getAllocAt(packageHnd);
|
assetPack = packages.getAllocAt(packageHnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start writing */
|
||||||
|
const std::unique_lock writeLock{ *assetManLock.get() };
|
||||||
#if CBE_ENABLE_PACKAGE_REFERENCE_TREE
|
#if CBE_ENABLE_PACKAGE_REFERENCE_TREE
|
||||||
{
|
{
|
||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("ReferenceTree"));
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("ReferenceTree"));
|
||||||
@@ -217,7 +249,7 @@ void AssetManager::onPackageDeleted(StringView packagePath)
|
|||||||
while (folderTree.isValid(parentFolderTreeIdx) && !folderTree.hasChild(parentFolderTreeIdx))
|
while (folderTree.isValid(parentFolderTreeIdx) && !folderTree.hasChild(parentFolderTreeIdx))
|
||||||
{
|
{
|
||||||
/* Keep the root always, unless it gets unmounted */
|
/* Keep the root always, unless it gets unmounted */
|
||||||
if (isRootFolder(parentFolderTreeIdx))
|
if (isRootFolderNoLock(parentFolderTreeIdx))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -230,34 +262,45 @@ void AssetManager::onPackageDeleted(StringView packagePath)
|
|||||||
/* Finally destroy the asset package */
|
/* Finally destroy the asset package */
|
||||||
destroyAssetPackage(packageHnd);
|
destroyAssetPackage(packageHnd);
|
||||||
}
|
}
|
||||||
void AssetManager::onPackageSaved(Object *pkgObj)
|
void AssetManager::onPackageSaved(MAYBE_UNUSED Object *pkgObj)
|
||||||
{
|
{
|
||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("AssetManager"));
|
|
||||||
#if CBE_ENABLE_PACKAGE_REFERENCE_TREE
|
#if CBE_ENABLE_PACKAGE_REFERENCE_TREE
|
||||||
|
|
||||||
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("AssetManager"));
|
||||||
|
|
||||||
Package *package = cast<Package>(pkgObj);
|
Package *package = cast<Package>(pkgObj);
|
||||||
debugAssert(package != nullptr);
|
debugAssert(package != nullptr);
|
||||||
|
|
||||||
auto [folderTreeIdx, packageHnd] = packagePathToPackage(package->getObjectData().path);
|
AssetManager::TreeNodeIdx folderTreeIdx;
|
||||||
|
AssetManager::PackageAllocator::AllocHandle packageHnd;
|
||||||
|
AssetPackage *assetPack;
|
||||||
|
/* After inserting the new dependencies, this will only contain dependencies that needs to be removed. */
|
||||||
|
std::unordered_set<NameString> prevDependencies;
|
||||||
|
{
|
||||||
|
const std::shared_lock readLock{ *assetManLock.get() };
|
||||||
|
|
||||||
|
std::tie(folderTreeIdx, packageHnd) = packagePathToPackage(package->getObjectData().path);
|
||||||
/* If no package found probably new package so ignore. */
|
/* If no package found probably new package so ignore. */
|
||||||
if (!folderTree.isValid(folderTreeIdx))
|
if (!folderTree.isValid(folderTreeIdx))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* After inserting the new dependencies, this will only contain dependencies that needs to be removed. */
|
assetPack = packages.getAllocAt(packageHnd);
|
||||||
std::unordered_set<NameString> prevDependencies;
|
|
||||||
AssetPackage *assetPack = packages.getAllocAt(packageHnd);
|
|
||||||
AssetPackageLinkNode *oldDep = assetPack->dependencies;
|
AssetPackageLinkNode *oldDep = assetPack->dependencies;
|
||||||
while (oldDep != nullptr)
|
while (oldDep != nullptr)
|
||||||
{
|
{
|
||||||
prevDependencies.insert(NameString{ packages.getAllocAt(oldDep->assetAllocHnd)->packagePath });
|
prevDependencies.insert(NameString{ packages.getAllocAt(oldDep->assetAllocHnd)->packagePath });
|
||||||
oldDep = LinkedListHelpers::next(oldDep);
|
oldDep = LinkedListHelpers::next(oldDep);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const PackageLoaderInfo *loaderInf = packageManager->getPackageLoader(StringID{ assetPack->packagePath });
|
const PackageLoaderInfo *loaderInf = packageManager->getPackageLoader(StringID{ assetPack->packagePath });
|
||||||
debugAssert(loaderInf != nullptr && loaderInf->loader != nullptr);
|
debugAssert(loaderInf != nullptr && loaderInf->loader != nullptr);
|
||||||
|
|
||||||
|
/* Start writing */
|
||||||
|
const std::unique_lock writeLock{ *assetManLock.get() };
|
||||||
|
|
||||||
std::unordered_set<NameString> uniquePkgPath;
|
std::unordered_set<NameString> uniquePkgPath;
|
||||||
for (const PackageDependencyData &dependency : loaderInf->loader->getDependencyObjects())
|
for (const PackageDependencyData &dependency : loaderInf->loader->getDependencyObjects())
|
||||||
{
|
{
|
||||||
@@ -313,6 +356,9 @@ void AssetManager::onContentDirMount(StringView contentDir)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Erase already inserted folder names */
|
/* Erase already inserted folder names */
|
||||||
|
{
|
||||||
|
const std::shared_lock readLock{ *assetManLock.get() };
|
||||||
|
|
||||||
for (const TreeNodeIdx rootIdx : folderTree.getAllRoots())
|
for (const TreeNodeIdx rootIdx : folderTree.getAllRoots())
|
||||||
{
|
{
|
||||||
debugAssert(!isPackageNode(rootIdx));
|
debugAssert(!isPackageNode(rootIdx));
|
||||||
@@ -321,8 +367,10 @@ void AssetManager::onContentDirMount(StringView contentDir)
|
|||||||
const FolderInfo &folderInfo = std::get<FolderTree_Folder>(folderTree[rootIdx].nodeData);
|
const FolderInfo &folderInfo = std::get<FolderTree_Folder>(folderTree[rootIdx].nodeData);
|
||||||
subfolderNames.erase(folderInfo.folderName);
|
subfolderNames.erase(folderInfo.folderName);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Add new folder names */
|
/* Add new folder names */
|
||||||
|
const std::unique_lock writeLock{ *assetManLock.get() };
|
||||||
for (const String &folderName : subfolderNames)
|
for (const String &folderName : subfolderNames)
|
||||||
{
|
{
|
||||||
FolderInfo folderInf{ .folderName = folderName, .folderId = StringID(folderName) };
|
FolderInfo folderInf{ .folderName = folderName, .folderId = StringID(folderName) };
|
||||||
@@ -331,6 +379,8 @@ void AssetManager::onContentDirMount(StringView contentDir)
|
|||||||
}
|
}
|
||||||
void AssetManager::onContentDirUnmount(StringView contentDir)
|
void AssetManager::onContentDirUnmount(StringView contentDir)
|
||||||
{
|
{
|
||||||
|
const std::unique_lock writeLock{ *assetManLock.get() };
|
||||||
|
|
||||||
for (const TreeNodeIdx rootIdx : folderTree.getAllRoots())
|
for (const TreeNodeIdx rootIdx : folderTree.getAllRoots())
|
||||||
{
|
{
|
||||||
debugAssert(!isPackageNode(rootIdx));
|
debugAssert(!isPackageNode(rootIdx));
|
||||||
@@ -354,6 +404,8 @@ void AssetManager::onContentDirUnmount(StringView contentDir)
|
|||||||
void AssetManager::onPackageScanned(PackageLoader *packageLoader, StringView packagePath, StringView packageFilePath, StringView contentDir)
|
void AssetManager::onPackageScanned(PackageLoader *packageLoader, StringView packagePath, StringView packageFilePath, StringView contentDir)
|
||||||
{
|
{
|
||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("AssetManager"));
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("AssetManager"));
|
||||||
|
const std::unique_lock writeLock{ *assetManLock.get() };
|
||||||
|
|
||||||
debugAssert(!packageLoader->getContainedObjects().empty());
|
debugAssert(!packageLoader->getContainedObjects().empty());
|
||||||
|
|
||||||
auto [treeNodeIdx, packageHnd] = ensureAssetPackageExists(packagePath);
|
auto [treeNodeIdx, packageHnd] = ensureAssetPackageExists(packagePath);
|
||||||
@@ -407,12 +459,13 @@ void AssetManager::onPackageScanned(PackageLoader *packageLoader, StringView pac
|
|||||||
alertOncef(bContentDirMatch, "Content directory mismatch. Found \"{}\" Expected \"{}\"", rootInfo.contentDir, contentDir);
|
alertOncef(bContentDirMatch, "Content directory mismatch. Found \"{}\" Expected \"{}\"", rootInfo.contentDir, contentDir);
|
||||||
if (!bContentDirMatch)
|
if (!bContentDirMatch)
|
||||||
{
|
{
|
||||||
LOG_ERROR(
|
CBE_LOG_ERROR(
|
||||||
CATEGORY, "Package's({}) content directory {} does not match the root folder({}) content directory {}", packagePath,
|
CATEGORY, "Package's({}) content directory {} does not match the root folder({}) content directory {}", packagePath,
|
||||||
contentDir, rootFolder.folderName, rootInfo.contentDir
|
contentDir, rootFolder.folderName, rootInfo.contentDir
|
||||||
);
|
);
|
||||||
LOG(CATEGORY,
|
CBE_LOG(
|
||||||
"If content directory relative path of any asset is colliding, prefix the asset relative path to ensure uniqueness!");
|
CATEGORY, "If content directory relative path of any asset is colliding, prefix the asset relative path to ensure uniqueness!"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -598,7 +651,7 @@ std::pair<AssetManager::TreeNodeIdx, AssetManager::PackageAllocator::AllocHandle
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const TreeNodeIdx parentNodeIdx = pathToFolderIdxInFolderTree(packagePath);
|
const TreeNodeIdx parentNodeIdx = pathToFolderIdxInFolderTreeNoLock(packagePath);
|
||||||
std::vector<TreeNodeIdx> leafNodeIdxs;
|
std::vector<TreeNodeIdx> leafNodeIdxs;
|
||||||
const bool bRecurse = false;
|
const bool bRecurse = false;
|
||||||
folderTree.getChildren(leafNodeIdxs, parentNodeIdx, bRecurse);
|
folderTree.getChildren(leafNodeIdxs, parentNodeIdx, bRecurse);
|
||||||
@@ -623,7 +676,7 @@ std::pair<AssetManager::TreeNodeIdx, AssetManager::PackageAllocator::AllocHandle
|
|||||||
return std::make_pair(folderTreeIdx, packageHnd);
|
return std::make_pair(folderTreeIdx, packageHnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetManager::TreeNodeIdx AssetManager::pathToFolderIdxInFolderTree(StringView packagePath) const
|
AssetManager::TreeNodeIdx AssetManager::pathToFolderIdxInFolderTreeNoLock(StringView packagePath) const
|
||||||
{
|
{
|
||||||
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("PackagePathToFolder"));
|
CBE_PROFILER_SCOPE(CBE_PROFILER_CHAR("PackagePathToFolder"));
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user