diff --git a/.clang-format b/.clang-format
index 4d990a5..7f8e865 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,66 +1,211 @@
-# Generated from CLion C/C++ Code Style settings
+---
BasedOnStyle: LLVM
AccessModifierOffset: -2
-AlignAfterOpenBracket: Align
-AlignConsecutiveAssignments: None
+AlignAfterOpenBracket: BlockIndent
+AlignArrayOfStructures: None
+AlignConsecutiveAssignments:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveBitFields:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveDeclarations:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveMacros:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignEscapedNewlines: Left
AlignOperands: Align
-AllowAllArgumentsOnNextLine: false
-AllowAllConstructorInitializersOnNextLine: false
-AllowAllParametersOfDeclarationOnNextLine: false
-AllowShortBlocksOnASingleLine: Always
-AllowShortCaseLabelsOnASingleLine: false
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllConstructorInitializersOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
-AllowShortIfStatementsOnASingleLine: Always
+AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLambdasOnASingleLine: All
-AllowShortLoopsOnASingleLine: true
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
-BreakBeforeBraces: Custom
+AttributeMacros:
+ - __capability
+BinPackArguments: false
+BinPackParameters: false
+BitFieldColonSpacing: Both
BraceWrapping:
- AfterCaseLabel: false
- AfterClass: false
- AfterControlStatement: Never
+ AfterCaseLabel: true
+ AfterClass: true
+ AfterControlStatement: Always
AfterEnum: false
AfterFunction: false
AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
AfterUnion: false
+ AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
IndentBraces: false
- SplitEmptyFunction: false
+ SplitEmptyFunction: true
SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakAfterJavaFieldAnnotations: false
BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Attach
+BreakBeforeConceptDeclarations: Always
BreakBeforeTernaryOperators: true
-BreakConstructorInitializers: BeforeColon
+BreakConstructorInitializers: AfterColon
BreakInheritanceList: BeforeColon
-ColumnLimit: 0
+BreakStringLiterals: true
+ColumnLimit: 120
+CommentPragmas: "^ IWYU pragma:"
CompactNamespaces: false
-ContinuationIndentWidth: 4
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 2
+ContinuationIndentWidth: 2
+Cpp11BracedListStyle: false
+DeriveLineEnding: true
+DerivePointerAlignment: false
+DisableFormat: false
+EmptyLineAfterAccessModifier: Never
+EmptyLineBeforeAccessModifier: LogicalBlock
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: false
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IfMacros:
+ - KJ_IF_MAYBE
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: ^"(llvm|llvm-c|clang|clang-c)/
+ Priority: 2
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: ^(<|"(gtest|gmock|isl|json)/)
+ Priority: 3
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: .*
+ Priority: 1
+ SortPriority: 0
+ CaseSensitive: false
+IncludeIsMainRegex: (Test)?$
+IncludeIsMainSourceRegex: ""
+IndentAccessModifiers: false
+IndentCaseBlocks: true
IndentCaseLabels: true
+IndentExternBlock: AfterExternBlock
+IndentGotoLabels: true
IndentPPDirectives: None
+IndentRequiresClause: true
IndentWidth: 2
+IndentWrappedFunctionNames: false
+InsertBraces: false
+InsertTrailingCommas: None
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
-MaxEmptyLinesToKeep: 2
+LambdaBodyIndentation: Signature
+Language: Cpp
+MacroBlockBegin: ""
+MacroBlockEnd: ""
+MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 2
+ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
+PPIndentWidth: -1
+PackConstructorInitializers: Never
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakOpenParenthesis: 0
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyIndentedWhitespace: 0
+PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
-ReflowComments: false
+QualifierAlignment: Leave
+ReferenceAlignment: Pointer
+ReflowComments: true
+RemoveBracesLLVM: false
+RequiresClausePosition: OwnLine
+SeparateDefinitionBlocks: Leave
+ShortNamespaceLines: 1
+SortIncludes: Never
+SortJavaStaticImport: Before
+SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
-SpaceAfterTemplateKeyword: false
+SpaceAfterTemplateKeyword: true
+SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
-SpaceBeforeRangeBasedForLoopColon: false
+SpaceBeforeParensOptions:
+ AfterControlStatements: true
+ AfterForeachMacros: true
+ AfterFunctionDeclarationName: false
+ AfterFunctionDefinitionName: false
+ AfterIfMacros: true
+ AfterOverloadedOperator: false
+ AfterRequiresInClause: false
+ AfterRequiresInExpression: false
+ BeforeNonEmptyParentheses: false
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceBeforeSquareBrackets: false
+SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
-SpacesBeforeTrailingComments: 0
-SpacesInAngles: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: Never
SpacesInCStyleCastParentheses: false
-SpacesInContainerLiterals: false
+SpacesInConditionalStatement: false
+SpacesInContainerLiterals: true
+SpacesInLineCommentPrefix:
+ Minimum: 1
+ Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
+Standard: Latest
+StatementAttributeLikeMacros:
+ - Q_EMIT
+StatementMacros:
+ - Q_UNUSED
+ - QT_REQUIRE_VERSION
TabWidth: 2
-UseTab: Always
+UseCRLF: false
+UseTab: ForContinuationAndIndentation
+WhitespaceSensitiveMacros:
+ - STRINGIZE
+ - PP_STRINGIZE
+ - BOOST_PP_STRINGIZE
+ - NS_SWIFT_NAME
+ - CF_SWIFT_NAME
\ No newline at end of file
diff --git a/.cproject b/.cproject
new file mode 100644
index 0000000..7891ac7
--- /dev/null
+++ b/.cproject
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.docs/Designs/Language.drawio b/.docs/Designs/Language.drawio
new file mode 100644
index 0000000..88238fd
--- /dev/null
+++ b/.docs/Designs/Language.drawio
@@ -0,0 +1,428 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.docs/Gallery/Oscript.gif b/.docs/Gallery/Oscript.gif
new file mode 100644
index 0000000..932a66f
Binary files /dev/null and b/.docs/Gallery/Oscript.gif differ
diff --git a/.docs/Gallery/RayTracer.png b/.docs/Gallery/RayTracer.png
new file mode 100644
index 0000000..1fe76b2
Binary files /dev/null and b/.docs/Gallery/RayTracer.png differ
diff --git a/.docs/Gallery/RayTracerLua.png b/.docs/Gallery/RayTracerLua.png
new file mode 100644
index 0000000..f49bbd0
Binary files /dev/null and b/.docs/Gallery/RayTracerLua.png differ
diff --git a/.docs/Gallery/Sketch3D.gif b/.docs/Gallery/Sketch3D.gif
new file mode 100644
index 0000000..890b1f3
Binary files /dev/null and b/.docs/Gallery/Sketch3D.gif differ
diff --git a/.docs/Gallery/oscript.txt b/.docs/Gallery/oscript.txt
new file mode 100644
index 0000000..72b43e1
--- /dev/null
+++ b/.docs/Gallery/oscript.txt
@@ -0,0 +1,16 @@
+method myLogFunction(value) {
+ print value;
+}
+
+myLogFunction("hello");
+
+var i = 10;
+
+if (i == 10) {
+ while (i > 0) {
+ print i;
+ i = i - 1;
+ }
+} else {
+ print "still doin' heavy calculations...";
+}
diff --git a/.github/workflows/WindowsPlatform.yml b/.github/workflows/WindowsPlatform.yml
new file mode 100644
index 0000000..760ad45
--- /dev/null
+++ b/.github/workflows/WindowsPlatform.yml
@@ -0,0 +1,17 @@
+name: Windows
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+jobs:
+ build:
+ runs-on: self-hosted
+
+ steps:
+ - uses: actions/checkout@v3
+
+ #- name: Build
+ #run: echo hello
diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml
new file mode 100644
index 0000000..5d40434
--- /dev/null
+++ b/.github/workflows/cmake-single-platform.yml
@@ -0,0 +1,62 @@
+# This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage.
+# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml
+name: CMake on a single platform
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+env:
+ # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
+ BUILD_TYPE: Release
+
+jobs:
+ build:
+ # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
+ # You can convert this to a matrix build if you need cross-platform coverage.
+ # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Sutup Dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install python3 python-is-python3
+ sudo apt-get install -y libx11-dev libgl1-mesa-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev
+ sudo apt-get install -y clang-15
+ sudo apt-get install -y libasound2-dev libglew-dev
+ sudo apt-get install -y portaudio19-dev
+ sudo apt-get install -y libwayland-dev libxkbcommon-dev
+
+ - name: install OpenImageDenoise
+ run: |
+ sudo snap install ispc
+ sudo apt-get install -y libtbb-dev
+ git clone --recursive https://github.com/RenderKit/oidn.git
+ cd oidn
+ mkdir build; cd build; cmake ..; make -j;
+ sudo make install
+
+ - name: Update repository
+ run: |
+ git submodule update --init --recursive
+
+ - name: Configure CMake
+ # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
+ # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
+ run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
+
+ - name: Build
+ # Build your program with the given configuration
+ run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -- -j$(nproc)
+
+ - name: Test
+ working-directory: ${{github.workspace}}/build
+ # Execute tests defined by the CMake configuration.
+ # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
+ run: ctest -C ${{env.BUILD_TYPE}}
+
diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml
index 0e85c3d..4041169 100644
--- a/.github/workflows/cmake.yml
+++ b/.github/workflows/cmake.yml
@@ -18,25 +18,47 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v3
- - name: Install LLVM
- run: sudo apt-get install -y llvm
-
- - name: Set LLVM Toolchain
- run: |
- sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++ 10
- sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 20
+ - name: Setup
+ shell: bash
+ run: |
+ # If your submodules are configured to use SSH instead of HTTPS please uncomment the following line
+ # git config --global url."https://github.com/".insteadOf "git@github.com:"
+ auth_header="$(git config --local --get http.https://github.com/.extraheader)"
- - name: Configure CMake
- run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
+ git submodule sync --recursive
+ git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
+
+ sudo apt-get update
+ sudo apt-get install python3 python-is-python3
+ sudo apt-get install -y libx11-dev libgl1-mesa-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev
+ sudo apt-get install -y clang-15
+ sudo apt-get install -y libasound2-dev libglew-dev
+ sudo apt-get install -y portaudio19-dev
+ #cd Externals/glew/
+ #make extensions
+ sudo apt update
+
+ #- name: Set LLVM Toolchain
+ #run: |
+ # sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++ 10
+ # sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 20
+
+ - name: Configure CMake
+ run: |
+ gcc -v
+ clang++-15 -v
+ CC=clang-15 CXX=clang++-15 cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
+
+ - name: Build
+ run: CC=clang CXX=clang++ cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -v -j
+
+ - name: Test
+ working-directory: ${{github.workspace}}/build
+ # Execute tests defined by the CMake configuration.
+ # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
+ run: ctest -j -C ${{env.BUILD_TYPE}}
- - name: Build
- run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
- - name: Test
- working-directory: ${{github.workspace}}/build
- # Execute tests defined by the CMake configuration.
- # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
- run: ctest -C ${{env.BUILD_TYPE}}
diff --git a/.gitignore b/.gitignore
index b0247cb..5493878 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,11 @@
*tmp*
bin
build*
+*build*
lib
install
.vscode
+out
+.vs
+*.bkp
+*.$*
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..9d1d1de
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,21 @@
+[submodule "Externals/imgui"]
+ path = Externals/imgui
+ url = https://github.com/elushaX/imgui.git
+[submodule "Externals/lua"]
+ path = Externals/lua
+ url = https://github.com/elushaX/lua.git
+[submodule "Externals/unittest-cpp"]
+ path = Externals/unittest-cpp
+ url = https://github.com/elushaX/unittest-cpp.git
+[submodule "Externals/glfw"]
+ path = Externals/glfw
+ url = https://github.com/elushaX/glfw.git
+[submodule "Externals/nanovg"]
+ path = Externals/nanovg
+ url = https://github.com/elushaX/nanovg.git
+[submodule "Externals/lalr"]
+ path = Externals/lalr
+ url = https://github.com/elushaX/lalr.git
+[submodule "Externals/asio"]
+ path = Externals/asio
+ url = https://github.com/elushaX/asio.git
diff --git a/.project b/.project
new file mode 100644
index 0000000..4dcf3c3
--- /dev/null
+++ b/.project
@@ -0,0 +1,20 @@
+
+
+ Modules
+
+
+
+
+
+ org.eclipse.cdt.core.cBuilder
+ clean,full,incremental,
+
+
+
+
+
+ org.eclipse.cdt.core.cnature
+ org.eclipse.cdt.core.ccnature
+ org.eclipse.cdt.cmake.core.cmakeNature
+
+
diff --git a/Allocators/CMakeLists.txt b/.wip/Language/CMakeLists.txt
similarity index 54%
rename from Allocators/CMakeLists.txt
rename to .wip/Language/CMakeLists.txt
index fae102e..2ac316c 100644
--- a/Allocators/CMakeLists.txt
+++ b/.wip/Language/CMakeLists.txt
@@ -3,20 +3,18 @@ cmake_minimum_required(VERSION 3.2)
set(CMAKE_CXX_STANDARD 23)
-project(Allocator)
+project(Language)
### ---------------------- Static Library --------------------- ###
-file(GLOB SOURCES "./private/*.cpp")
-file(GLOB HEADERS "./public/*.hpp")
+file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
+file(GLOB HEADERS "./public/*.hpp" "./public/*/*.hpp")
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
-target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
-target_link_libraries(${PROJECT_NAME} PUBLIC Utils)
+target_include_directories(${PROJECT_NAME} PUBLIC public/)
+target_link_libraries(${PROJECT_NAME} PUBLIC Strings)
### -------------------------- Tests -------------------------- ###
enable_testing()
file(GLOB TEST_SOURCES "./tests/*.cpp")
add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES})
-target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils)
-add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests)
-
-install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib)
\ No newline at end of file
+target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} UnitTest++)
+add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests)
\ No newline at end of file
diff --git a/.wip/Language/private/Grammar.cpp b/.wip/Language/private/Grammar.cpp
new file mode 100644
index 0000000..d6acecf
--- /dev/null
+++ b/.wip/Language/private/Grammar.cpp
@@ -0,0 +1,36 @@
+
+#include "Grammar.hpp"
+
+using namespace tp;
+
+ContextFreeGrammar::Arg::Arg(const String& id, bool terminal, bool epsilon) {
+ mId = id;
+ mIsTerminal = terminal;
+ mIsEpsilon = epsilon;
+}
+
+const String& ContextFreeGrammar::Arg::getId() const { return mId; }
+
+bool ContextFreeGrammar::Arg::operator==(const Arg& in) const {
+ return (mId == in.mId) && (mIsEpsilon == in.mIsEpsilon) && (mIsTerminal == in.mIsTerminal);
+}
+
+ContextFreeGrammar::Rule::Rule(const String& id, const InitialierList& args) {
+ mId = id;
+ mArgs = args;
+}
+
+bool ContextFreeGrammar::Rule::operator==(const Rule& in) const { return (mId == in.mId) && (mArgs == in.mArgs); }
+
+bool ContextFreeGrammar::Rule::isProductive() const {
+ for (auto arg : mArgs) {
+ if (arg->getId() == mId) return false;
+ }
+ return true;
+}
+
+void ContextFreeGrammar::addRule(const Rule& rule) { mRules.append(rule); }
+
+void ContextFreeGrammar::addRule(const String& id, const InitialierList& args) { addRule(Rule(id, args)); }
+
+void ContextFreeGrammar::setStart(const String& startRule) { mStartTerminal = startRule; }
\ No newline at end of file
diff --git a/.wip/Language/private/LanguageCommon.cpp b/.wip/Language/private/LanguageCommon.cpp
new file mode 100644
index 0000000..147536d
--- /dev/null
+++ b/.wip/Language/private/LanguageCommon.cpp
@@ -0,0 +1,8 @@
+
+#include "LanguageCommon.hpp"
+#include "Strings.hpp"
+
+using namespace tp;
+
+static ModuleManifest* sModuleDependencies[] = { &gModuleStrings, nullptr };
+ModuleManifest tp::gModuleLanguage = ModuleManifest("Language", nullptr, nullptr, sModuleDependencies);
diff --git a/.wip/Language/public/Automata.hpp b/.wip/Language/public/Automata.hpp
new file mode 100644
index 0000000..0e059b9
--- /dev/null
+++ b/.wip/Language/public/Automata.hpp
@@ -0,0 +1,249 @@
+
+#pragma once
+
+#include "Utils.hpp"
+#include "List.hpp"
+#include "Map.hpp"
+#include "Tree.hpp"
+
+namespace tp {
+
+ // Non-Deterministic Finite-State Automata
+ template
+ class FiniteStateAutomation {
+ public:
+ struct State;
+
+ public:
+ class Transition {
+ friend FiniteStateAutomation;
+
+ public:
+ enum Type { ANY, EPSILON, SYMBOL };
+
+ public:
+ Transition(Type type, State* state, tAlphabetType symbol = tAlphabetType()) {
+ mState = state;
+ mType = type;
+ mSymbol = symbol;
+ }
+
+ [[nodiscard]] bool isTransition(const tAlphabetType& symbol) const {
+ return (mType == ANY || mType == EPSILON) || (mSymbol == symbol);
+ }
+
+ [[nodiscard]] bool doesConsumes(const tAlphabetType& symbol) const {
+ return (mType == ANY || (mType == SYMBOL && mSymbol == symbol));
+ }
+
+ [[nodiscard]] bool isEpsilon() const { return mType == EPSILON; }
+
+ const State* getState() const { return mState; }
+ const tAlphabetType& getSymbol() const { return mSymbol; }
+
+ private:
+ State* mState = nullptr;
+ Type mType;
+ tAlphabetType mSymbol;
+ };
+
+ class State {
+ friend FiniteStateAutomation;
+
+ public:
+ State() = default;
+
+ public:
+ void setValue(const tStateType& stateValue) { mStateVal = stateValue; }
+ void setAcceptance(bool isAccepting) { mIsAccepting = isAccepting; }
+ [[nodiscard]] bool isAccepting() const { return mIsAccepting; }
+ const tStateType& getStateVal() const { return mStateVal; }
+ [[nodiscard]] const Buffer* getTransitions() const { return &mTransitions; }
+
+ private:
+ Buffer mTransitions{};
+ tStateType mStateVal = tStateType();
+ bool mIsAccepting = false;
+ };
+
+ private:
+ List mStates;
+ State* mStartState = nullptr;
+ Range mAlphabetRange = { ENV_UALNI_MAX, ENV_UALNI_MIN };
+
+ public:
+ FiniteStateAutomation() = default;
+
+ State* addState(const tStateType& state, bool accepting) {
+ auto node = mStates.newNode();
+ node->data.mIsAccepting = accepting;
+ node->data.mStateVal = state;
+ mStates.pushBack(node);
+ return &node->data;
+ }
+
+ void addTransition(State* from, State* to, const tAlphabetType& symbol) {
+ from->mTransitions.append(Transition(Transition::SYMBOL, to, symbol));
+ if (mAlphabetRange.mBegin < ualni(symbol)) mAlphabetRange.mBegin = ualni(symbol);
+ if (mAlphabetRange.mEnd > ualni(symbol)) mAlphabetRange.mEnd = ualni(symbol);
+ }
+
+ void addEpsilonTransition(State* from, State* to) { from->mTransitions.append(Transition(Transition::SYMBOL, to)); }
+
+ void addAnyTransition(State* from, State* to) { from->mTransitions.append(Transition(Transition::ANY, to)); }
+
+ void setStartState(State* start) { mStartState = start; }
+
+ [[nodiscard]] State* getStartState() const { return mStartState; }
+
+ [[nodiscard]] bool isValid() const {
+ if (!mStartState) {
+ return false;
+ }
+ return true;
+ }
+
+ [[nodiscard]] ualni numStates() const { return mStates.length(); }
+
+ [[nodiscard]] const List* getStates() const { return &mStates; }
+
+ [[nodiscard]] Range getAlphabetRange() const { return mAlphabetRange; }
+
+ private:
+ typedef AvlTree, bool> StatesSet;
+
+ // Expands initial set with states that are reachable from initial set with no input consumption (E-transitions)
+ static void expandSet(StatesSet& set) {
+ List workingSet;
+
+ set.forEach([&](AvlNumericKey& key, bool) { workingSet.pushBack(key.val); });
+
+ while (workingSet.length()) {
+ auto first = workingSet.first()->data;
+ set.insert(first, {});
+
+ for (auto transition : first->mTransitions) {
+ if (!transition->isEpsilon()) continue;
+ if (set.find(transition->mState)) continue;
+ workingSet.pushBack(transition->mState);
+ }
+
+ workingSet.popFront();
+ }
+ }
+
+ // States that are reachable from initial set with symbol transition
+ static void findMoveSet(StatesSet& from, StatesSet& moveSet, tAlphabetType symbol) {
+ from.forEach([&](AvlNumericKey& key, bool) {
+ for (auto transition : key.val->mTransitions) {
+ if (transition->isEpsilon()) continue;
+ if (!transition->isTransition(symbol)) continue;
+ if (moveSet.find(transition->mState)) continue;
+ moveSet.insert(transition->mState, {});
+ }
+ });
+ }
+
+ public:
+ bool makeDeterministic() {
+ if (!isValid()) return false;
+
+ struct GroupKey {
+ const StatesSet* group;
+ static ualni hash(GroupKey key) { return 0; }
+ bool operator==(const GroupKey& key) const { return false; }
+ };
+
+ struct GroupInfo {
+ StatesSet* group = nullptr;
+ AvlTree, tAlphabetType> transitions;
+ State* newState = nullptr;
+ bool accepting = false;
+ tStateType stateVal = tStateType();
+ };
+
+ Buffer groups = { {} };
+ Map groupInfos;
+
+ groups.first().insert(getStartState(), false);
+
+ expandSet(groups.first());
+
+ groupInfos.put({ &groups.first() }, { &groups.first() });
+
+ // 1) find new states
+ List workingSet;
+ workingSet.pushBack(&groups.first());
+
+ while (workingSet.length()) {
+ StatesSet* group = workingSet.first()->data;
+ GroupInfo* info = &groupInfos.get({ group });
+
+ for (auto symbol : getAlphabetRange()) {
+
+ // calculate new possible state
+ StatesSet potentialGroup;
+
+ findMoveSet(*group, potentialGroup, tAlphabetType(symbol));
+ expandSet(potentialGroup);
+
+ if (!potentialGroup.size()) continue;
+
+ // find existing or create group
+ StatesSet* targetGroup = nullptr;
+ auto iter = groupInfos.presents({ &potentialGroup });
+ if (iter) {
+ targetGroup = groupInfos.getSlotVal(iter).group;
+ } else {
+ targetGroup = &groups.append(potentialGroup);
+ groupInfos.put({ targetGroup }, { targetGroup });
+ workingSet.pushBack(targetGroup);
+ }
+
+ // assert transition is added
+ info->transitions.insert(targetGroup, tAlphabetType(symbol));
+ }
+
+ workingSet.popFront();
+ }
+
+ // 2) find new states termination values
+ for (auto group : groupInfos) {
+ GroupInfo* info = &group->val;
+ ualni accepting = 0;
+
+ info->group->forEach([&](AvlNumericKey& key, bool) {
+ if (key.val->mIsAccepting) {
+ accepting++;
+ info->accepting = true;
+ info->stateVal = key.val->mStateVal;
+ }
+ });
+
+ if (!accepting) {
+ info->accepting = false;
+ info->stateVal = info->group->head()->key.val->mStateVal;
+ }
+ }
+
+ // 3) transfer
+ mStates.removeAll();
+
+ // create states
+ for (auto group : groupInfos) {
+ group->val.newState = addState(group->val.stateVal, group->val.accepting);
+ }
+
+ // create transitions
+ for (auto group : groupInfos) {
+ auto functor = [&](AvlNumericKey targetGroupKey, tAlphabetType symbol) {
+ GroupInfo* targetGroup = &groupInfos.get({ (StatesSet*) targetGroupKey.val });
+ addTransition(group->val.newState, targetGroup->newState, symbol);
+ };
+ group->val.transitions.forEach(functor);
+ }
+
+ return true;
+ }
+ };
+}
\ No newline at end of file
diff --git a/.wip/Language/public/ContextFreeAutomata.hpp b/.wip/Language/public/ContextFreeAutomata.hpp
new file mode 100644
index 0000000..9c4f698
--- /dev/null
+++ b/.wip/Language/public/ContextFreeAutomata.hpp
@@ -0,0 +1,137 @@
+
+#pragma once
+
+#include "Strings.hpp"
+#include "Automata.hpp"
+#include "Buffer2D.hpp"
+
+namespace tp {
+
+ template
+ class ContextFreeAutomata {
+
+ struct Action {
+ enum Type { SHIFT, REDUCE, TRAP } type = TRAP;
+ ualni num = 0; // state to shift (shift action) or pop count (reduce action)
+ };
+
+ public:
+ struct StackItem {
+ ualni state = 0;
+ tAlphabetType symbol;
+ Buffer leafs;
+ };
+
+ struct AcceptResult {
+ bool accepted = false;
+ ualni advancedIdx = 0;
+ const StackItem* ast = nullptr;
+ };
+
+ public:
+ ContextFreeAutomata() = default;
+
+ AcceptResult accept(const tAlphabetType* stream, ualni size) {
+ mCurrentState = mStartState;
+ mStack.append(&mItems.append({ mCurrentState, {}, {} }));
+
+ ualni advancedIdx = 0;
+ while (advancedIdx < size) {
+ const tAlphabetType& symbol = *(stream + advancedIdx);
+
+ if (!(symbol >= mRange.mBegin && symbol < mRange.mEnd)) {
+ return { false, advancedIdx, nullptr };
+ }
+
+ const Action& action = mTable.get({ ualni(symbol - mRange.mBegin), mCurrentState });
+
+ if (action.type == Action::TRAP) {
+ return { false, advancedIdx, nullptr };
+ }
+
+ if (action.type == Action::SHIFT) {
+ mStack.last()->symbol = symbol;
+ mStack.append(&mItems.append({ mCurrentState, {}, {} }));
+ mCurrentState = action.num;
+ }
+
+ if (mTable.get({ 0, mCurrentState }).type == Action::REDUCE) {
+ StackItem* newItem = &mItems.append(StackItem{});
+ for (auto iter : Range(action.num)) {
+ newItem->leafs.append(mStack.last());
+ mCurrentState = mStack.last()->state;
+ mStack.pop();
+ }
+
+ if (!mStack.size()) {
+ if (advancedIdx == size) {
+ return { true, advancedIdx, newItem };
+ } else {
+ return { false, advancedIdx, {} };
+ }
+ }
+
+ mStack.append(&mItems.append({ mCurrentState, {}, {} }));
+ }
+
+ advancedIdx++;
+ }
+
+ return { false, advancedIdx, nullptr };
+ }
+
+ public:
+ typedef FiniteStateAutomation Automata;
+ typedef Automata::State AutomataState;
+
+ void construct(const Automata& automata) {
+ mRange = automata.getAlphabetRange();
+
+ const ualni numStates = automata.numStates();
+ const ualni numSymbols = mRange.idxDiff();
+
+ mTable.reserve({ numSymbols, numStates });
+ mTable.assign(Action{ Action::TRAP, 0 });
+
+ Map states;
+ ualni stateIndex = 0;
+ for (auto state : *automata.getStates()) {
+ states.put(&state.data(), { stateIndex });
+ stateIndex++;
+ }
+
+ stateIndex = 0;
+ for (auto state : *automata.getStates()) {
+ if (&state.data() == automata.getStartState()) {
+ mStartState = stateIndex;
+ }
+
+ if (state->isAccepting()) {
+ ASSERT(state->getTransitions()->size() == 0)
+ for (auto symbolIndex : Range(numSymbols)) {
+ mTable.set({ stateIndex, symbolIndex }, { Action::REDUCE, state->getStateVal().numArgs() });
+ }
+ } else {
+ for (auto transition : *state->getTransitions()) {
+ ualni symbolIndex = ualni(transition->getSymbol()) - mRange.mBegin;
+ ualni targetStateIndex = states.get(transition->getState());
+ mTable.set({ stateIndex, symbolIndex }, { Action::SHIFT, targetStateIndex });
+ }
+ }
+
+ stateIndex++;
+ }
+ }
+
+ private:
+ Buffer2D mTable;
+
+ Buffer mItems;
+ Buffer mStack;
+
+ ualni mStartState = 0;
+ ualni mCurrentState = 0;
+
+ Range mRange;
+ };
+}
diff --git a/.wip/Language/public/ContextFreeCompiler.hpp b/.wip/Language/public/ContextFreeCompiler.hpp
new file mode 100644
index 0000000..db05b61
--- /dev/null
+++ b/.wip/Language/public/ContextFreeCompiler.hpp
@@ -0,0 +1,181 @@
+
+#pragma once
+
+#include "Automata.hpp"
+#include "Grammar.hpp"
+
+namespace tp {
+
+ class ContextFreeCompiler {
+ public:
+ struct SymbolVal {
+ SymbolVal() = default;
+
+ SymbolVal(ualni aId, ualni aStart, ualni aLen) {
+ id = aId;
+ start = aStart;
+ len = aLen;
+ };
+
+ SymbolVal(ualni val) { id = val; }
+
+ operator ualni() const { return id; }
+
+ bool operator==(const SymbolVal& in) const { return in.id == id; }
+ SymbolVal& operator=(const SymbolVal& in) = default;
+
+ ualni id = 0;
+ ualni start = 0;
+ ualni len = 0;
+ };
+
+ struct Item {
+ const ContextFreeGrammar::Rule* mRule = nullptr;
+ ualni mAdvanceIdx = 0;
+ ualni numArgs() const { return 0; }
+ };
+
+ struct Symbol {
+ String mId;
+ bool mIsTerminal = false;
+ };
+
+ private:
+ struct NonTerminal {
+ Buffer rules;
+ Map references;
+ Map referencing;
+
+ public:
+ [[nodiscard]] bool isProductive() const {
+ for (auto rule : rules) {
+ if (rule->isProductive()) return true;
+ }
+ return false;
+ }
+
+ [[nodiscard]] bool isLooped(Map& processed, const String& id) const {
+ for (auto ref : referencing) {
+ if (processed.presents(ref->key)) return true;
+ }
+ processed.put(id, {});
+ for (auto ref : referencing) {
+ if (ref->val->isLooped(processed, ref->key)) return true;
+ }
+ return false;
+ }
+ };
+
+ public:
+ bool compile(const ContextFreeGrammar& grammar, FiniteStateAutomation& automata) {
+ if (!init(grammar)) return false;
+ return true;
+ }
+
+ [[nodiscard]] const Buffer* getSymbols() const { return &mSymbols; }
+ [[nodiscard]] SymbolVal getSymbolId(const String& name) const { return mSymbolLookup.get(name); }
+
+ private:
+ bool init(const ContextFreeGrammar& grammar) {
+
+ if (!grammar.getRules()->size()) {
+ return false;
+ }
+
+ for (auto rule : *grammar.getRules()) {
+ if (!rule->getArgs()->size()) {
+ return false;
+ }
+ }
+
+ findNonTerminals(grammar);
+
+ for (auto nonTerminal : mNonTerminals) {
+ for (auto rule : nonTerminal->val.rules) {
+ for (auto arg : *rule->getArgs()) {
+ if (arg->isTerminal() || arg->isEpsilon()) continue;
+
+ if (!mNonTerminals.presents(arg->getId())) {
+ printf("Referenced non-terminal '%s' is not defined\n", arg->getId().read());
+ return false;
+ }
+ }
+ }
+ }
+
+ findAllReferences(grammar);
+
+ for (auto nonTerminal : mNonTerminals) {
+ if (!nonTerminal->val.references.size() && nonTerminal->key != grammar.getStartTerminal()) {
+ printf("Non-terminal '%s' is defined but not used\n", nonTerminal->key.read());
+ return false;
+ }
+ }
+
+ for (auto nonTerminal : mNonTerminals) {
+ if (!nonTerminal->val.isProductive()) {
+ printf("Non-terminal '%s' is not productive\n", nonTerminal->val.rules.first()->getId().read());
+ return false;
+ }
+ }
+
+ Map processed;
+ if (mNonTerminals.get(grammar.getStartTerminal()).isLooped(processed, grammar.getStartTerminal())) {
+ printf("Note that grammar is looped.\n");
+ return false;
+ }
+
+ initSymbols(grammar);
+
+ return true;
+ }
+
+ void findNonTerminals(const ContextFreeGrammar& grammar) {
+ for (auto rule : *grammar.getRules()) {
+ if (!mNonTerminals.presents(rule->getId())) {
+ mNonTerminals.put(rule->getId(), {});
+ }
+ auto nonTerminal = &mNonTerminals.get(rule->getId());
+ nonTerminal->rules.append(&rule.data());
+ }
+ }
+
+ void findAllReferences(const ContextFreeGrammar& grammar) {
+ for (auto nonTerminal : mNonTerminals) {
+ for (auto rule : nonTerminal->val.rules) {
+ for (auto arg : *rule->getArgs()) {
+ if (arg->isTerminal() || arg->isEpsilon()) continue;
+
+ NonTerminal* reference = &mNonTerminals.get(arg->getId());
+ nonTerminal->val.referencing.put(arg->getId(), reference);
+ reference->references.put(nonTerminal->key, &nonTerminal->val);
+ }
+ }
+ }
+ }
+
+ void initSymbols(const ContextFreeGrammar& grammar) {
+ for (auto nonTerminal : mNonTerminals) {
+ mSymbols.append({ nonTerminal->key, false });
+ mSymbolLookup.put(nonTerminal->key, SymbolVal(mSymbols.size() - 1));
+
+ for (auto rule : nonTerminal->val.rules) {
+ for (auto arg : *rule->getArgs()) {
+ if (arg->isEpsilon() || arg->isTerminal()) continue;
+ if (mTerminals.presents(arg->getId())) continue;
+ mTerminals.put(arg->getId(), {});
+
+ mSymbols.append({ nonTerminal->key, true });
+ mSymbolLookup.put(nonTerminal->key, SymbolVal(mSymbols.size() - 1));
+ }
+ }
+ }
+ }
+
+ private:
+ Map mNonTerminals;
+ Map mTerminals;
+ Buffer mSymbols;
+ Map mSymbolLookup;
+ };
+}
diff --git a/.wip/Language/public/Grammar.hpp b/.wip/Language/public/Grammar.hpp
new file mode 100644
index 0000000..df1fba5
--- /dev/null
+++ b/.wip/Language/public/Grammar.hpp
@@ -0,0 +1,224 @@
+
+#pragma once
+
+#include "Buffer.hpp"
+#include "LanguageCommon.hpp"
+#include "Map.hpp"
+#include "Strings.hpp"
+
+namespace tp {
+
+ class ContextFreeGrammar {
+ public:
+ struct Arg {
+ friend class Rule;
+
+ public:
+ Arg() = default;
+ explicit Arg(const String& id, bool terminal = true, bool epsilon = false);
+
+ public:
+ bool operator==(const Arg& in) const;
+ [[nodiscard]] const String& getId() const;
+ [[nodiscard]] bool isTerminal() const { return mIsTerminal; }
+ [[nodiscard]] bool isEpsilon() const { return mIsEpsilon; }
+
+ private:
+ String mId;
+ bool mIsTerminal = false;
+ bool mIsEpsilon = false;
+ };
+
+ class Rule {
+ public:
+ Rule() = default;
+ Rule(const String& id, const InitialierList& args);
+
+ public:
+ bool operator==(const Rule& in) const;
+ [[nodiscard]] bool isProductive() const;
+ [[nodiscard]] const String& getId() const { return mId; }
+ [[nodiscard]] const Buffer* getArgs() const { return &mArgs; }
+
+ private:
+ String mId;
+ Buffer mArgs;
+ };
+
+ public:
+ ContextFreeGrammar() = default;
+
+ public:
+ void addRule(const Rule& rule);
+ void addRule(const String& id, const InitialierList& args);
+ void setStart(const String& startRule);
+ [[nodiscard]] const Buffer* getRules() const { return &mRules; }
+ [[nodiscard]] const String& getStartTerminal() const { return mStartTerminal; }
+
+ public:
+ Buffer mRules;
+ String mStartTerminal;
+ bool mIsLooped = false;
+ };
+
+ template
+ class RegularGrammar {
+ public:
+ struct Node {
+ enum Type {
+ NONE,
+ ANY,
+ OR,
+ IF,
+ CLASS,
+ COMPOUND,
+ REPEAT,
+ VAL,
+ } mType = NONE;
+
+ explicit Node(Type type) :
+ mType(type) {}
+
+ virtual ~Node() = default;
+ };
+
+ class ValueNode : public Node {
+ public:
+ explicit ValueNode(tAlphabetType val) :
+ mVal(val),
+ Node(Node::VAL) {}
+
+ ~ValueNode() override = default;
+
+ public:
+ tAlphabetType mVal;
+ };
+
+ class CompoundNode : public Node {
+ public:
+ CompoundNode() :
+ Node(Node::COMPOUND) {}
+
+ CompoundNode(const InitialierList& nodes) :
+ Node(Node::COMPOUND) {
+ mSequence = nodes;
+ }
+
+ ~CompoundNode() override {
+ for (auto iter : mSequence) {
+ delete iter.data();
+ }
+ mSequence.clear();
+ }
+
+ public:
+ Buffer mSequence;
+ };
+
+ class AlternationNode : public Node {
+ public:
+ AlternationNode() :
+ Node(Node::OR) {}
+
+ AlternationNode(const Node* a, const Node* b) :
+ Node(Node::OR) {
+ mFirst = a;
+ mSecond = b;
+ }
+
+ ~AlternationNode() override {
+ delete mFirst;
+ delete mSecond;
+ }
+
+ public:
+ const Node* mFirst = nullptr;
+ const Node* mSecond = nullptr;
+ };
+
+ class IfNode : public Node {
+ public:
+ IfNode() :
+ Node(Node::IF) {}
+
+ explicit IfNode(const Node* a) :
+ Node(Node::IF) {
+ mNode = a;
+ }
+
+ ~IfNode() override { delete mNode; }
+
+ public:
+ const Node* mNode = nullptr;
+ };
+
+ class AnyNode : public Node {
+ public:
+ AnyNode() :
+ Node(Node::ANY) {}
+
+ ~AnyNode() override = default;
+ };
+
+ class RepetitionNode : public Node {
+ public:
+ RepetitionNode() :
+ Node(Node::REPEAT) {}
+
+ explicit RepetitionNode(const Node* rep, bool plus = false) :
+ Node(Node::REPEAT) {
+ mNode = rep;
+ mPlus = plus;
+ }
+
+ ~RepetitionNode() override { delete mNode; }
+
+ public:
+ Node* mNode = nullptr;
+ bool mPlus = false;
+ };
+
+ class ClassNode : public Node {
+ public:
+ ClassNode() :
+ Node(Node::CLASS) {}
+
+ explicit ClassNode(const Buffer>& ranges, bool exclude = false) :
+ Node(Node::CLASS) {
+ mExclude = exclude;
+ mRanges = ranges;
+ }
+
+ ~ClassNode() override { mRanges.removeAll(); }
+
+ public:
+ Buffer> mRanges;
+ bool mExclude = false;
+ };
+
+ public:
+ RegularGrammar() = default;
+
+ ~RegularGrammar() {
+ for (auto rule : mRules) {
+ delete rule->t1;
+ }
+ }
+
+ void addRule(const Node* node, tTokType id) { mRules.append({ node, id }); }
+
+ public:
+ const Node* seq(const InitialierList& nodes) { return new CompoundNode(nodes); }
+ const Node* val(tAlphabetType in) { return new ValueNode(in); }
+ const Node* alt(const Node* a, const Node* b) { return new AlternationNode(a, b); }
+ const Node* may(const Node* a) { return new IfNode(a); }
+ const Node* any() { return new AnyNode(); }
+ const Node* rep(const Node* rep, bool plus = false) { return new RepetitionNode(rep, plus); }
+ const Node* ranges(const Buffer>& ranges, bool exclude = false) {
+ return new ClassNode(ranges, exclude);
+ }
+
+ public:
+ Buffer> mRules;
+ };
+}
diff --git a/.wip/Language/public/LanguageCommon.hpp b/.wip/Language/public/LanguageCommon.hpp
new file mode 100644
index 0000000..0429431
--- /dev/null
+++ b/.wip/Language/public/LanguageCommon.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "Module.hpp"
+
+namespace tp {
+ extern ModuleManifest gModuleLanguage;
+}
diff --git a/.wip/Language/public/Parser.hpp b/.wip/Language/public/Parser.hpp
new file mode 100644
index 0000000..10985ae
--- /dev/null
+++ b/.wip/Language/public/Parser.hpp
@@ -0,0 +1,107 @@
+
+#pragma once
+
+#include "RegularCompiler.hpp"
+#include "RegularAutomata.hpp"
+
+#include "ContextFreeCompiler.hpp"
+#include "ContextFreeAutomata.hpp"
+
+namespace tp {
+
+ template
+ class Parser {
+
+ typedef RegularGrammar RegularGrammar;
+ typedef RegularCompiler RegularCompiler;
+ typedef FiniteStateAutomation RegularGraph;
+ typedef RegularAutomata RegularAutomata;
+
+ // ContextFreeGrammar;
+ // ContextFreeCompiler;
+ typedef FiniteStateAutomation ContextFreeGraph;
+ typedef ContextFreeAutomata ContextFreeAutomata;
+
+ public:
+ struct ParseResult {
+ bool accepted = false;
+ const ContextFreeAutomata::StackItem* ast = nullptr;
+ };
+
+ public:
+ Parser() = default;
+
+ public:
+ bool compileTables(
+ const ContextFreeGrammar& cfGrammar,
+ const RegularGrammar& reGrammar,
+ const Map& contextFreeToRegular
+ ) {
+ // Compile Regular Grammar
+ {
+ RegularGraph graph;
+ RegularCompiler compiler;
+ compiler.compile(graph, reGrammar);
+ graph.makeDeterministic();
+ mRegularAutomata.construct(graph);
+ }
+
+ // compile context free grammar
+ {
+ ContextFreeGraph graph;
+ ContextFreeCompiler compiler;
+ compiler.compile(cfGrammar, graph);
+ graph.makeDeterministic();
+ mContextFreeAutomata.construct(graph);
+
+ // make glue
+ for (auto symbol : *compiler.getSymbols()) {
+ auto symbolId = compiler.getSymbolId(symbol->mId);
+ if (symbol->mIsTerminal) {
+ auto iter = contextFreeToRegular.presents(symbol->mId);
+ if (!iter) return false;
+ mGrammarGlue.put(contextFreeToRegular.getSlotVal(iter), symbolId);
+ } else {
+ mAstNames.put(symbolId, symbol->mId);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ ParseResult parse(const tAlphabetType* sentence, ualni sentenceLength) {
+ // get tokens stream
+ Buffer tokens;
+
+ const tAlphabetType* sentenceIter = sentence;
+ ualni lengthIter = sentenceLength;
+
+ while (lengthIter) {
+ auto result = mRegularAutomata.accept(sentenceIter, lengthIter);
+
+ if (!result.accepted) {
+ return { false, nullptr };
+ }
+
+ tokens.append(ContextFreeCompiler::SymbolVal(
+ mGrammarGlue.get(result.state), ualni(sentenceIter - sentence), result.advancedIdx
+ ));
+
+ sentenceIter += result.advancedIdx;
+ lengthIter -= result.advancedIdx;
+ }
+
+ ContextFreeAutomata::AcceptResult result = mContextFreeAutomata.accept(tokens.getBuff(), tokens.size());
+ return { result.accepted, result.ast };
+ }
+
+ public:
+ // save load compiled tables
+ RegularAutomata mRegularAutomata;
+ ContextFreeAutomata mContextFreeAutomata;
+
+ Map mGrammarGlue;
+ Map mAstNames;
+ };
+}
diff --git a/.wip/Language/public/RegularAutomata.hpp b/.wip/Language/public/RegularAutomata.hpp
new file mode 100644
index 0000000..501dada
--- /dev/null
+++ b/.wip/Language/public/RegularAutomata.hpp
@@ -0,0 +1,101 @@
+
+#pragma once
+
+#include "Strings.hpp"
+#include "Automata.hpp"
+#include "Buffer2D.hpp"
+
+namespace tp {
+
+ template
+ class RegularAutomata {
+ public:
+ struct AcceptResult {
+ bool accepted = false;
+ ualni advancedIdx = 0;
+ tStateType state = tStateType();
+ };
+
+ public:
+ RegularAutomata() = default;
+
+ AcceptResult accept(const tAlphabetType* stream, ualni size) {
+ mCurrentState = mStartState;
+
+ ualni advancedIdx = 0;
+
+ while (advancedIdx < size) {
+ const tAlphabetType& symbol = *(stream + advancedIdx);
+
+ if (!(symbol >= mSymbolRange.mBegin && symbol < mSymbolRange.mEnd)) {
+ return { false, advancedIdx, {} };
+ }
+
+ mCurrentState = mTable.get({ (ualni) (symbol - mSymbolRange.mBegin), mCurrentState });
+
+ if (mCurrentState == mStates.size()) {
+ return { false, advancedIdx, {} };
+ }
+
+ if (mStates[mCurrentState].first) {
+ return { true, advancedIdx, mStates[mCurrentState].second };
+ }
+
+ advancedIdx++;
+ }
+
+ return { false, advancedIdx, {} };
+ }
+
+ public:
+ void construct(const FiniteStateAutomation& automata) {
+ const auto range = automata.getAlphabetRange();
+ mSymbolRange = { tAlphabetType(range.mBegin), tAlphabetType(range.mEnd) };
+
+ auto range_len = ualni(mSymbolRange.mEnd - mSymbolRange.mBegin);
+ auto sizeX = range_len ? range_len : 1;
+ auto sizeY = (ualni) (automata.numStates());
+
+ mTable.reserve({ sizeX, sizeY });
+ mTable.assign(automata.numStates());
+ mStates.reserve(sizeY);
+
+ ualni idx = 0;
+ for (auto state : *automata.getStates()) {
+ mStates[idx] = { state->isAccepting(), state->getStateVal() };
+ idx++;
+ }
+
+ idx = 0;
+ for (auto state : *automata.getStates()) {
+ if (&state.data() == automata.getStartState()) {
+ mStartState = mCurrentState = idx;
+ }
+ idx++;
+ }
+
+ ualni stateIdx = 0;
+ for (auto state : *automata.getStates()) {
+ for (auto transition : *state->getTransitions()) {
+ ualni stateIdx2 = 0;
+ for (auto state2 : *automata.getStates()) {
+ if (transition->getState() == &state2.data()) break;
+ stateIdx2++;
+ }
+ auto const code = transition->getSymbol();
+ mTable.set({ (ualni) (code - mSymbolRange.mBegin), (ualni) stateIdx }, stateIdx2);
+ }
+ stateIdx++;
+ }
+ }
+
+ private:
+ Buffer2D mTable;
+ Buffer> mStates;
+
+ ualni mCurrentState = 0;
+ ualni mStartState = 0;
+
+ Range mSymbolRange = { 0, 0 };
+ };
+}
diff --git a/.wip/Language/public/RegularCompiler.hpp b/.wip/Language/public/RegularCompiler.hpp
new file mode 100644
index 0000000..a27f464
--- /dev/null
+++ b/.wip/Language/public/RegularCompiler.hpp
@@ -0,0 +1,208 @@
+
+#pragma once
+
+#include "Grammar.hpp"
+#include "Automata.hpp"
+
+namespace tp {
+
+ template
+ class RegularCompiler {
+
+ typedef FiniteStateAutomation Graph;
+ typedef typename Graph::State Vertex;
+ typedef RegularGrammar Grammar;
+
+ struct Node {
+ Vertex* left = nullptr;
+ Vertex* right = nullptr;
+ };
+
+ private:
+ Graph* mGraph = nullptr;
+
+ public:
+ struct CompileError {
+ uhalni mRuleIndex = 0;
+ tStateType mRuleState;
+ const char* description = nullptr;
+ [[nodiscard]] bool isError() const { return description; }
+ };
+
+ CompileError mError;
+
+ void compile(Graph& graph, const tAlphabetType* regex, tStateType state) {
+ mGraph = &graph;
+ compileUtil(regex, state);
+ }
+
+ void compile(Graph& aGraph, const Grammar& grammar) {
+ mGraph = &aGraph;
+
+ auto left = addVertex();
+ auto right = addVertex();
+
+ halni idx = 0;
+ for (auto rule : grammar.mRules) {
+
+ auto node = compileUtil(rule.data().first, rule.data().second);
+
+ if (!(node.left && node.right)) {
+ mError.mRuleIndex = idx;
+ return;
+ }
+
+ transitionAny(left, node.left);
+ transitionAny(node.right, right);
+
+ idx++;
+ }
+
+ mGraph->setStartState(left);
+ }
+
+ private:
+ Node compileUtil(const Grammar::Node* astNode, tStateType state) {
+
+ auto node = compileNode(astNode, nullptr, nullptr);
+
+ node.right->setValue(state);
+ node.right->setAcceptance(true);
+
+ mGraph->setStartState(node.left);
+
+ return node;
+ }
+
+ Node compileVal(Grammar::ValueNode* val, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto left = aLeft ? aLeft : addVertex();
+ auto right = aRight ? aRight : addVertex();
+ transitionVal(left, right, val->mVal);
+ return { left, right };
+ }
+
+ Node compileAlternation(const Grammar::AlternationNode* alt, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto first_node = compileNode(alt->mFirst, aLeft, aRight);
+ auto second_node = compileNode(alt->mSecond);
+ transitionAny(first_node.left, second_node.left);
+ transitionAny(second_node.right, first_node.right);
+ return first_node;
+ }
+
+ Node compileAny(const Grammar::AnyNode*, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto left = aLeft ? aLeft : addVertex();
+ auto right = aRight ? aRight : addVertex();
+ transitionAny(left, right, true);
+ return { left, right };
+ }
+
+ Node compileRepeat(const Grammar::RepetitionNode* repeat, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ if (repeat->mPlus) {
+ auto middle = addVertex();
+
+ auto left_node = compileNode(repeat->mNode, aLeft, middle);
+
+ auto right_node = compileNode(repeat->mNode, middle, aRight);
+ transitionAny(right_node.right, right_node.left);
+ transitionAny(right_node.left, right_node.right);
+
+ return { left_node.left, right_node.right };
+ } else {
+ auto node = compileNode(repeat->mNode, aLeft, aRight);
+ transitionAny(node.right, node.left);
+ transitionAny(node.left, node.right);
+ return node;
+ }
+ }
+
+ Node compileIf(const Grammar::IfNode* ifNode, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto node = compileNode(ifNode->mNode, aLeft, aRight);
+ transitionAny(node.left, node.right);
+ return node;
+ }
+
+ Node compileClass(const Grammar::ClassNode* node, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto left = aLeft ? aLeft : addVertex();
+ auto right = aRight ? aRight : addVertex();
+
+ if (node->mRanges.size() == 1) {
+ auto const& range = node->mRanges.first();
+ transitionRange(left, right, { ualni(range.mBegin), ualni(range.mEnd) }, node->mExclude);
+ return { left, right };
+ }
+
+ for (auto range : node->mRanges) {
+ auto middle = addVertex();
+ transitionRange(left, middle, { ualni(range->mBegin), ualni(range->mEnd) }, node->mExclude);
+ transitionAny(middle, right);
+ }
+ return { left, right };
+ }
+
+ Node compileCompound(const Grammar::CompoundNode* compound, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ Vertex* left = nullptr;
+ Vertex* rigth = nullptr;
+
+ ualni idx = 0;
+ for (auto child : compound->mSequence) {
+ auto pass_left = idx == 0 ? aLeft : rigth;
+ auto pass_right = idx == compound->mSequence.size() - 1 ? aRight : nullptr;
+ auto node = compileNode(child.data(), pass_left, pass_right);
+ if (!left) left = node.left;
+ rigth = node.right;
+ idx++;
+ }
+
+ return { left, rigth };
+ }
+
+ Node compileNode(const Grammar::Node* node, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ switch (node->mType) {
+ case Grammar::Node::CLASS: return compileClass((typename Grammar::ClassNode*) node, aLeft, aRight);
+ case Grammar::Node::COMPOUND: return compileCompound((typename Grammar::CompoundNode*) node, aLeft, aRight);
+ case Grammar::Node::IF: return compileIf((typename Grammar::IfNode*) node, aLeft, aRight);
+ case Grammar::Node::REPEAT: return compileRepeat((typename Grammar::RepetitionNode*) node, aLeft, aRight);
+ case Grammar::Node::ANY: return compileAny((typename Grammar::AnyNode*) node, aLeft, aRight);
+ case Grammar::Node::OR: return compileAlternation((typename Grammar::AlternationNode*) node, aLeft, aRight);
+ case Grammar::Node::VAL: return compileVal((typename Grammar::ValueNode*) node, aLeft, aRight);
+ case Grammar::Node::NONE: break;
+ }
+ ASSERT(0)
+ return {};
+ }
+
+ void transitionAny(Vertex* from, Vertex* to, bool consumes = false) {
+ for (auto symbol : Range(tMinSymbol, tMaxSymbol)) {
+ transitionVal(from, to, symbol);
+ }
+ }
+
+ void transitionVal(Vertex* from, Vertex* to, tAlphabetType val) { mGraph->addTransition(from, to, val); }
+
+ void transitionRange(Vertex* from, Vertex* to, Range range, bool exclude) {
+ if (exclude) {
+ Range first = { tMinSymbol, range.mBegin - 1 };
+ Range second = { range.mEnd + 1, tMaxSymbol };
+
+ if (first.valid()) {
+ for (auto symbol : first) {
+ transitionVal(from, to, symbol);
+ }
+ }
+
+ if (second.valid()) {
+ for (auto symbol : second) {
+ transitionVal(from, to, symbol);
+ }
+ }
+
+ } else {
+ for (auto symbol : range) {
+ transitionVal(from, to, symbol);
+ }
+ }
+ }
+
+ Vertex* addVertex() { return mGraph->addState(tInTransition, false); }
+ };
+}
diff --git a/.wip/Language/public/SimpleParser.hpp b/.wip/Language/public/SimpleParser.hpp
new file mode 100644
index 0000000..ad1dc6e
--- /dev/null
+++ b/.wip/Language/public/SimpleParser.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "Parser.hpp"
+
+namespace tp {
+
+ // Gives ability to express grammar in the Unified Format as sentence
+ template
+ class SimpleParser {
+ enum UGTokens : alni { InTransition = -1, TestSeq };
+
+ typedef Parser UGParser;
+ typedef Parser UserParser;
+
+ public:
+ SimpleParser() {
+ // Grammar for unified grammar format sentence that tables compiled from
+
+ // Define Context-Free grammar
+ ContextFreeGrammar contextFreeGrammar;
+ {
+ // use existing CF grammar interface
+ contextFreeGrammar.addRule("a", { ContextFreeGrammar::Arg("") });
+ contextFreeGrammar.setStart("a");
+ }
+
+ // Define Regular grammar
+ RegularGrammar regularGrammar;
+ {
+ // this is basically ast from existing tokenizer
+ regularGrammar.addRule(regularGrammar.seq({ regularGrammar.val('a'), regularGrammar.val('b') }), TestSeq);
+ }
+
+ Map terminalsMap;
+ terminalsMap.put("TestSeq", TestSeq);
+
+ mUnifiedGrammarParser.compileTables(contextFreeGrammar, regularGrammar, terminalsMap);
+ }
+
+ public:
+ void compileTables(const tAlphabetType* grammar, ualni grammarLength) {
+ mUnifiedGrammarParser.parse(grammar, grammarLength);
+
+ // compile each ast into RegularGrammar and ContextFree Grammar api instructions
+ ContextFreeGrammar userContextFreeGrammar;
+ RegularGrammar userRegularGrammar;
+
+ // ...
+ // split ast into RE and CF part
+ // generate and execute grammar api commands
+ // use existing tokenizer code to create RE transition matrix
+ // ...
+ // compile tables from user grammar
+
+ Map terminalsMap;
+ terminalsMap.put("TestSeq", 0);
+
+ mUserParser.compileTables(userContextFreeGrammar, userRegularGrammar, terminalsMap);
+ }
+
+ UserParser::ParseResult parse(const tAlphabetType* grammar, ualni grammarLength) {
+ return mUserParser.parse(grammar, grammarLength);
+ }
+
+ private:
+ UGParser mUnifiedGrammarParser;
+ UserParser mUserParser;
+ };
+}
diff --git a/.wip/Language/tests/Test.hpp b/.wip/Language/tests/Test.hpp
new file mode 100644
index 0000000..f0b25f1
--- /dev/null
+++ b/.wip/Language/tests/Test.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+// #include "SimpleParser.hpp"
+
+const char* gGrammar = R"(
+# Grammar in the CF-RE United Format (Defined by language module)
+
+Rules : {
+ ScopeList : ScopeList Scope | Scope ;
+ Scope : \ScopeBegin StatementList \ScopeEnd;
+ StatementList : StatementList Statement \StatementEnd;
+ StatementList : Statement \StatementEnd;
+ Statement : \StatementBody;
+}
+
+Terminals : {
+ Space : " " | "\t" | "\n" | "\r";
+ ScopeBegin : "{";
+ ScopeEnd : "}";
+ StatementEnd : ";";
+ StatementBody : "a" | "b";
+}
+
+Start : Scope;
+Ignore : Space;
+)";
+
+const char* gSentence = R"(
+{}
+
+{ }
+
+{
+a;
+ a ; a ;
+
+ }
+
+ {
+a;
+ a;
+}
+)";
\ No newline at end of file
diff --git a/.wip/Language/tests/Tests.cpp b/.wip/Language/tests/Tests.cpp
new file mode 100644
index 0000000..ce4beb2
--- /dev/null
+++ b/.wip/Language/tests/Tests.cpp
@@ -0,0 +1,45 @@
+
+#include "Test.hpp"
+
+/*
+using namespace tp;
+
+void testAutomation() {
+
+ FiniteStateAutomation automata;
+
+ auto start = automata.addState(0, false);
+ auto end = automata.addState(1, true);
+
+ automata.addTransition(start, end, 'a');
+
+ automata.setStartState(start);
+
+ automata.makeDeterministic();
+}
+
+void test() {
+ auto parser = SimpleParser();
+
+ parser.compileTables(gGrammar, String::Logic::calcLength(gGrammar));
+ auto result = parser.parse(gSentence, String::Logic::calcLength(gSentence));
+}
+
+int main() {
+
+ tp::ModuleManifest* deps[] = { &tp::gModuleLanguage, nullptr };
+ tp::ModuleManifest testModule("Test", nullptr, nullptr, deps);
+
+ if (!testModule.initialize()) {
+ return 1;
+ }
+
+ testAutomation();
+ test();
+
+ testModule.deinitialize();
+}
+
+*/
+
+int main() { return 0; }
\ No newline at end of file
diff --git a/3DEditor/CMakeLists.txt b/3DEditor/CMakeLists.txt
new file mode 100644
index 0000000..356210b
--- /dev/null
+++ b/3DEditor/CMakeLists.txt
@@ -0,0 +1,24 @@
+project(3DEditor)
+
+### ---------------------- Externals --------------------- ###
+set(BINDINGS_INCLUDE ../Externals/glfw/include ../Externals)
+set(BINDINGS_LIBS glfw Imgui)
+
+### ---------------------- Static Library --------------------- ###
+file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
+file(GLOB HEADERS "./public/*.hpp" "./public/*/*.hpp")
+
+add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
+
+target_include_directories(${PROJECT_NAME} PUBLIC ./public/ ${BINDINGS_INCLUDE} ./ext/)
+target_link_libraries(${PROJECT_NAME} PUBLIC Widgets RasterRender RayTracer)
+target_link_libraries(${PROJECT_NAME} PRIVATE ${BINDINGS_LIBS})
+target_link_libraries(${PROJECT_NAME} PRIVATE OpenImageDenoise)
+
+### -------------------------- Applications -------------------------- ###
+file(GLOB APP_SOURCES "./applications/*.cpp")
+add_executable(3DEditorApp ${APP_SOURCES})
+target_link_libraries(3DEditorApp ${PROJECT_NAME})
+
+file(COPY "rsc" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
+file(COPY "rsc/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
diff --git a/3DEditor/applications/Entry.cpp b/3DEditor/applications/Entry.cpp
new file mode 100644
index 0000000..a7dfd9d
--- /dev/null
+++ b/3DEditor/applications/Entry.cpp
@@ -0,0 +1,42 @@
+
+#include "EditorWidget.hpp"
+
+#include "GraphicApplication.hpp"
+
+using namespace tp;
+
+
+class EditorGUI : public Application {
+public:
+ EditorGUI() {
+ auto canvas = this->mGraphics->getCanvas();
+ mGui = new EditorWidget(canvas, &mEditor);
+
+ mEditor.loadDefaults();
+
+ // mScene.mCamera.lookAtPoint({ 0, 0, 0 }, { 3, 3, 2 }, { 0, 0, 1 });
+ }
+
+ ~EditorGUI() override { delete mGui; }
+
+ void processFrame(EventHandler* eventHandler) override {
+ auto rec = RectF({ 0, 0 }, mWindow->getSize());
+
+ mGui->setArea(rec);
+ mGui->setVisible(true);
+ mGui->updateConfigWrapper(mWidgetManager);
+ mGui->procWrapper(*eventHandler, rec);
+ }
+
+ void drawFrame(Canvas* canvas) override { mGui->drawWrapper(*canvas); }
+
+private:
+ Editor mEditor;
+ WidgetManager mWidgetManager;
+ EditorWidget* mGui;
+};
+
+int main() {
+ EditorGUI gui;
+ gui.run();
+}
diff --git a/3DEditor/private/Denoise.cpp b/3DEditor/private/Denoise.cpp
new file mode 100644
index 0000000..7c4955a
--- /dev/null
+++ b/3DEditor/private/Denoise.cpp
@@ -0,0 +1,40 @@
+
+#include "Editor.hpp"
+
+#include "OpenImageDenoise/oidn.hpp"
+#include
+
+using namespace tp;
+
+void Editor::denoise() {
+ auto& out = mPathTracerBuffers;
+
+ // Initialize OIDN device
+ oidn::DeviceRef device = oidn::newDevice(oidn::DeviceType::CPU);
+ device.commit();
+
+ // Define buffer size
+ const int channels = 4; // ARGB
+ const auto size = out.color.size();
+ // Create the denoising filter
+ oidn::FilterRef filter = device.newFilter("RT"); // Use the 'RT' filter for path tracing
+
+ // Set the input and output buffer (same buffer for in-place denoising)
+ filter.setImage("color", out.color.getBuff(), oidn::Format::Float3, size.x, size.y, 0, sizeof(float) * channels); // ARGB
+ filter.setImage("output", out.color.getBuff(), oidn::Format::Float3, size.x, size.y, 0, sizeof(float) * channels); // ARGB
+
+ // Set additional parameters if needed
+ filter.set("hdr", false); // Assuming the input image is not HDR
+
+ // Commit the filter
+ filter.commit();
+
+ // Execute the filter
+ filter.execute();
+
+ // Check for errors
+ const char* errorMessage;
+ if (device.getError(errorMessage) != oidn::Error::None) {
+ std::cerr << "Error: " << errorMessage << std::endl;
+ }
+}
\ No newline at end of file
diff --git a/3DEditor/private/Editor.cpp b/3DEditor/private/Editor.cpp
new file mode 100644
index 0000000..e8838cf
--- /dev/null
+++ b/3DEditor/private/Editor.cpp
@@ -0,0 +1,118 @@
+
+#include "Editor.hpp"
+
+#include "GraphicsApi.hpp"
+
+using namespace tp;
+
+Editor::Editor() {
+
+ { // create path tracer gpu texture
+ glGenTextures(1, &mPathRenderTexture);
+ glBindTexture(GL_TEXTURE_2D, mPathRenderTexture);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 100, 100, 0, GL_RGBA, GL_FLOAT, nullptr);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+}
+
+void Editor::renderViewport() {
+ switch (mRenderType) {
+ case RenderType::RASTER:
+ mRasterRenderer.render(mScene, mScene.mRenderSettings.size);
+ break;
+
+ case RenderType::PATH_TRACER:
+ {
+ // TODO : include in render viewport method
+ // renderPathFrame();
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+uint4 Editor::getViewportTexID() {
+ switch (mRenderType) {
+ case RenderType::PATH_TRACER:
+ return mPathRenderTexture;
+
+ case RenderType::RASTER:
+ return mRasterRenderer.getRenderBufferID();
+
+ default:
+ return 0;
+ }
+}
+
+void Editor::loadDefaults() {
+ mScene.load("rsc/scene/script.lua");
+}
+
+void Editor::setViewportSize(const Vec2F& size) {
+ // TODO remove
+ // mScene.mCamera.rotate(0.01f, 0.0);
+
+ mScene.mRenderSettings.size = size;
+
+ mRasterRenderer.getRenderBuffer()->resize(size);
+ mScene.mCamera.setRatio(size.y / size.x);
+}
+
+Editor::~Editor() {
+ glDeleteTextures(1, &mPathRenderTexture);
+}
+
+void Editor::renderPathFrame() {
+ mPathRenderer.render(mScene, mPathTracerBuffers, mScene.mRenderSettings);
+ sendBuffersToGPU();
+}
+
+void Editor::sendBuffersToGPU() {
+ glBindTexture(GL_TEXTURE_2D, mPathRenderTexture);
+ const auto size = mPathTracerBuffers.color.size();
+ glTexImage2D(
+ GL_TEXTURE_2D,
+ 0,
+ GL_RGBA32F,
+ (GLsizei) size.x,
+ (GLsizei) size.y,
+ 0,
+ GL_RGBA,
+ GL_FLOAT,
+ mPathTracerBuffers.color.getBuff()
+ );
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+void Editor::denoisePathRenderBuffers() {
+ denoise();
+ sendBuffersToGPU();
+}
+
+void Editor::setRenderType(Editor::RenderType type) {
+ mRenderType = type;
+}
+
+void Editor::navigationOrbit(const Vec2F& delta) {
+ mScene.mCamera.rotate(delta.x, delta.y);
+}
+
+void Editor::navigationPan(const Vec2F& pos, const Vec2F& prevPos) {
+ mScene.mCamera.move(pos, prevPos);
+}
+
+void Editor::navigationZoom(halnf factor) {
+ mScene.mCamera.zoom(factor);
+}
+
+void Editor::navigationReset() {
+ mScene.mCamera.lookAtPoint({ 0, 0, 0 }, { 1, 5, 1 }, { 0, 0, 1 });
+}
\ No newline at end of file
diff --git a/3DEditor/public/Editor.hpp b/3DEditor/public/Editor.hpp
new file mode 100644
index 0000000..ee16122
--- /dev/null
+++ b/3DEditor/public/Editor.hpp
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "RayTracer.hpp"
+#include "RasterRender.hpp"
+
+namespace tp {
+ class Editor {
+ public:
+ enum class RenderType { RASTER, PATH_TRACER };
+
+ public:
+ Editor();
+ ~Editor();
+
+ void loadDefaults();
+
+ uint4 getViewportTexID();
+
+ void setViewportSize(const Vec2F& size);
+ void renderViewport();
+
+ void renderPathFrame();
+ void setRenderType(RenderType type);
+ void denoisePathRenderBuffers();
+
+ void navigationOrbit(const Vec2F& delta);
+ void navigationPan(const Vec2F& pos, const Vec2F& prevPos);
+ void navigationZoom(halnf factor);
+ void navigationReset();
+
+
+ private:
+ void sendBuffersToGPU();
+ void denoise();
+
+ private:
+ Scene mScene;
+
+ RenderType mRenderType = RenderType::RASTER;
+
+ RasterRender mRasterRenderer;
+ RayTracer mPathRenderer;
+
+ RayTracer::OutputBuffers mPathTracerBuffers;
+ uint4 mPathRenderTexture = 0;
+ };
+}
\ No newline at end of file
diff --git a/3DEditor/public/EditorWidget.hpp b/3DEditor/public/EditorWidget.hpp
new file mode 100644
index 0000000..36b32bb
--- /dev/null
+++ b/3DEditor/public/EditorWidget.hpp
@@ -0,0 +1,180 @@
+#include "Widgets.hpp"
+
+#include "Editor.hpp"
+
+namespace tp {
+
+ template
+ class ViewportWidget : public Widget {
+ public:
+ explicit ViewportWidget(Canvas* canvas, Editor* editor) {
+ mEditor = editor;
+ mImage = canvas->createImageFromTextId(mEditor->getViewportTexID(), { 0, 0 });
+ mCanvas = canvas;
+ }
+
+ ~ViewportWidget() { mCanvas->deleteImageHandle(mImage); }
+
+ void eventProcess(const Events& events) override {
+ mEditor->setViewportSize(this->mArea.size);
+ }
+
+ void eventDraw(Canvas& canvas) override {
+ mEditor->renderViewport();
+
+ canvas.updateTextureID(mImage, mEditor->getViewportTexID());
+ canvas.drawImage(this->mArea, &mImage, PI);
+ }
+
+ public:
+ Editor* mEditor;
+
+ Canvas* mCanvas = nullptr;
+ Canvas::ImageHandle mImage;
+ };
+
+ template
+ class EditorWidget : public Widget {
+ public:
+ EditorWidget(Canvas* canvas, Editor* editor) :
+ mViewport(canvas, editor)
+ {
+ mEditor = editor;
+
+ this->mChildWidgets.pushBack(&mViewport);
+ this->mChildWidgets.pushBack(&mSplitView);
+ this->mChildWidgets.pushBack(&mSettingsWidget);
+
+ // Render
+ {
+ mRenderPathTracer.setLabel("Render with Path Tracer");
+ mRenderRaster.setLabel("Render with Raster");
+ mRenderDeNoise.setLabel("Denoise (IntelOpenImage)");
+
+ mRenderMenu.addWidgetToMenu(&mRenderPathTracer);
+ mRenderMenu.addWidgetToMenu(&mRenderRaster);
+ mRenderMenu.addWidgetToMenu(&mRenderDeNoise);
+
+ mRenderMenu.setLabel("Render");
+ }
+
+ // Navigation
+ {
+ mNavigationPan.setLabel("Pan");
+ mNavigationOrbit.setLabel("Orbit");
+ mNavigationZoom.setLabel("Zoom");
+ mNavigationReset.setLabel("Reset");
+
+ mNavigationMenu.addWidgetToMenu(&mNavigationPan);
+ mNavigationMenu.addWidgetToMenu(&mNavigationOrbit);
+ mNavigationMenu.addWidgetToMenu(&mNavigationZoom);
+ mNavigationMenu.addWidgetToMenu(&mNavigationReset);
+
+ mNavigationMenu.setLabel("Navigation");
+ }
+
+ mSettingsWidget.addWidget(&mRenderMenu);
+ mSettingsWidget.addWidget(&mNavigationMenu);
+ }
+
+ void eventProcess(const Events& events) override {
+ mSplitView.setArea(this->mArea);
+
+ mViewport.setArea(mSplitView.getFirst());
+ mViewport.mEnable = mSplitView.getFirstEnabled();
+
+ mSettingsWidget.setArea(mSplitView.getSecond());
+ mSettingsWidget.mEnable = mSplitView.getSecondEnabled();
+
+ // render settings
+ {
+ if (mRenderPathTracer.isFired()) {
+ mEditor->renderPathFrame();
+ mEditor->setRenderType(Editor::RenderType::PATH_TRACER);
+ }
+
+ if (mRenderRaster.isFired()) {
+ mEditor->setRenderType(Editor::RenderType::RASTER);
+ }
+
+ if (mRenderDeNoise.isFired()) {
+ mEditor->denoisePathRenderBuffers();
+ }
+ }
+
+ // navigation
+ {
+ if (mNavigationOrbit.isFired()) {
+ mNavigationType = ORBIT;
+ }
+
+ if (mNavigationPan.isFired()) {
+ mNavigationType = PAN;
+ }
+
+ if (mNavigationZoom.isFired()) {
+ mNavigationType = ZOOM;
+ }
+
+ if (mNavigationReset.isFired()) {
+ mEditor->navigationReset();
+ }
+
+ const auto& activeArea = mViewport.mArea;
+ if (this->isHolding() && activeArea.isInside(events.getPointer())) {
+ switch (mNavigationType) {
+ case ORBIT:
+ mEditor->navigationOrbit(events.getPointerDelta() / activeArea.size * 3);
+ break;
+
+ case PAN:
+ {
+ auto pointer = (((events.getPointer() - activeArea.pos) / activeArea.size) - 0.5f) * 2;
+ auto prevPointer = (((events.getPointerPrev() - activeArea.pos) / activeArea.size) - 0.5f) * 2;
+ mEditor->navigationPan(prevPointer, pointer);
+ }
+ break;
+
+ case ZOOM:
+ mEditor->navigationZoom(1 + (events.getPointerDelta().y / activeArea.size.y));
+ break;
+ }
+ }
+ }
+ }
+
+ void eventDraw(Canvas& canvas) override {
+ canvas.rect(this->mArea, mBaseColor);
+ }
+
+ void eventUpdateConfiguration(WidgetManager& wm) override {
+ wm.setActiveId("3DEditor");
+ mBaseColor = wm.getColor("Base", "Base");
+ }
+
+ public:
+ Editor* mEditor = nullptr;
+
+ SplitView mSplitView;
+
+ ViewportWidget mViewport;
+ ScrollableWindow mSettingsWidget;
+
+ // Controls
+ CollapsableMenu mRenderMenu;
+ ButtonWidget mRenderPathTracer;
+ ButtonWidget mRenderRaster;
+ ButtonWidget mRenderDeNoise;
+
+ // Navigation
+ enum NavigationType { ORBIT, PAN, ZOOM } mNavigationType = ORBIT;
+
+ CollapsableMenu mNavigationMenu;
+ ButtonWidget mNavigationPan;
+ ButtonWidget mNavigationOrbit;
+ ButtonWidget mNavigationZoom;
+ ButtonWidget mNavigationReset;
+
+ RGBA mBaseColor;
+ };
+}
\ No newline at end of file
diff --git a/3DEditor/rsc/Font.ttf b/3DEditor/rsc/Font.ttf
new file mode 100644
index 0000000..8a63054
Binary files /dev/null and b/3DEditor/rsc/Font.ttf differ
diff --git a/3DEditor/rsc/scene/meshes.mtl b/3DEditor/rsc/scene/meshes.mtl
new file mode 100644
index 0000000..b2d7e64
--- /dev/null
+++ b/3DEditor/rsc/scene/meshes.mtl
@@ -0,0 +1,20 @@
+# Blender MTL File: 'scene.blend'
+# Material Count: 2
+
+newmtl Material
+Ns 323.999994
+Ka 1.000000 1.000000 1.000000
+Kd 0.800000 0.800000 0.800000
+Ks 0.500000 0.500000 0.500000
+Ke 0.000000 0.000000 0.000000
+Ni 1.450000
+d 1.000000
+illum 2
+
+newmtl None
+Ns 500
+Ka 0.8 0.8 0.8
+Kd 0.8 0.8 0.8
+Ks 0.8 0.8 0.8
+d 1
+illum 2
diff --git a/3DEditor/rsc/scene/meshes.obj b/3DEditor/rsc/scene/meshes.obj
new file mode 100644
index 0000000..4368e9e
--- /dev/null
+++ b/3DEditor/rsc/scene/meshes.obj
@@ -0,0 +1,112 @@
+# Blender v3.0.1 OBJ File: 'scene.blend'
+# www.blender.org
+mtllib meshes.mtl
+o Cube
+v -1.039076 1.039076 1.792917
+v -1.039076 1.039076 -1.285234
+v 1.039076 1.039076 1.792917
+v 1.039076 1.039076 -1.285234
+v -1.039076 -1.039076 1.792917
+v -1.039076 -1.039076 -1.285234
+v 1.039076 -1.039076 1.792917
+v 1.039076 -1.039076 -1.285234
+vt 0.625000 0.500000
+vt 0.625000 0.750000
+vt 0.875000 0.750000
+vt 0.875000 0.500000
+vt 0.375000 0.750000
+vt 0.375000 1.000000
+vt 0.625000 1.000000
+vt 0.375000 0.000000
+vt 0.375000 0.250000
+vt 0.625000 0.250000
+vt 0.625000 0.000000
+vt 0.125000 0.500000
+vt 0.125000 0.750000
+vt 0.375000 0.500000
+vn 0.0000 0.0000 -1.0000
+vn -1.0000 0.0000 0.0000
+vn 0.0000 1.0000 0.0000
+vn 0.0000 0.0000 1.0000
+vn 1.0000 0.0000 0.0000
+usemtl Material
+s off
+f 1/1/1 3/2/1 7/3/1 5/4/1
+f 4/5/2 8/6/2 7/7/2 3/2/2
+f 8/8/3 6/9/3 5/10/3 7/11/3
+f 6/12/4 8/13/4 4/5/4 2/14/4
+f 6/9/5 2/14/5 1/1/5 5/10/5
+o Cube.001
+v 0.411013 -0.208216 -1.264212
+v 0.411013 -0.208216 -0.612621
+v -0.208216 -0.411013 -1.264212
+v -0.208216 -0.411013 -0.612621
+v 0.208216 0.411013 -1.264212
+v 0.208216 0.411013 -0.612621
+v -0.411013 0.208216 -1.264212
+v -0.411013 0.208216 -0.612621
+vt 0.375000 0.000000
+vt 0.625000 0.000000
+vt 0.625000 0.250000
+vt 0.375000 0.250000
+vt 0.625000 0.500000
+vt 0.375000 0.500000
+vt 0.625000 0.750000
+vt 0.375000 0.750000
+vt 0.625000 1.000000
+vt 0.375000 1.000000
+vt 0.125000 0.500000
+vt 0.125000 0.750000
+vt 0.875000 0.500000
+vt 0.875000 0.750000
+vn 0.3112 -0.9503 0.0000
+vn -0.9503 -0.3112 0.0000
+vn -0.3112 0.9503 0.0000
+vn 0.9503 0.3112 0.0000
+vn 0.0000 0.0000 -1.0000
+vn 0.0000 0.0000 1.0000
+usemtl None
+s off
+f 9/15/6 10/16/6 12/17/6 11/18/6
+f 11/18/7 12/17/7 16/19/7 15/20/7
+f 15/20/8 16/19/8 14/21/8 13/22/8
+f 13/22/9 14/21/9 10/23/9 9/24/9
+f 11/25/10 15/20/10 13/22/10 9/26/10
+f 16/19/11 12/27/11 10/28/11 14/21/11
+o Cube.002
+v -0.351718 -0.467100 -0.602050
+v -0.351718 -0.467100 0.049542
+v -0.800825 0.004996 -0.602050
+v -0.800825 0.004996 0.049542
+v 0.120377 -0.017993 -0.602050
+v 0.120377 -0.017993 0.049542
+v -0.328730 0.454103 -0.602050
+v -0.328730 0.454103 0.049542
+vt 0.375000 0.000000
+vt 0.625000 0.000000
+vt 0.625000 0.250000
+vt 0.375000 0.250000
+vt 0.625000 0.500000
+vt 0.375000 0.500000
+vt 0.625000 0.750000
+vt 0.375000 0.750000
+vt 0.625000 1.000000
+vt 0.375000 1.000000
+vt 0.125000 0.500000
+vt 0.125000 0.750000
+vt 0.875000 0.500000
+vt 0.875000 0.750000
+vn -0.7245 -0.6892 0.0000
+vn -0.6892 0.7245 0.0000
+vn 0.7245 0.6892 0.0000
+vn 0.6892 -0.7245 0.0000
+vn 0.0000 0.0000 -1.0000
+vn 0.0000 0.0000 1.0000
+usemtl None
+s off
+f 17/29/12 18/30/12 20/31/12 19/32/12
+f 19/32/13 20/31/13 24/33/13 23/34/13
+f 23/34/14 24/33/14 22/35/14 21/36/14
+f 21/36/15 22/35/15 18/37/15 17/38/15
+f 19/39/16 23/34/16 21/36/16 17/40/16
+f 24/33/17 20/41/17 18/42/17 22/35/17
diff --git a/3DEditor/rsc/scene/script.lua b/3DEditor/rsc/scene/script.lua
new file mode 100644
index 0000000..ef6d484
--- /dev/null
+++ b/3DEditor/rsc/scene/script.lua
@@ -0,0 +1,25 @@
+
+Meshes = "meshes.obj"
+
+Camera = {
+ pos = { 0.5, 4.5, 0.2 },
+ size_x = 600,
+ size_y = 800,
+}
+
+Lights = {
+ {
+ pos = { -0.5, 3.5, 1 },
+ intensity = 1
+ },
+ {
+ pos = { 0, 0, 1 },
+ intensity = 0.5
+ },
+}
+
+RenderSettings = {
+ depth = 1,
+ spray = 1,
+ multisampling = 1,
+}
\ No newline at end of file
diff --git a/3DEditor/rsc/shaders/default.frag b/3DEditor/rsc/shaders/default.frag
new file mode 100644
index 0000000..2a1ba46
--- /dev/null
+++ b/3DEditor/rsc/shaders/default.frag
@@ -0,0 +1,39 @@
+#version 330 core
+
+in vec2 FragPos;
+in vec2 screenSize;
+
+out vec4 FragColor;
+
+void drawDepth() {
+ FragColor = vec4(gl_FragCoord.z, gl_FragCoord.z, gl_FragCoord.z, 1.f);
+}
+
+void drawPoints()
+{
+ // Define the point size
+ float pointSize = 15.0;
+
+ // Get the current fragment position in screen coordinates
+ vec2 fragCoord = gl_FragCoord.xy;
+
+ // Calculate the distance from the fragment to the vertex position
+ float dist = length(fragCoord - FragPos);
+
+ // If the distance is within the point size, draw the point
+ if (true || dist < pointSize)
+ {
+ FragColor = vec4(1.0, 0.0, 0.0, 1.0); // Red color for the point
+ }
+ else
+ {
+ FragColor = vec4(1.0, 1.0, 1.0, 1.0);
+ }
+
+ FragColor = vec4(FragPos.x, FragPos.y, 0, 1);
+}
+
+void main() {
+ drawPoints();
+ // drawDepth();
+}
\ No newline at end of file
diff --git a/3DEditor/rsc/shaders/default.vert b/3DEditor/rsc/shaders/default.vert
new file mode 100644
index 0000000..7a4c14b
--- /dev/null
+++ b/3DEditor/rsc/shaders/default.vert
@@ -0,0 +1,17 @@
+#version 330 core
+
+layout(location = 0) in vec3 Point;
+
+uniform vec2 ScreenSize;
+uniform vec4 Origin;
+uniform mat4 Basis;
+uniform mat4 Camera;
+
+out vec2 FragPos;
+out vec2 screenSize;
+
+void main() {
+ gl_Position = Camera * vec4(Point.xyz, 1.0);
+ FragPos = gl_Position.xy;
+ screenSize = ScreenSize;
+}
\ No newline at end of file
diff --git a/3DScene/CMakeLists.txt b/3DScene/CMakeLists.txt
new file mode 100644
index 0000000..f4bf146
--- /dev/null
+++ b/3DScene/CMakeLists.txt
@@ -0,0 +1,13 @@
+project(3DScene)
+
+### ---------------------- Static Library --------------------- ###
+file(GLOB SOURCES "./private/*.cpp")
+file(GLOB HEADERS "./public/*.hpp")
+
+add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
+
+target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
+target_include_directories(${PROJECT_NAME} PRIVATE ../Externals/)
+
+target_link_libraries(${PROJECT_NAME} PUBLIC Math)
+target_link_libraries(${PROJECT_NAME} PRIVATE Lua)
\ No newline at end of file
diff --git a/3DScene/private/LuaFormat.cpp b/3DScene/private/LuaFormat.cpp
new file mode 100644
index 0000000..3d61aad
--- /dev/null
+++ b/3DScene/private/LuaFormat.cpp
@@ -0,0 +1,220 @@
+
+#include "Scene.hpp"
+
+extern "C" {
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#include
+
+// Function to read a Lua table representing RenderSettings
+int readRenderSettings(lua_State* L, tp::RenderSettings& settings) {
+ lua_getglobal(L, "RenderSettings");
+ if (!lua_istable(L, -1)) {
+ printf("RenderSettings is not a table.\n");
+ return 0; // Error
+ }
+
+ // Read depth field
+ lua_getfield(L, -1, "depth");
+ if (lua_isnumber(L, -1)) {
+ settings.depth = (int) lua_tonumber(L, -1);
+ } else {
+ printf("RenderSettings 'depth' field is missing or not a number.\n");
+ lua_pop(L, 1); // Pop the 'depth' field
+ return 0; // Error
+ }
+ lua_pop(L, 1); // Pop the 'depth' field
+
+ // Read spray field
+ lua_getfield(L, -1, "spray");
+ if (lua_isnumber(L, -1)) {
+ settings.spray = (int) lua_tonumber(L, -1);
+ } else {
+ printf("RenderSettings 'spray' field is missing or not a number.\n");
+ lua_pop(L, 1); // Pop the 'spray' field
+ return 0; // Error
+ }
+ lua_pop(L, 1); // Pop the 'spray' field
+
+ // Read depth field
+ lua_getfield(L, -1, "multisampling");
+ if (lua_isnumber(L, -1)) {
+ settings.multisampling = (int) lua_tonumber(L, -1);
+ } else {
+ printf("RenderSettings 'depth' field is missing or not a number.\n");
+ lua_pop(L, 1); // Pop the 'depth' field
+ return 0; // Error
+ }
+ lua_pop(L, 1); // Pop the 'depth' field
+
+ return 1; // Success
+}
+
+// Function to read a Lua table representing a light
+int readLight(lua_State* L, tp::PointLight* light) {
+ lua_getfield(L, -1, "pos"); // Get the "pos" field from the light table
+ if (!lua_istable(L, -1)) {
+ printf("Light is missing the 'pos' table.\n");
+ return 0; // Error
+ }
+ for (int i = 0; i < 3; i++) {
+ lua_rawgeti(L, -1, i + 1); // Index is 1-based in Lua
+ if (!lua_isnumber(L, -1)) {
+ printf("Light 'pos' field is not a number at index %d.\n", i);
+ lua_pop(L, 2); // Pop both the number and the 'pos' table
+ return 0; // Error
+ }
+ light->pos[i] = lua_tonumber(L, -1);
+ lua_pop(L, 1); // Pop the number
+ }
+ lua_pop(L, 1); // Pop the 'pos' table
+
+ lua_getfield(L, -1, "intensity"); // Get the "intensity" field from the light table
+ if (!lua_isnumber(L, -1)) {
+ printf("Light is missing the 'intensity' field or it's not a number.\n");
+ lua_pop(L, 1); // Pop the 'intensity' field
+ return 0; // Error
+ }
+ light->intensity = lua_tonumber(L, -1);
+ lua_pop(L, 1); // Pop the 'intensity' field
+
+ return 1; // Success
+}
+
+bool tp::Scene::loadLuaFormat(const std::string& scenePath) {
+ lua_State* L = luaL_newstate();
+ luaL_openlibs(L);
+
+ namespace fs = std::filesystem;
+
+ fs::path fullPath(scenePath);
+
+ // Extract the filename
+ std::string fileName = fullPath.filename().string();
+
+ // Remove the filename from the path
+ fs::path directoryPath = fullPath.remove_filename();
+
+ if (luaL_dofile(L, scenePath.c_str()) != 0) {
+ lua_close(L);
+ printf("Cant open scene script.\n");
+ return false;
+ }
+
+ lua_getglobal(L, "Meshes");
+
+ if (lua_isstring(L, -1)) {
+ std::string meshesPath = lua_tostring(L, -1);
+
+ directoryPath /= meshesPath;
+
+ if (!loadOBJFormat(directoryPath.string())) {
+ printf("No 'meshes' loaded - check ur .obj path and validate content of .obj .\n");
+ return false;
+ }
+
+ } else {
+ printf("No 'meshes' path given.\n");
+ return false;
+ }
+
+ // --- camera
+
+ // Access Camera table
+ lua_getglobal(L, "Camera");
+ if (!lua_istable(L, -1)) {
+ printf("Camera is not a table.\n");
+ lua_close(L);
+ return false;
+ }
+
+ // Verify you are inside the "Camera" table
+ int cameraTableIndex = lua_gettop(L); // Get the index of the "Camera" table
+
+ // Access the "pos" field and validate it
+ lua_getfield(L, cameraTableIndex, "pos");
+ if (!lua_istable(L, -1) || lua_rawlen(L, -1) != 3) {
+ printf("Invalid 'pos' field in Camera table.\n");
+ lua_close(L);
+ return false;
+ }
+
+ // Read the values from the table
+ float pos[3];
+ for (int i = 0; i < 3; i++) {
+ lua_rawgeti(L, -1, i + 1);
+ if (lua_isnumber(L, -1)) {
+ pos[i] = lua_tonumber(L, -1);
+ } else {
+ printf("Invalid 'pos' field value at index %d.\n", i + 1);
+ lua_close(L);
+ return false;
+ }
+ lua_pop(L, 1);
+ }
+
+ // Access the "size_x" field and validate it
+ lua_getfield(L, cameraTableIndex, "size_x");
+ if (!lua_isnumber(L, -1)) {
+ printf("Invalid or missing 'size_x' field in Camera table.\n");
+ lua_close(L);
+ return false;
+ }
+ int size_x = lua_tointeger(L, -1);
+ lua_pop(L, 1); // Pop the 'size_x' value from the stack
+
+ // Access the "size_y" field and validate it
+ lua_getfield(L, cameraTableIndex, "size_y");
+ if (!lua_isnumber(L, -1)) {
+ printf("Invalid or missing 'size_y' field in Camera table.\n");
+ lua_close(L);
+ return false;
+ }
+ int size_y = lua_tointeger(L, -1);
+
+ mRenderSettings.size = { (tp::halnf) size_x, (tp::halnf) size_y };
+
+ mCamera.lookAtPoint({ 0, 0, 0 }, { pos[0], pos[1], pos[2] }, { 0, 0, 1 });
+ mCamera.setFOV(3.14 / 4);
+ mCamera.setFar(100);
+ mCamera.setRatio((tp::halnf) size_y / (tp::halnf) size_x);
+
+ // ---------- LIGHTS
+ {
+ lua_getglobal(L, "Lights");
+ if (!lua_istable(L, -1)) {
+ printf("Lights is not a table.\n");
+ lua_close(L);
+ return false; // Error
+ }
+
+ // Read and process each light in the "Lights" table
+ int numLights = lua_rawlen(L, -1); // Get the number of lights in the table
+ for (int i = 1; i <= numLights; i++) {
+ lua_rawgeti(L, -1, i); // Get the i-th element (light) from the table
+ if (lua_istable(L, -1)) {
+ tp::PointLight light;
+ if (!readLight(L, &light)) {
+ printf("Cant read lights data\n");
+ lua_close(L);
+ return false; // Error
+ }
+ mLights.append(light);
+ }
+ lua_pop(L, 1); // Pop the i-th light table
+ }
+ }
+
+ // ----------- settings --------------
+ if (!readRenderSettings(L, mRenderSettings)) {
+ printf("Cant Read Render Settings");
+ lua_close(L);
+ return false; // Error
+ }
+
+ lua_close(L);
+
+ return true;
+}
diff --git a/3DScene/private/OBJFormat.cpp b/3DScene/private/OBJFormat.cpp
new file mode 100644
index 0000000..3fcf8ad
--- /dev/null
+++ b/3DScene/private/OBJFormat.cpp
@@ -0,0 +1,51 @@
+
+#include "Scene.hpp"
+
+extern "C" {
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+#include "obj/OBJ_Loader.h"
+
+#include
+
+bool tp::Scene::loadOBJFormat(const std::string& objetsPath) {
+ using namespace tp;
+
+ objl::Loader Loader;
+
+ if (!Loader.LoadFile(objetsPath)) {
+ std::cout << "Failed to Load File. May have failed to find it or it was not an .obj file.\n";
+ return false;
+ }
+
+ for (auto& curMesh : Loader.LoadedMeshes) {
+ mObjects.append(Object());
+
+ auto object = &mObjects.last();
+
+ for (auto& vertex : curMesh.Vertices) {
+ // printf("{ %f, %f, %f }, \n", vertex.Position.X, vertex.Position.Y, vertex.Position.Z);
+ object->mTopology.Points.append(Vec3F{ vertex.Position.X, vertex.Position.Y, vertex.Position.Z });
+ object->mTopology.Normals.append(Vec3F{ vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z });
+ }
+
+ for (int j = 0; j < curMesh.Indices.size(); j += 3) {
+ auto idx1 = (uhalni) curMesh.Indices[j];
+ auto idx2 = (uhalni) curMesh.Indices[j + 1];
+ auto idx3 = (uhalni) curMesh.Indices[j + 2];
+ // printf("{ %i, %i, %i },\n", idx1, idx2, idx3);
+ object->mTopology.Indexes.append(Vec3{ idx1, idx2, idx3 });
+ }
+
+ if (object->mTopology.Normals.size() != object->mTopology.Points.size()) {
+ printf("Logic error loading normals\n");
+ }
+
+ object->mCache.Source = &object->mTopology;
+ object->mCache.updateCache();
+ }
+
+ return mObjects.size();
+}
\ No newline at end of file
diff --git a/3DScene/private/Scene.cpp b/3DScene/private/Scene.cpp
new file mode 100644
index 0000000..f1927b2
--- /dev/null
+++ b/3DScene/private/Scene.cpp
@@ -0,0 +1,6 @@
+
+#include "Scene.hpp"
+
+bool tp::Scene::load(const std::string& scenePath) {
+ return loadLuaFormat(scenePath);
+}
\ No newline at end of file
diff --git a/3DScene/public/Scene.hpp b/3DScene/public/Scene.hpp
new file mode 100644
index 0000000..f7aa56f
--- /dev/null
+++ b/3DScene/public/Scene.hpp
@@ -0,0 +1,61 @@
+#pragma once
+
+#include "Topology.hpp"
+#include "Buffer2D.hpp"
+#include "Camera.hpp"
+
+#include
+
+namespace tp {
+ struct RenderSettings {
+ uhalni depth = 2;
+ uhalni spray = 1;
+ ualni multisampling = 1;
+ Vec2 size;
+ };
+
+ class GPUBuffers {
+ public:
+ GPUBuffers() = default;
+ virtual ~GPUBuffers() = default;
+
+ virtual void drawCall() = 0;
+ };
+
+ class Object {
+ public:
+ Object() = default;
+
+ ~Object() {
+ delete mGUPBuffers;
+ };
+
+ public:
+ Topology mTopology;
+ TopologyCache mCache;
+ GPUBuffers* mGUPBuffers = nullptr;
+ };
+
+ struct PointLight {
+ Vec3F pos;
+ halnf fallOut = 1.f;
+ halnf intensity = 1.f;
+ };
+
+ class Scene {
+ public:
+ Scene() = default;
+
+ bool load(const std::string& scenePath);
+
+ bool loadLuaFormat(const std::string& scenePath);
+ bool loadOBJFormat(const std::string& objectsPath);
+
+ public:
+ Buffer