From 7e2534667c9c8598990b8cc0d5772f3f1d6095da Mon Sep 17 00:00:00 2001 From: John Galvin <96908304+Johnnyg63@users.noreply.github.com> Date: Sun, 22 Jun 2025 16:36:40 +0100 Subject: [PATCH 01/11] Base XCode single project file for MacOS. Base XCode single project file for MacOS. --- dev/xcode_macos/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 dev/xcode_macos/README.md diff --git a/dev/xcode_macos/README.md b/dev/xcode_macos/README.md new file mode 100644 index 00000000..f7e3fcb6 --- /dev/null +++ b/dev/xcode_macos/README.md @@ -0,0 +1,9 @@ +# olcPGE 3 for MacOS XCode Project File + +

+CopyRight: + One Lone Coder +

+

+ ToDo +

From 45750bba597d8bcf1ad896d97f763f5dee1eebfd Mon Sep 17 00:00:00 2001 From: John Galvin <96908304+Johnnyg63@users.noreply.github.com> Date: Sun, 22 Jun 2025 16:37:50 +0100 Subject: [PATCH 02/11] Base XCode single project file for MacOS. Base Project File --- .../olcPGE3.xcodeproj/project.pbxproj | 312 ++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 dev/xcode_macos/olcPGE3.xcodeproj/project.pbxproj diff --git a/dev/xcode_macos/olcPGE3.xcodeproj/project.pbxproj b/dev/xcode_macos/olcPGE3.xcodeproj/project.pbxproj new file mode 100644 index 00000000..055de16c --- /dev/null +++ b/dev/xcode_macos/olcPGE3.xcodeproj/project.pbxproj @@ -0,0 +1,312 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 8998CE2F2E057F85007DDD96 /* test_vector2d.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8998CE2B2E057F85007DDD96 /* test_vector2d.cpp */; }; + 8998CE302E057F85007DDD96 /* test_main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8998CE2D2E057F85007DDD96 /* test_main.cpp */; }; + 8998CE312E057F85007DDD96 /* test_pixels.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8998CE2E2E057F85007DDD96 /* test_pixels.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 8998CE1E2E057F4E007DDD96 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 8998CE202E057F4E007DDD96 /* olcPGE3 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = olcPGE3; sourceTree = BUILT_PRODUCTS_DIR; }; + 8998CE2B2E057F85007DDD96 /* test_vector2d.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test_vector2d.cpp; sourceTree = ""; }; + 8998CE2C2E057F85007DDD96 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; + 8998CE2D2E057F85007DDD96 /* test_main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test_main.cpp; sourceTree = ""; }; + 8998CE2E2E057F85007DDD96 /* test_pixels.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = test_pixels.cpp; sourceTree = ""; }; + 8998CE332E057F92007DDD96 /* pixel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pixel.h; sourceTree = ""; }; + 8998CE342E057F92007DDD96 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = ""; }; + 8998CE352E057F92007DDD96 /* matrix2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = matrix2d.h; sourceTree = ""; }; + 8998CE362E057F92007DDD96 /* vector2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vector2d.h; sourceTree = ""; }; + 8998CE372E057F92007DDD96 /* transform2d.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = transform2d.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8998CE1D2E057F4E007DDD96 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8998CE172E057F4E007DDD96 = { + isa = PBXGroup; + children = ( + 8998CE322E057F92007DDD96 /* src */, + 8998CE2A2E057F85007DDD96 /* tests */, + 8998CE212E057F4E007DDD96 /* Products */, + ); + sourceTree = ""; + }; + 8998CE212E057F4E007DDD96 /* Products */ = { + isa = PBXGroup; + children = ( + 8998CE202E057F4E007DDD96 /* olcPGE3 */, + ); + name = Products; + sourceTree = ""; + }; + 8998CE2A2E057F85007DDD96 /* tests */ = { + isa = PBXGroup; + children = ( + 8998CE2B2E057F85007DDD96 /* test_vector2d.cpp */, + 8998CE2C2E057F85007DDD96 /* CMakeLists.txt */, + 8998CE2D2E057F85007DDD96 /* test_main.cpp */, + 8998CE2E2E057F85007DDD96 /* test_pixels.cpp */, + ); + name = tests; + path = ../tests; + sourceTree = ""; + }; + 8998CE322E057F92007DDD96 /* src */ = { + isa = PBXGroup; + children = ( + 8998CE332E057F92007DDD96 /* pixel.h */, + 8998CE342E057F92007DDD96 /* config.h */, + 8998CE352E057F92007DDD96 /* matrix2d.h */, + 8998CE362E057F92007DDD96 /* vector2d.h */, + 8998CE372E057F92007DDD96 /* transform2d.h */, + ); + name = src; + path = ../src; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8998CE1F2E057F4E007DDD96 /* olcPGE3 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8998CE272E057F4E007DDD96 /* Build configuration list for PBXNativeTarget "olcPGE3" */; + buildPhases = ( + 8998CE1C2E057F4E007DDD96 /* Sources */, + 8998CE1D2E057F4E007DDD96 /* Frameworks */, + 8998CE1E2E057F4E007DDD96 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = olcPGE3; + productName = olcPGE3; + productReference = 8998CE202E057F4E007DDD96 /* olcPGE3 */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8998CE182E057F4E007DDD96 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1430; + TargetAttributes = { + 8998CE1F2E057F4E007DDD96 = { + CreatedOnToolsVersion = 14.3.1; + }; + }; + }; + buildConfigurationList = 8998CE1B2E057F4E007DDD96 /* Build configuration list for PBXProject "olcPGE3" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8998CE172E057F4E007DDD96; + productRefGroup = 8998CE212E057F4E007DDD96 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8998CE1F2E057F4E007DDD96 /* olcPGE3 */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 8998CE1C2E057F4E007DDD96 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8998CE302E057F85007DDD96 /* test_main.cpp in Sources */, + 8998CE312E057F85007DDD96 /* test_pixels.cpp in Sources */, + 8998CE2F2E057F85007DDD96 /* test_vector2d.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 8998CE252E057F4E007DDD96 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 13.3; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 8998CE262E057F4E007DDD96 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 13.3; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + }; + name = Release; + }; + 8998CE282E057F4E007DDD96 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 8998CE292E057F4E007DDD96 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8998CE1B2E057F4E007DDD96 /* Build configuration list for PBXProject "olcPGE3" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8998CE252E057F4E007DDD96 /* Debug */, + 8998CE262E057F4E007DDD96 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8998CE272E057F4E007DDD96 /* Build configuration list for PBXNativeTarget "olcPGE3" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8998CE282E057F4E007DDD96 /* Debug */, + 8998CE292E057F4E007DDD96 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 8998CE182E057F4E007DDD96 /* Project object */; +} From e5d489b1b1a56762d5b1799e73af1fa922b1fe20 Mon Sep 17 00:00:00 2001 From: John Galvin <96908304+Johnnyg63@users.noreply.github.com> Date: Sun, 22 Jun 2025 16:44:46 +0100 Subject: [PATCH 03/11] Added new files --- .../project.xcworkspace/contents.xcworkspacedata | 7 +++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++++++++ 2 files changed, 15 insertions(+) create mode 100644 dev/xcode_macos/olcPGE3.xcodeproj/olcPGE3.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 dev/xcode_macos/olcPGE3.xcodeproj/olcPGE3.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/dev/xcode_macos/olcPGE3.xcodeproj/olcPGE3.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/dev/xcode_macos/olcPGE3.xcodeproj/olcPGE3.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/dev/xcode_macos/olcPGE3.xcodeproj/olcPGE3.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/dev/xcode_macos/olcPGE3.xcodeproj/olcPGE3.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/dev/xcode_macos/olcPGE3.xcodeproj/olcPGE3.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/dev/xcode_macos/olcPGE3.xcodeproj/olcPGE3.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + From 68532655f4bb87a1e4907284b80e5f0c77449bf1 Mon Sep 17 00:00:00 2001 From: John Galvin <96908304+Johnnyg63@users.noreply.github.com> Date: Sun, 22 Jun 2025 17:24:36 +0100 Subject: [PATCH 04/11] Update README.md --- dev/xcode_macos/README.md | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/dev/xcode_macos/README.md b/dev/xcode_macos/README.md index f7e3fcb6..7eac8a09 100644 --- a/dev/xcode_macos/README.md +++ b/dev/xcode_macos/README.md @@ -1,9 +1,33 @@ # olcPGE 3 for MacOS XCode Project File

-CopyRight: - One Lone Coder + This is the XCode project for MacOS.
+

+

+ To launch this project in XCode: +

    +
  • Launch XCode
  • +
  • Select Code a Project
  • +
  • Enter this repo git link
  • +
  • Clone...
  • +
+ Once the project is cloned, XCode will automatically detect the olcPGE3.xcodeproj folder and load the solution for you.
+
+ Finally althought the olcPGE3.xcodeproj is displayed as a folder in GitHub, XCode will detected and display it as an XCode project +

+ +

+ Some simple rules: +

  • The olcPGE3.xcodeproj folder and files are all auto generated
  • +
  • The olcPGE3.xcodeproj folder and files should never be editied outside of XCode itself
  • +
  • For this solution we will be using XCode Version 14.3.1 (14E300c), if you are making edits to the XCode you must use this version
  • +
  • If you are using a higher version of XCode Version 14.3.1 (14E300c), and wish to make edit, please raise a PR and attached your olcPGE3.xcodeproj solultion to the PR, the team will then see how best to get your edits in
  • +
  • Finally olcPGE3.xcodeproj folder is the very same concept as the Solution and Filer files in Visual Studio. Therefore it is important for the team to ensure these files are always stable and can be loaded by a wide range of users. Hence the rules above
  • + + Any questions please reach out to us on Discord: One Lone Coder

    +

    - ToDo +CopyRight: + One Lone Coder

    From e8aff6e408b143baa5e132e8d1a6e82702d213df Mon Sep 17 00:00:00 2001 From: John Galvin <96908304+Johnnyg63@users.noreply.github.com> Date: Sun, 22 Jun 2025 17:48:01 +0100 Subject: [PATCH 05/11] Update README.md --- dev/xcode_macos/README.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/dev/xcode_macos/README.md b/dev/xcode_macos/README.md index 7eac8a09..8e61d3bc 100644 --- a/dev/xcode_macos/README.md +++ b/dev/xcode_macos/README.md @@ -2,20 +2,18 @@

    This is the XCode project for MacOS.
    -

    -

    To launch this project in XCode: -

      -
    • Launch XCode
    • -
    • Select Code a Project
    • -
    • Enter this repo git link
    • -
    • Clone...
    • -
    - Once the project is cloned, XCode will automatically detect the olcPGE3.xcodeproj folder and load the solution for you.
    -
    - Finally althought the olcPGE3.xcodeproj is displayed as a folder in GitHub, XCode will detected and display it as an XCode project

    + +
      +
    • Launch XCode and select **Clone and existing project**
    • +
    • Enter the repo git link and click **Clone**
    • +
    • Once the project is cloned, XCode will automatically detect the olcPGE3.xcodeproj folder and load the solution for you
    • +
    • Finally althought the olcPGE3.xcodeproj is displayed as a folder in GitHub, XCode will detected and display it as an XCode project
    • + +
    +

    Some simple rules:

  • The olcPGE3.xcodeproj folder and files are all auto generated
  • From 0026f4c44f740259e4c7cfd20b13e81998ea4190 Mon Sep 17 00:00:00 2001 From: John Galvin <96908304+Johnnyg63@users.noreply.github.com> Date: Sun, 22 Jun 2025 18:02:20 +0100 Subject: [PATCH 06/11] Create CMakeLists.txt Added base CMakeList.txt for moros --- dev/tests/CMakeLists.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 dev/tests/CMakeLists.txt diff --git a/dev/tests/CMakeLists.txt b/dev/tests/CMakeLists.txt new file mode 100644 index 00000000..4d2ba57d --- /dev/null +++ b/dev/tests/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.10) +project(pge3_test) + +# Set C++ Standards +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +###################################################################### +# Directories + +# Source Files are Curated Here +file( + GLOB SOURCE_CXX_FILES + "*.c" + "*.cpp" + "*.h" + "*.hpp" +) + +# Executable aka binary output +add_executable(${CMAKE_PROJECT_NAME} ${SOURCE_CXX_FILES}) From 81234f8b38b308fcdd924926f63c36681f45b17c Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Fri, 27 Jun 2025 21:47:12 +0100 Subject: [PATCH 07/11] Added matrix and transform classes --- dev/msvc/olcPGE3.vcxproj | 3 +- dev/msvc/olcPGE3.vcxproj.filters | 5 +- .../olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj | 3 + .../olcPGE3_BuildSH.vcxproj.filters | 5 + .../olcPGE3_BuildSH/olcPixelGameEngine3.h | 421 +++++++++++++++++- dev/src/core.cpp | 10 +- dev/src/core.h | 6 +- dev/src/matrix2d.h | 0 dev/src/matrix3d.h | 221 +++++++++ dev/src/sh_template.h | 6 +- dev/src/transform2d.h | 215 +++++++++ dev/tests/test_main.cpp | 3 +- dev/tests/test_matrices.cpp | 24 + 13 files changed, 909 insertions(+), 13 deletions(-) delete mode 100644 dev/src/matrix2d.h create mode 100644 dev/src/matrix3d.h create mode 100644 dev/tests/test_matrices.cpp diff --git a/dev/msvc/olcPGE3.vcxproj b/dev/msvc/olcPGE3.vcxproj index 0e3f218d..a1e43951 100644 --- a/dev/msvc/olcPGE3.vcxproj +++ b/dev/msvc/olcPGE3.vcxproj @@ -182,7 +182,7 @@ - + @@ -198,6 +198,7 @@ false false + false false diff --git a/dev/msvc/olcPGE3.vcxproj.filters b/dev/msvc/olcPGE3.vcxproj.filters index 7751fa76..21d9b9e5 100644 --- a/dev/msvc/olcPGE3.vcxproj.filters +++ b/dev/msvc/olcPGE3.vcxproj.filters @@ -45,7 +45,7 @@ Header Files - + Header Files @@ -74,5 +74,8 @@ Source Files + + Source Files\tests + \ No newline at end of file diff --git a/dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj b/dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj index 8db3ecb8..e93c0b77 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj +++ b/dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj @@ -21,6 +21,9 @@ + + + 17.0 Win32Proj diff --git a/dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj.filters b/dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj.filters index 64dec603..c9a96e5b 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj.filters +++ b/dev/msvc/olcPGE3_BuildSH/olcPGE3_BuildSH.vcxproj.filters @@ -19,4 +19,9 @@ Source Files + + + Header Files + + \ No newline at end of file diff --git a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h index 66ad6661..84b1ad71 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h +++ b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h @@ -660,7 +660,410 @@ namespace olc } #define PGE_VECTOR2D_DECLARED 1 #endif -//! + +#if !defined(PGE_MATRIX3D_DECLARED) +namespace olc +{ + + /* + A complete 3x3 Matrix structure, with a variety + of useful utility functions and operator overloads + specifically targeting 2D graphical transformations + + as per https://en.wikipedia.org/wiki/Affine_transformation + + Access: column, row + */ + template + struct m_3d + { + static_assert(std::is_arithmetic::value, "olc::m_2d must be numeric"); + + // The 3x3 elements! + std::array m{ {0 } }; + + // Constructor created identity matrix + inline constexpr m_3d() + { + identity(); + } + + // Copy constructor + inline constexpr m_3d(const m_3d& mat) = default; + + // Assignment operator + inline constexpr m_3d& operator=(const m_3d& mat) = default; + + // Retrieve a specific element's 1D index + inline constexpr size_t idx(const size_t c, const size_t r) const + { + return r * 3 + c; + } + + // Retrieve non-const access to specific element + inline constexpr T& operator()(const size_t col, const size_t row) + { + return m[idx(col, row)]; + } + + // Retrieve const access to specific element + inline constexpr const T& operator()(const size_t col, const size_t row) const + { + return m[idx(col, row)]; + } + + // Set all elements to 0 + inline constexpr void clear() + { + std::fill(m.begin(), m.end(), T(0)); + } + + // Create identity matrix + inline constexpr void identity() + { + clear(); + auto& me = (*this); + me(0, 0) = 1; + me(1, 1) = 1; + me(2, 2) = 1; + } + + // Create translation matrix via components + template + inline constexpr void translate(const Q x, const Q y) + { + identity(); + auto& me = (*this); + me(2, 0) = T(x); + me(2, 1) = T(y); + } + + // Create translation matrix via vector + template + inline constexpr void translate(const olc::v_2d& v) + { + translate(v.x, v.y); + } + + // Create scaling matrix via components + template + inline constexpr void scale(const Q x, const Q y) + { + identity(); + auto& me = (*this); + me(0, 0) = T(x); + me(1, 1) = T(y); + } + + // Create scaling matrix via vector + template + inline constexpr void scale(const olc::v_2d& v) + { + scale(v.x, v.y); + } + + // Create rotation matrix with radians + template + inline constexpr void rotate(const Q rads) + { + identity(); + auto& me = (*this); + me(0, 0) = std::cos(rads); + me(0, 1) = std::sin(rads); + me(1, 0) = -me(0, 1); + me(1, 1) = me(0, 0); + } + + // Create shearing matrix via components + template + inline constexpr void shear(const Q x, const Q y) + { + identity(); + auto& me = (*this); + me(0, 1) = T(y); + me(1, 0) = T(x); + } + + // Create shearing matrix via vector + template + inline constexpr void shear(const olc::v_2d& v) + { + shear(v.x, v.y); + } + + + // Return inverted matrix + inline constexpr auto invert() const + { + // https://stackoverflow.com/a/18504573 + olc::m_3d out; + auto& me = (*this); + + T det = me(0, 0) * (me(1, 1) * me(2, 2) - me(2, 1) * me(1, 2)) - + me(0, 1) * (me(1, 0) * me(2, 2) - me(1, 2) * me(2, 0)) + + me(0, 2) * (me(1, 0) * me(2, 1) - me(1, 1) * me(2, 0)); + + T invdet = T(1) / det; + + out(0, 0) = (me(1, 1) * me(2, 2) - me(2, 1) * me(1, 2)) * invdet; + out(0, 1) = (me(0, 2) * me(2, 1) - me(0, 1) * me(2, 2)) * invdet; + out(0, 2) = (me(0, 1) * me(1, 2) - me(0, 2) * me(1, 1)) * invdet; + out(1, 0) = (me(1, 2) * me(2, 0) - me(1, 0) * me(2, 2)) * invdet; + out(1, 1) = (me(0, 0) * me(2, 2) - me(0, 2) * me(2, 0)) * invdet; + out(1, 2) = (me(1, 0) * me(0, 2) - me(0, 0) * me(1, 2)) * invdet; + out(2, 0) = (me(1, 0) * me(2, 1) - me(2, 0) * me(1, 1)) * invdet; + out(2, 1) = (me(2, 0) * me(0, 1) - me(0, 0) * me(2, 1)) * invdet; + out(2, 2) = (me(0, 0) * me(1, 1) - me(1, 0) * me(0, 1)) * invdet; + return out; + } + + // Transform a vector by this matrix + template + inline constexpr auto operator * (const olc::v_2d& v) const + { + auto& me = *this; + olc::v_2d vOut; + vOut.x = Q(me(0, 0) * v.x + me(1, 0) * v.y + me(2, 0) * Q(1)); + vOut.y = Q(me(0, 1) * v.x + me(1, 1) * v.y + me(2, 1) * Q(1)); + Q z = Q(me(0, 2) * v.x + me(1, 2) * v.y + me(2, 2) * Q(1)); + return (vOut / z); + } + + // Multiply this matrix with another + template + inline constexpr auto operator * (const olc::m_3d& rhs) const + { + auto& me = *this; + olc::m_3d out; + for (size_t c = 0; c < 3; c++) + for (size_t r = 0; r < 3; r++) + out(r, c) = me(r, 0) * rhs(0, c) + me(r, 1) * rhs(1, c) + me(r, 2) * rhs(2, c); + return out; + } + + // Return this matrix as a std::string, of the form "[c1r1, c2r1, c3r1]\n[c1r2, c2r2, c3r2]\n[c1r3, c2r3, c3r3]" + inline std::string str() const + { + const auto& me = *this; + return std::string("[") + std::to_string(me(0, 0)) + "," + std::to_string(me(1, 0)) + "," + std::to_string(me(2, 0)) + "]\n" + + "[" + std::to_string(me(0, 1)) + "," + std::to_string(me(1, 1)) + "," + std::to_string(me(2, 1)) + "]\n" + + "[" + std::to_string(me(0, 2)) + "," + std::to_string(me(1, 2)) + "," + std::to_string(me(2, 2)) + "]\n"; + } + }; + + // Allow olc::m_3d to play nicely with std::cout + template + inline std::ostream& operator << (std::ostream& os, const m_3d& rhs) + { + os << rhs.str(); + return os; + } + + // Convenient types ready-to-go + typedef m_3d mf3d; + typedef m_3d md3d; +} +#define PGE_MATRIX3D_DECLARED 1 +#endif + +#if !defined(PGE_TRANSFORM2D_DECLARED) +namespace olc +{ + namespace internal + { + // Return sign of number as -1 or +1 + template + inline constexpr int sgn(T val) + { + return (T(0) < val) - (val < T(0)); + } + } + + + + /* + A complete 2D transformation structure + */ + template + class t_2d + { + static_assert(std::is_floating_point::value, "olc::t_2d must be floating point type"); + + public: + // Constructor + inline constexpr t_2d() = default; + + // Copy constructor + inline constexpr t_2d(const t_2d& t) = default; + + // Assignment operator + inline constexpr t_2d& operator=(const t_2d& t) = default; + + // Transform a vector by this transform + template + inline constexpr auto forward(const olc::v_2d& v) + { + return m_mForward * v; + } + + // Transform a vector of v_2d by this transform + template + inline constexpr auto forward(const std::vector>& v) + { + std::vector> o(v.size()); + std::transform(v.begin(), v.end(), o.begin(), [this](const olc::v_2d& i) {return m_mForward * i; }); + return o; + } + + // Transform a vector by the inverse of this transform + template + inline constexpr auto inverse(const olc::v_2d& v) + { + return m_mInverse * v; + } + + // Transform a vector by this transform + template + inline constexpr auto inverse(const std::vector>& v) + { + std::vector> o(v.size()); + std::transform(v.begin(), v.end(), o.begin(), [this](const olc::v_2d& i) {return m_mInverse * i; }); + return o; + } + + + // Set scaling component of this transformation + template + inline constexpr void scale(const olc::v_2d& v) + { + m_vScale = v; + m_mScale.scale(m_vScale); + update(); + } + + // Get scaling component of this transformation + inline constexpr const auto& scale() const + { + return m_vScale; + } + + // Set translation component of this transformation + template + inline constexpr void translate(const olc::v_2d& v) + { + m_vTranslate = v; + m_mTranslate.translate(m_vTranslate); + update(); + } + + // Get translation component of this transformation + inline constexpr const auto& translate() const + { + return m_vTranslate; + } + + // Set translation component of this transformation + template + inline constexpr void rotate(const Q& v, const olc::v_2d& p = { 0,0 }) + { + m_dTheta = T(v); + m_vRotatePoint = p; + m_3d matTrans1; + matTrans1.translate(-m_vRotatePoint); + m_3d matTrans2; + matTrans2.translate(m_vRotatePoint); + m_3d matRotate; + matRotate.rotate(m_dTheta); + m_mRotate = matTrans1 * matRotate * matTrans2; + update(); + } + + // Get translation component of this transformation + inline constexpr const auto& rotate() const + { + return m_dTheta; + } + + // Set shear component of this transformation + template + inline constexpr void shear(const olc::v_2d& v) + { + m_vShear = v; + m_mShear.shear(m_vShear); + update(); + } + + // Get shear component of this transformation + inline constexpr const auto& shear() const + { + return m_vShear; + } + + // Get forward transformation matrix + inline constexpr const auto& forward_matrix() const + { + return m_mForward; + } + + // Get inverse transformation matrix + inline constexpr const auto& inverse_matrix() const + { + return m_mInverse; + } + + // Construct transform from an existing transformation matrix + template + inline constexpr void from_matrix(const olc::m_3d& mat) + { + // https://math.stackexchange.com/a/13165 + m_vTranslate = + { + T(mat(2, 0)), + T(mat(2, 1)) + }; + + m_vScale = + { + T(olc::internal::sgn(mat(0, 0)) * std::hypot(mat(0, 0), mat(1, 0))), + T(olc::internal::sgn(mat(1, 1)) * std::hypot(mat(0, 1), mat(1, 1))) + }; + + m_dTheta = T(std::atan2(mat(0, 1), mat(1, 1))); + + m_mTranslate.translate(m_vTranslate); + m_mScale.scale(m_vScale); + m_mRotate.rotate(m_dTheta); + update(); + } + + protected: + // Constructs resultant matrices when transformation changes + inline constexpr void update() + { + m_mForward = m_mRotate * m_mShear * m_mScale * m_mTranslate; + m_mInverse = m_mForward.invert(); + } + + protected: + T m_dTheta = 0; + olc::v_2d m_vScale = { 1,1 }; + olc::v_2d m_vTranslate = { 0,0 }; + olc::v_2d m_vRotatePoint = { 0,0 }; + olc::v_2d m_vShear = { 0,0 }; + olc::m_3d m_mForward; + olc::m_3d m_mInverse; + olc::m_3d m_mScale; + olc::m_3d m_mTranslate; + olc::m_3d m_mRotate; + olc::m_3d m_mShear; + }; + + typedef t_2d tf2d; + typedef t_2d td2d; +} +#define PGE_TRANSFORM2D_DECLARED 1 +#endif + #if !defined(PGE_WINDOW_DECLARED) namespace olc { @@ -724,7 +1127,7 @@ namespace olc public: bool Construct(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize, bool bFullScreen = false); - bool Construct(const PGEConfig& config = PGEConfig{}); + bool Construct(const PGEConfig& cfg = PGEConfig{}); public: bool Start(); @@ -743,7 +1146,9 @@ namespace olc // Frame Timing & Overall Clocking std::chrono::steady_clock::time_point timeFrame1; std::chrono::steady_clock::time_point timeFrame2; - std::chrono::duration durationFrame; + std::chrono::duration durationFrame{ 0 }; + + PGEConfig config; }; } #define PGE_CORE_DECLARED 1 @@ -815,11 +1220,17 @@ namespace olc bool PixelGameEngine::Construct(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize, bool bFullScreen) { + config.vScreenSize = vScreenSize; + config.vPixelSize = vPixelSize; + config.bFullScreen = bFullScreen; + return false; } - bool PixelGameEngine::Construct(const PGEConfig& config) - { + bool PixelGameEngine::Construct(const PGEConfig& cfg) + { + config = cfg; + return false; } diff --git a/dev/src/core.cpp b/dev/src/core.cpp index 79d846a3..214fd726 100644 --- a/dev/src/core.cpp +++ b/dev/src/core.cpp @@ -13,11 +13,17 @@ namespace olc bool PixelGameEngine::Construct(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize, bool bFullScreen) { + config.vScreenSize = vScreenSize; + config.vPixelSize = vPixelSize; + config.bFullScreen = bFullScreen; + return false; } - bool PixelGameEngine::Construct(const PGEConfig& config) - { + bool PixelGameEngine::Construct(const PGEConfig& cfg) + { + config = cfg; + return false; } diff --git a/dev/src/core.h b/dev/src/core.h index 2e243d9d..d9f766b1 100644 --- a/dev/src/core.h +++ b/dev/src/core.h @@ -57,7 +57,7 @@ namespace olc public: bool Construct(const olc::vi2d& vScreenSize, const olc::vi2d& vPixelSize, bool bFullScreen = false); - bool Construct(const PGEConfig& config = PGEConfig{}); + bool Construct(const PGEConfig& cfg = PGEConfig{}); public: bool Start(); @@ -76,7 +76,9 @@ namespace olc // Frame Timing & Overall Clocking std::chrono::steady_clock::time_point timeFrame1; std::chrono::steady_clock::time_point timeFrame2; - std::chrono::duration durationFrame; + std::chrono::duration durationFrame{ 0 }; + + PGEConfig config; }; } #define PGE_CORE_DECLARED 1 diff --git a/dev/src/matrix2d.h b/dev/src/matrix2d.h deleted file mode 100644 index e69de29b..00000000 diff --git a/dev/src/matrix3d.h b/dev/src/matrix3d.h new file mode 100644 index 00000000..9e75f1c9 --- /dev/null +++ b/dev/src/matrix3d.h @@ -0,0 +1,221 @@ +#pragma once + +//! START STDHEADER GLOBAL +#include +#include +#include +#include +#include +//! END STDHEADER + +//! START CUSTOMHEADER +#include "config.h" +#include "vector2d.h" +//! END CUSTOMHEADER + +//! START DECLARATION +#if !defined(PGE_MATRIX3D_DECLARED) +namespace olc +{ + + /* + A complete 3x3 Matrix structure, with a variety + of useful utility functions and operator overloads + specifically targeting 2D graphical transformations + + as per https://en.wikipedia.org/wiki/Affine_transformation + + Access: column, row + */ + template + struct m_3d + { + static_assert(std::is_arithmetic::value, "olc::m_2d must be numeric"); + + // The 3x3 elements! + std::array m{ {0 } }; + + // Constructor created identity matrix + inline constexpr m_3d() + { + identity(); + } + + // Copy constructor + inline constexpr m_3d(const m_3d& mat) = default; + + // Assignment operator + inline constexpr m_3d& operator=(const m_3d& mat) = default; + + // Retrieve a specific element's 1D index + inline constexpr size_t idx(const size_t c, const size_t r) const + { + return r * 3 + c; + } + + // Retrieve non-const access to specific element + inline constexpr T& operator()(const size_t col, const size_t row) + { + return m[idx(col, row)]; + } + + // Retrieve const access to specific element + inline constexpr const T& operator()(const size_t col, const size_t row) const + { + return m[idx(col, row)]; + } + + // Set all elements to 0 + inline constexpr void clear() + { + std::fill(m.begin(), m.end(), T(0)); + } + + // Create identity matrix + inline constexpr void identity() + { + clear(); + auto& me = (*this); + me(0, 0) = 1; + me(1, 1) = 1; + me(2, 2) = 1; + } + + // Create translation matrix via components + template + inline constexpr void translate(const Q x, const Q y) + { + identity(); + auto& me = (*this); + me(2, 0) = T(x); + me(2, 1) = T(y); + } + + // Create translation matrix via vector + template + inline constexpr void translate(const olc::v_2d& v) + { + translate(v.x, v.y); + } + + // Create scaling matrix via components + template + inline constexpr void scale(const Q x, const Q y) + { + identity(); + auto& me = (*this); + me(0, 0) = T(x); + me(1, 1) = T(y); + } + + // Create scaling matrix via vector + template + inline constexpr void scale(const olc::v_2d& v) + { + scale(v.x, v.y); + } + + // Create rotation matrix with radians + template + inline constexpr void rotate(const Q rads) + { + identity(); + auto& me = (*this); + me(0, 0) = std::cos(rads); + me(0, 1) = std::sin(rads); + me(1, 0) = -me(0, 1); + me(1, 1) = me(0, 0); + } + + // Create shearing matrix via components + template + inline constexpr void shear(const Q x, const Q y) + { + identity(); + auto& me = (*this); + me(0, 1) = T(y); + me(1, 0) = T(x); + } + + // Create shearing matrix via vector + template + inline constexpr void shear(const olc::v_2d& v) + { + shear(v.x, v.y); + } + + + // Return inverted matrix + inline constexpr auto invert() const + { + // https://stackoverflow.com/a/18504573 + olc::m_3d out; + auto& me = (*this); + + T det = me(0, 0) * (me(1, 1) * me(2, 2) - me(2, 1) * me(1, 2)) - + me(0, 1) * (me(1, 0) * me(2, 2) - me(1, 2) * me(2, 0)) + + me(0, 2) * (me(1, 0) * me(2, 1) - me(1, 1) * me(2, 0)); + + T invdet = T(1) / det; + + out(0, 0) = (me(1, 1) * me(2, 2) - me(2, 1) * me(1, 2)) * invdet; + out(0, 1) = (me(0, 2) * me(2, 1) - me(0, 1) * me(2, 2)) * invdet; + out(0, 2) = (me(0, 1) * me(1, 2) - me(0, 2) * me(1, 1)) * invdet; + out(1, 0) = (me(1, 2) * me(2, 0) - me(1, 0) * me(2, 2)) * invdet; + out(1, 1) = (me(0, 0) * me(2, 2) - me(0, 2) * me(2, 0)) * invdet; + out(1, 2) = (me(1, 0) * me(0, 2) - me(0, 0) * me(1, 2)) * invdet; + out(2, 0) = (me(1, 0) * me(2, 1) - me(2, 0) * me(1, 1)) * invdet; + out(2, 1) = (me(2, 0) * me(0, 1) - me(0, 0) * me(2, 1)) * invdet; + out(2, 2) = (me(0, 0) * me(1, 1) - me(1, 0) * me(0, 1)) * invdet; + return out; + } + + // Transform a vector by this matrix + template + inline constexpr auto operator * (const olc::v_2d& v) const + { + auto& me = *this; + olc::v_2d vOut; + vOut.x = Q(me(0, 0) * v.x + me(1, 0) * v.y + me(2, 0) * Q(1)); + vOut.y = Q(me(0, 1) * v.x + me(1, 1) * v.y + me(2, 1) * Q(1)); + Q z = Q(me(0, 2) * v.x + me(1, 2) * v.y + me(2, 2) * Q(1)); + return (vOut / z); + } + + // Multiply this matrix with another + template + inline constexpr auto operator * (const olc::m_3d& rhs) const + { + auto& me = *this; + olc::m_3d out; + for (size_t c = 0; c < 3; c++) + for (size_t r = 0; r < 3; r++) + out(r, c) = me(r, 0) * rhs(0, c) + me(r, 1) * rhs(1, c) + me(r, 2) * rhs(2, c); + return out; + } + + // Return this matrix as a std::string, of the form "[c1r1, c2r1, c3r1]\n[c1r2, c2r2, c3r2]\n[c1r3, c2r3, c3r3]" + inline std::string str() const + { + const auto& me = *this; + return std::string("[") + std::to_string(me(0, 0)) + "," + std::to_string(me(1, 0)) + "," + std::to_string(me(2, 0)) + "]\n" + + "[" + std::to_string(me(0, 1)) + "," + std::to_string(me(1, 1)) + "," + std::to_string(me(2, 1)) + "]\n" + + "[" + std::to_string(me(0, 2)) + "," + std::to_string(me(1, 2)) + "," + std::to_string(me(2, 2)) + "]\n"; + } + }; + + // Allow olc::m_3d to play nicely with std::cout + template + inline std::ostream& operator << (std::ostream& os, const m_3d& rhs) + { + os << rhs.str(); + return os; + } + + // Convenient types ready-to-go + typedef m_3d mf3d; + typedef m_3d md3d; +} +#define PGE_MATRIX3D_DECLARED 1 +#endif +//! END DECLARATION \ No newline at end of file diff --git a/dev/src/sh_template.h b/dev/src/sh_template.h index db644512..614061f9 100644 --- a/dev/src/sh_template.h +++ b/dev/src/sh_template.h @@ -14,7 +14,11 @@ //! GRAB pixel.h DECLARATION //! GRAB vector2d.h DECLARATION -//! + +//! GRAB matrix3d.h DECLARATION + +//! GRAB transform2d.h DECLARATION + //! GRAB window.h DECLARATION //! GRAB core.h DECLARATION diff --git a/dev/src/transform2d.h b/dev/src/transform2d.h index e69de29b..90e4a82e 100644 --- a/dev/src/transform2d.h +++ b/dev/src/transform2d.h @@ -0,0 +1,215 @@ +#pragma once + +//! START STDHEADER GLOBAL +#include +#include +#include +#include +#include +#include +//! END STDHEADER + +//! START CUSTOMHEADER +#include "config.h" +#include "matrix3d.h" +//! END CUSTOMHEADER + +//! START DECLARATION +#if !defined(PGE_TRANSFORM2D_DECLARED) +namespace olc +{ + namespace internal + { + // Return sign of number as -1 or +1 + template + inline constexpr int sgn(T val) + { + return (T(0) < val) - (val < T(0)); + } + } + + + + /* + A complete 2D transformation structure + */ + template + class t_2d + { + static_assert(std::is_floating_point::value, "olc::t_2d must be floating point type"); + + public: + // Constructor + inline constexpr t_2d() = default; + + // Copy constructor + inline constexpr t_2d(const t_2d& t) = default; + + // Assignment operator + inline constexpr t_2d& operator=(const t_2d& t) = default; + + // Transform a vector by this transform + template + inline constexpr auto forward(const olc::v_2d& v) + { + return m_mForward * v; + } + + // Transform a vector of v_2d by this transform + template + inline constexpr auto forward(const std::vector>& v) + { + std::vector> o(v.size()); + std::transform(v.begin(), v.end(), o.begin(), [this](const olc::v_2d& i) {return m_mForward * i; }); + return o; + } + + // Transform a vector by the inverse of this transform + template + inline constexpr auto inverse(const olc::v_2d& v) + { + return m_mInverse * v; + } + + // Transform a vector by this transform + template + inline constexpr auto inverse(const std::vector>& v) + { + std::vector> o(v.size()); + std::transform(v.begin(), v.end(), o.begin(), [this](const olc::v_2d& i) {return m_mInverse * i; }); + return o; + } + + + // Set scaling component of this transformation + template + inline constexpr void scale(const olc::v_2d& v) + { + m_vScale = v; + m_mScale.scale(m_vScale); + update(); + } + + // Get scaling component of this transformation + inline constexpr const auto& scale() const + { + return m_vScale; + } + + // Set translation component of this transformation + template + inline constexpr void translate(const olc::v_2d& v) + { + m_vTranslate = v; + m_mTranslate.translate(m_vTranslate); + update(); + } + + // Get translation component of this transformation + inline constexpr const auto& translate() const + { + return m_vTranslate; + } + + // Set translation component of this transformation + template + inline constexpr void rotate(const Q& v, const olc::v_2d& p = { 0,0 }) + { + m_dTheta = T(v); + m_vRotatePoint = p; + m_3d matTrans1; + matTrans1.translate(-m_vRotatePoint); + m_3d matTrans2; + matTrans2.translate(m_vRotatePoint); + m_3d matRotate; + matRotate.rotate(m_dTheta); + m_mRotate = matTrans1 * matRotate * matTrans2; + update(); + } + + // Get translation component of this transformation + inline constexpr const auto& rotate() const + { + return m_dTheta; + } + + // Set shear component of this transformation + template + inline constexpr void shear(const olc::v_2d& v) + { + m_vShear = v; + m_mShear.shear(m_vShear); + update(); + } + + // Get shear component of this transformation + inline constexpr const auto& shear() const + { + return m_vShear; + } + + // Get forward transformation matrix + inline constexpr const auto& forward_matrix() const + { + return m_mForward; + } + + // Get inverse transformation matrix + inline constexpr const auto& inverse_matrix() const + { + return m_mInverse; + } + + // Construct transform from an existing transformation matrix + template + inline constexpr void from_matrix(const olc::m_3d& mat) + { + // https://math.stackexchange.com/a/13165 + m_vTranslate = + { + T(mat(2, 0)), + T(mat(2, 1)) + }; + + m_vScale = + { + T(olc::internal::sgn(mat(0, 0)) * std::hypot(mat(0, 0), mat(1, 0))), + T(olc::internal::sgn(mat(1, 1)) * std::hypot(mat(0, 1), mat(1, 1))) + }; + + m_dTheta = T(std::atan2(mat(0, 1), mat(1, 1))); + + m_mTranslate.translate(m_vTranslate); + m_mScale.scale(m_vScale); + m_mRotate.rotate(m_dTheta); + update(); + } + + protected: + // Constructs resultant matrices when transformation changes + inline constexpr void update() + { + m_mForward = m_mRotate * m_mShear * m_mScale * m_mTranslate; + m_mInverse = m_mForward.invert(); + } + + protected: + T m_dTheta = 0; + olc::v_2d m_vScale = { 1,1 }; + olc::v_2d m_vTranslate = { 0,0 }; + olc::v_2d m_vRotatePoint = { 0,0 }; + olc::v_2d m_vShear = { 0,0 }; + olc::m_3d m_mForward; + olc::m_3d m_mInverse; + olc::m_3d m_mScale; + olc::m_3d m_mTranslate; + olc::m_3d m_mRotate; + olc::m_3d m_mShear; + }; + + typedef t_2d tf2d; + typedef t_2d td2d; +} +#define PGE_TRANSFORM2D_DECLARED 1 +#endif +//! END DECLARATION \ No newline at end of file diff --git a/dev/tests/test_main.cpp b/dev/tests/test_main.cpp index 7d51993a..daec38ff 100644 --- a/dev/tests/test_main.cpp +++ b/dev/tests/test_main.cpp @@ -1,12 +1,13 @@ extern "C" void test_pixels(); extern "C" void test_vector2d(); - +extern "C" void test_matrices(); int main() { test_pixels(); test_vector2d(); + test_matrices(); return 0; } \ No newline at end of file diff --git a/dev/tests/test_matrices.cpp b/dev/tests/test_matrices.cpp new file mode 100644 index 00000000..7e393d4d --- /dev/null +++ b/dev/tests/test_matrices.cpp @@ -0,0 +1,24 @@ +#include +#include +#include + +#include "../src/config.h" +#include "../src/matrix3d.h" +#include "../src/transform2d.h" + +extern "C" void test_matrices() +{ + olc::mf3d mat1; + + mat1.identity(); + + std::cout << mat1.str() << std::endl; + + olc::tf2d trans1; + trans1.translate(olc::vf2d{ 2, 3 }); + trans1.rotate(0.6f); + + std::cout << trans1.forward_matrix() << std::endl; + + +} \ No newline at end of file From 6b41cd2a3f083f43af002f3d44c96e7fb6b50d10 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sat, 28 Jun 2025 21:52:12 +0100 Subject: [PATCH 08/11] started work on polyformer. This is where the rabbit hole begins lol... --- dev/msvc/olcPGE3.vcxproj | 3 ++ dev/msvc/olcPGE3.vcxproj.filters | 9 ++++ dev/src/draw2d.h | 58 ++++++++++++++++++++++ dev/src/gputask.h | 82 ++++++++++++++++++++++++++++++++ dev/src/polyform.h | 25 ++++++++++ 5 files changed, 177 insertions(+) create mode 100644 dev/src/draw2d.h create mode 100644 dev/src/gputask.h create mode 100644 dev/src/polyform.h diff --git a/dev/msvc/olcPGE3.vcxproj b/dev/msvc/olcPGE3.vcxproj index a1e43951..16b25c53 100644 --- a/dev/msvc/olcPGE3.vcxproj +++ b/dev/msvc/olcPGE3.vcxproj @@ -177,6 +177,8 @@ + + @@ -184,6 +186,7 @@ + diff --git a/dev/msvc/olcPGE3.vcxproj.filters b/dev/msvc/olcPGE3.vcxproj.filters index 21d9b9e5..18292e13 100644 --- a/dev/msvc/olcPGE3.vcxproj.filters +++ b/dev/msvc/olcPGE3.vcxproj.filters @@ -57,6 +57,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + diff --git a/dev/src/draw2d.h b/dev/src/draw2d.h new file mode 100644 index 00000000..4643fc0c --- /dev/null +++ b/dev/src/draw2d.h @@ -0,0 +1,58 @@ +#pragma once + +//! START STDHEADER +#include +#include +#include +#include +#include +#include +//! END STDHEADER + +//! START CUSTOMHEADER +#include "config.h" +#include "vector2d.h" +#include "pixel.h" +//! END CUSTOMHEADER + +//! START DECLARATION +#if !defined(PGE_IMAGE_DECLARED) +namespace olc +{ + class Draw2D + { + public: + Draw2D(); + + public: + // Plot a single pixel + template + void Draw(const olc::v_2d& pos, const olc::Pixel col = olc::Colour::WHITE) + { + /* + if(target image not in ram) + copy_gpu_to_cpu(target image) + + + + */ + } + + + // Draws a single pixel wide line + template + void DrawLine(const olc::v_2d& p1, const olc::v_2d& p2, const olc::Pixel col = olc::Colour::WHITE) + { + /* + if(target image not in ram) + copy_gpu_to_cpu(target image) + + + + */ + } + }; +} +#define PGE_DRAW2D_DECLARED +#endif +//! END DECLARATION \ No newline at end of file diff --git a/dev/src/gputask.h b/dev/src/gputask.h new file mode 100644 index 00000000..8c1626d5 --- /dev/null +++ b/dev/src/gputask.h @@ -0,0 +1,82 @@ +#pragma once + +//! START STDHEADER GLOBAL +#include +#include +#include +#include +//! END STDHEADER + +//! START CUSTOMHEADER +#include "config.h" +#include "pixel.h" +//! END CUSTOMHEADER + +//! START DECLARATION +#if !defined(PGE_GPUTASK_DECLARED) +namespace olc +{ + namespace pgeguts + { + // This is the default "packet" of work that is sent to + // a GPU for drawing. Various drawing operations throughout + // PGE create GPUTasks which are stored and dispatched when + // appropriate. It's contents are purposefully abstract + // to ensure rendering tools do not need to be reliant + // upon PGE structures + struct GPUTask + { + struct Vertex + { + float p[6]; // x, y, z, w, u, v + olc::Pixel c; // 32-bit colour + }; + + // Simple vertex buffer + std::vector vertexBuffer; + + // Model-View-Projection Matrix for shader + std::array mvpMatrix = { { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + } }; + + // Use depth components + bool bDepth = false; + + // Overall biasing colour (great for blends) + olc::Pixel tint = olc::Colour::WHITE; + + // Define super structure to be drawn + enum class Structure : uint8_t + { + // Vertex buffer is a series of points + Point = 0, + // Vertex buffer is a series of line segments + Line, + // Vertex buffer is a fan of triangles + Fan, + // Vertex buffer is a strip of adjacent triangles + Strip, + // Vertex buffer is a series of discrete triangles + List + } structure = Structure::Fan; + + // Define if GPU should face cull based on winding order + enum class CullMode : uint8_t + { + // No + None = 0, + // Cull if vertices are listed in clockwise order + ClockWise, + // Cull if vertices are listed in anticlockwise order + CounterClockWise + } cullmode = CullMode::None; + }; + } +} +#define PGE_GPUTASK_DECLARED 1 +#endif +//! END DECLARATION \ No newline at end of file diff --git a/dev/src/polyform.h b/dev/src/polyform.h new file mode 100644 index 00000000..611ad3a0 --- /dev/null +++ b/dev/src/polyform.h @@ -0,0 +1,25 @@ +#pragma once + +//! START STDHEADER GLOBAL +#include +#include +#include +#include +//! END STDHEADER + +//! START CUSTOMHEADER +#include "config.h" +//! END CUSTOMHEADER + +//! START DECLARATION +#if !defined(PGE_POLYFORM_DECLARED) +namespace olc +{ + namespace polyformer + { + + } +} +#define PGE_POLYFORM_DECLARED 1 +#endif +//! END DECLARATION \ No newline at end of file From 7efbe87d9a71ebf7883b09798c2dbabeffd0ada5 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 6 Jul 2025 22:19:36 +0100 Subject: [PATCH 09/11] another gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5c7e6759..a93da9a7 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ /dev/msvc/olcPGE3/x64/Debug /dev/tests/build /dev/msvc/olcPGE3_BuildSH/x64/Debug +/dev/msvc/olcPGE3/x64/Release From fb3482d7a0b60e91c2b25150e651009e2f31379c Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sun, 6 Jul 2025 22:20:41 +0100 Subject: [PATCH 10/11] Well there's a window on windows, not much else, but SH version is coming together, so really just a pausing point and code backup after a solid days effort. --- dev/msvc/gpu_opengl33.h | 17 + dev/msvc/olcPGE3.vcxproj | 18 +- dev/msvc/olcPGE3.vcxproj.filters | 30 + .../olcPGE3_BuildSH/olcPixelGameEngine3.h | 1343 ++++++++++++++++- dev/src/api_opengl.cpp | 52 + dev/src/api_opengl.h | 166 ++ dev/src/config.h | 49 + dev/src/core.cpp | 79 +- dev/src/core.h | 16 +- dev/src/draw2d.h | 2 +- dev/src/gpu_iface.h | 74 + dev/src/gpu_opengl33.cpp | 102 ++ dev/src/gpu_opengl33.h | 44 + dev/src/gputask.h | 40 + dev/src/host_iface.h | 72 + dev/src/host_win_winapi.cpp | 307 ++++ dev/src/host_win_winapi.h | 85 ++ dev/src/image.cpp | 52 + dev/src/image.h | 67 +- dev/src/olcpge3.h | 17 + dev/src/polyform.h | 13 + dev/src/sh_template.h | 52 + dev/src/window.cpp | 91 +- dev/src/window.h | 62 +- dev/tests/test_main.cpp | 7 + dev/tests/test_mh.cpp | 31 + 26 files changed, 2807 insertions(+), 81 deletions(-) create mode 100644 dev/msvc/gpu_opengl33.h create mode 100644 dev/src/api_opengl.cpp create mode 100644 dev/src/api_opengl.h create mode 100644 dev/src/gpu_iface.h create mode 100644 dev/src/gpu_opengl33.cpp create mode 100644 dev/src/gpu_opengl33.h create mode 100644 dev/src/host_iface.h create mode 100644 dev/src/host_win_winapi.cpp create mode 100644 dev/src/image.cpp create mode 100644 dev/src/olcpge3.h create mode 100644 dev/tests/test_mh.cpp diff --git a/dev/msvc/gpu_opengl33.h b/dev/msvc/gpu_opengl33.h new file mode 100644 index 00000000..8771d92c --- /dev/null +++ b/dev/msvc/gpu_opengl33.h @@ -0,0 +1,17 @@ +#pragma once + +#include "gpu_iface.h" +#include "api_opengl.h" + +namespace olc +{ + namespace gpu + { + class gpu_opengl33 : public gpu_iface + { + }; + } +} + + + diff --git a/dev/msvc/olcPGE3.vcxproj b/dev/msvc/olcPGE3.vcxproj index 16b25c53..2f4484c7 100644 --- a/dev/msvc/olcPGE3.vcxproj +++ b/dev/msvc/olcPGE3.vcxproj @@ -175,16 +175,21 @@ + + + + + @@ -193,15 +198,20 @@ + + + + - false - false - false - false + true + true + true + true + false false diff --git a/dev/msvc/olcPGE3.vcxproj.filters b/dev/msvc/olcPGE3.vcxproj.filters index 18292e13..21f7cc2b 100644 --- a/dev/msvc/olcPGE3.vcxproj.filters +++ b/dev/msvc/olcPGE3.vcxproj.filters @@ -66,6 +66,21 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -86,5 +101,20 @@ Source Files\tests + + Source Files + + + Source Files + + + Source Files + + + Source Files\tests + + + Source Files + \ No newline at end of file diff --git a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h index 84b1ad71..a5abe704 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h +++ b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h @@ -16,8 +16,11 @@ #include #include #include + +#include #include #include +#include // Version number (accessible as value for incremental comparisons) #define PGE_VER 300 @@ -42,6 +45,55 @@ #define PGE_SPELLING PGE_SPELL_CORRECTLY #endif + +// Choose "Operating System" +#define OLC_HOST_WINDOWS 1 +#define OLC_HOST_LINUX_X11 2 +#define OLC_HOST_LINUX_WAYLAND 3 +#define OLC_HOST_MACOS 4 +#define OLC_HOST_EMSCRIPTEN 5 +#define OLC_HOST_ANDROID 6 +#define OLC_HOST_IOS 7 + +#if !defined(OLC_HOST) + #if defined(_WIN32) + #define OLC_HOST OLC_HOST_WINDOWS + #endif + + #if defined(__linux__) || defined(__FreeBSD__) + // Note: Assumes X11 atm + #define OLC_HOST OLC_HOST_LINUX_X11 + #endif + + #if defined(__APPLE__) + #define OLC_HOST OLC_HOST_MACOS + #endif + + #if defined(__EMSCRIPTEN__) + #define OLC_HOST OLC_HOST_EMSCRIPTEN + #endif + + #if defined(__ANDROID__) + #define OLC_HOST OLC_HOST_ANDROID + #endif + + #if defined(__iOS__) + #define OLC_HOST OLC_HOST_IOS + #endif +#endif + + +// Choose "GPU Interface" +#define OLC_GPU_NONE 1 +#define OLC_GPU_OPENGL33 2 + +#if !defined(OLC_GPU) + #define OLC_GPU OLC_GPU_OPENGL33 +#endif + +#define LICENCE_DEFAULT "OneLoneCoder.com - Pixel Game Engine 3 - " + + #if !defined(PGE_PIXEL_DECLARED) namespace olc { @@ -1063,31 +1115,254 @@ namespace olc } #define PGE_TRANSFORM2D_DECLARED 1 #endif + +#if !defined(PGE_IMAGE_DECLARED) +namespace olc +{ + struct ImageConfig + { + bool Filtered = false; + bool Clamp = false; + }; + + class ImageCPU + { + public: + ImageCPU() = default; + ImageCPU(const olc::vi2d& size); + virtual ~ImageCPU(); + + public: + const olc::vi2d& Size() const; + olc::Pixel* Data(); + + public: + olc::vi2d dimensions; + std::vector pixels; + }; + + struct ImageGPU + { + olc::vf2d size; + olc::vf2d invsize; + int32_t resourceID = -1; + }; + + class Image + { + public: + // Constructs a general purpose image + Image(const olc::vi2d& size, const bool onCPU = true, const bool onGPU = true); + + public: + bool InCPU() const; + bool InGPU() const; + + + protected: + std::unique_ptr imCPU = nullptr; + std::unique_ptr imGPU = nullptr; + }; +} +#define PGE_IMAGE_DECLARED 1 +#endif #if !defined(PGE_WINDOW_DECLARED) + +#if OLC_HOST == OLC_HOST_WINDOWS + #define FRIENDLY_HOST Host_Windows_WinAPI +#endif + namespace olc { - class Window + namespace host { + class FRIENDLY_HOST; + class Host; + } + + class Window : public std::enable_shared_from_this + { + friend class olc::host::FRIENDLY_HOST; + public: Window(); virtual ~Window(); + void ConnecToHost(olc::host::Host* host); + public: virtual bool OnUserCreate(); virtual bool OnUserUpdate(float fElapsedTime); virtual bool OnUserDestroy(); + private: + // Set Mouse Device State + bool olc_OnMouseButton(const uint8_t nButton, const bool bPressed); + bool olc_OnMouseMove(const olc::vi2d& vMousePos); + bool olc_OnMouseWheel(const int32_t nScroll); + bool olc_OnMouseFocus(const bool bHasFocus); + + // Set Window State + bool olc_OnWindowPosition(const olc::vi2d& vWindowPos); + bool olc_OnWindowSize(const olc::vi2d& vWindowSize); + + // Set Keyboard State + + + public: bool olc_ShouldRemove() const; bool olc_WindowUpdate(const float fElapsedTime); + public: + const size_t GetUID() const; + + const olc::vi2d& GetSize() const; + bool SetSize(const olc::vi2d& vSize); + + const olc::vi2d& GetPosition() const; + bool SetPosition(const olc::vi2d& vPosition); + + const std::string& GetTitle() const; + bool SetTitle(const std::string& sTitle); + + + protected: + virtual bool olc_PrimaryWindowInit(); + protected: bool bShouldRemove = false; + + protected: + size_t nUniqueID = -1; + olc::vi2d vWindowPos; + olc::vi2d vWindowSize; + std::string sFrameTitle; + + private: + olc::vi2d volatile_vMousePos; + + olc::host::Host* pHost = nullptr; + //olc::gpu::Renderer* pRenderer = nullptr; }; } #define PGE_WINDOW_DECLARED 1 #endif + +#if !defined(PGE_RENDERER_IFACE_DECLARED) +namespace olc +{ + namespace gpu + { + struct RendererConfig + { + bool FullScreen = false; + bool VerticalSync = false; + }; + + enum class RendererError + { + None, + InvalidDCPixelFormat, + FailedToSetDCPixelFormat, + FailedToCreateRenderContext, + FailedToSwitchRenderContext, + }; + + class Renderer + { + public: + Renderer() = default; + virtual ~Renderer() {}; + + public: + // Check/Get last error + const RendererError GetLastError() const { return lastError; } + + public: // Device Stuff + // Constructs a GPU Device interface + virtual bool CreateDevice(std::vector params, const RendererConfig& cfg) = 0; + // Destroys a GPU device interface + virtual bool DestroyDevice() = 0; + + public: // Texture Resource Stuff + // Allocates a new texture resource in VRAM, returns handle + virtual uint32_t CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg = olc::ImageConfig()) = 0; + // Writes to / updates an existing texture resource in VRAM, using existing Image in SRAM + virtual bool WriteTexture(const uint32_t texid, olc::Image& image) = 0; + // Writes to / updates an existing Image in SRAM, from existing texture resource in VRAM + virtual bool ReadTexture(const uint32_t texid, olc::Image& image) = 0; + // Destroys and releases texture resource for given handle + virtual bool DeleteTexture(const uint32_t texid) = 0; + // Makes active the given texture resource (for subsequent sampling operations) + virtual bool AssignTextureSource(const uint32_t slot, const uint32_t texid) = 0; + // Makes active the given texture resource (for subsequent rendering operations) + virtual bool AssignTextureTarget(const uint32_t slot, const uint32_t texid) = 0; + + protected: + RendererError lastError = RendererError::None; + }; + } +} +#define PGE_RENDERER_IFACE_DECLARED 1 +#endif + +#if !defined(PGE_HOST_IFACE_DECLARED) +namespace olc +{ + namespace pgeguts + { + inline static size_t uuid = 0; + + inline constexpr size_t CreateUID() + { + return uuid++; + } + } + + namespace host + { + + + + struct HostConfig + { + + }; + + enum class HostError + { + None, + + }; + + class Host + { + public: + Host() = default; + virtual ~Host() {}; + + public: + // Check/Get last error + const HostError GetLastError() const { return lastError; } + + public: + virtual bool StartSystemEventLoop() = 0; + virtual bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen) = 0; + + virtual bool UpdateWindowFrameTitle(olc::Window* pWindow) = 0; + + + virtual bool ConnectHostResourceToRenderer() = 0; + + protected: + HostError lastError = HostError::None; + }; + } +} +#define PGE_HOST_IFACE_DECLARED 1 +#endif #if !defined(PGE_CORE_DECLARED) namespace olc @@ -1137,35 +1412,429 @@ namespace olc bool OnUserUpdate(float fElapsedTime) override; bool OnUserDestroy() override; + protected: + bool olc_PrimaryWindowInit() override; + private: void EngineThread(); private: - std::deque> deqChildWindows; + std::deque> deqChildWindows; // Frame Timing & Overall Clocking std::chrono::steady_clock::time_point timeFrame1; std::chrono::steady_clock::time_point timeFrame2; std::chrono::duration durationFrame{ 0 }; + std::chrono::duration durationFrameCount{ 0 }; + size_t frameCount = 0; PGEConfig config; + + std::thread coreThread; + std::atomic coreActive; + + std::unique_ptr gpu; + std::unique_ptr host; }; } #define PGE_CORE_DECLARED 1 #endif + + + + + +#if OLC_HOST == OLC_HOST_WINDOWS +#if defined(UNICODE) || defined(_UNICODE) + #define olcT(s) L##s +#else + #define olcT(s) s +#endif + +#define _WINSOCKAPI_ + +#if !defined(VC_EXTRALEAN) +#define VC_EXTRALEAN +#endif + +#if !defined(NOMINMAX) +#define NOMINMAX +#endif + +// In Code::Blocks +#if !defined(_WIN32_WINNT) + #ifdef HAVE_MSMF + #define _WIN32_WINNT 0x0600 // Windows Vista + #else + #define _WIN32_WINNT 0x0500 // Windows 2000 + #endif +#endif + +// Embrace MSVC superiority +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "gdi32.lib") + +#include +#undef _WINSOCKAPI_ + +namespace olc +{ + namespace host + { + + + + class Host_Windows_WinAPI : public olc::host::Host + { + + + public: + Host_Windows_WinAPI() = default; + virtual ~Host_Windows_WinAPI() {}; + + + public: + bool StartSystemEventLoop(); + bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen); + + bool UpdateWindowFrameTitle(olc::Window* pWindow); + + bool ConnectHostResourceToRenderer(); + + LRESULT OnWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + std::string test; + + private: + std::unordered_map mapUID2HWND; + std::unordered_map mapHWND2PTR; + + std::wstring ConvertS2W(std::string s); + + }; + } +} +#endif + +#if OLC_GPU == OLC_GPU_OPENGL33 +#define CALLSTYLE +#if OLC_HOST == OLC_HOST_WINDOWS + #include + #pragma comment(lib, "gdi32.lib") + #pragma comment(lib, "opengl32.lib") + #include + #define CALLSTYLE __stdcall + #define OGL_LOAD(t) (t##_t*)wglGetProcAddress(#t) +#endif + +#if OLC_HOST == OLC_HOST_LINUX_X11 || OLC_HOST == OLC_HOST_LINUX_WAYLAND + #include + #if OLC_HOST == OLC_HOST_LINUX_X11 + namespace X11 + { + #include + } + #endif +#endif + +#if OLC_HOST == OLC_HOST_MACOS + #define GL_SILENCE_DEPRECATION + #include + #include + #include +#endif + +#if OLC_HOST == OLC_HOST_EMSCRIPTEN + #include + #include + #define GL_GLEXT_PROTOTYPES + #include + #include + #define CALLSTYLE + #define GL_CLAMP GL_CLAMP_TO_EDGE +#endif + +namespace olc +{ + namespace apis::opengl + { +#if OLC_HOST == OLC_HOST_WINDOWS + // "Target Host Window Handle" + typedef HDC glDeviceContext_t; + // "State of OpenGL Machinary" + typedef HGLRC glRenderContext_t; +#endif + + typedef char GLchar; + typedef ptrdiff_t GLsizeiptr; + + typedef GLuint CALLSTYLE glCreateShader_t(GLenum type); + typedef GLuint CALLSTYLE glCreateProgram_t(void); + typedef void CALLSTYLE glDeleteShader_t(GLuint shader); + typedef void CALLSTYLE glCompileShader_t(GLuint shader); + typedef void CALLSTYLE glLinkProgram_t(GLuint program); + typedef void CALLSTYLE glDeleteProgram_t(GLuint program); + typedef void CALLSTYLE glAttachShader_t(GLuint program, GLuint shader); + typedef void CALLSTYLE glBindBuffer_t(GLenum target, GLuint buffer); + typedef void CALLSTYLE glBufferData_t(GLenum target, GLsizeiptr size, const void* data, GLenum usage); + typedef void CALLSTYLE glGenBuffers_t(GLsizei n, GLuint* buffers); + typedef void CALLSTYLE glVertexAttribPointer_t(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer); + typedef void CALLSTYLE glEnableVertexAttribArray_t(GLuint index); + typedef void CALLSTYLE glUseProgram_t(GLuint program); + typedef void CALLSTYLE glBindVertexArray_t(GLuint array); + typedef void CALLSTYLE glGenVertexArrays_t(GLsizei n, GLuint* arrays); + typedef void CALLSTYLE glGetShaderInfoLog_t(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog); + typedef GLint CALLSTYLE glGetUniformLocation_t(GLuint program, const GLchar* name); + typedef void CALLSTYLE glUniform1f_t(GLint location, GLfloat v0); + typedef void CALLSTYLE glUniform1i_t(GLint location, GLint v0); + typedef void CALLSTYLE glUniform2fv_t(GLint location, GLsizei count, const GLfloat* value); + typedef void CALLSTYLE glUniform4fv_t(GLint location, GLsizei count, const GLfloat* value); + typedef void CALLSTYLE glUniformMatrix4fv_t(GLint location, GLsizei count, GLboolean trasnpose, const GLfloat* value); + typedef void CALLSTYLE glActiveTexture_t(GLenum texture); + typedef void CALLSTYLE glGenFrameBuffers_t(GLsizei n, GLuint* ids); + typedef void CALLSTYLE glBindFrameBuffer_t(GLenum target, GLuint fb); + typedef GLenum CALLSTYLE glCheckFrameBufferStatus_t(GLenum target); + typedef void CALLSTYLE glDeleteFrameBuffers_t(GLsizei n, const GLuint* fbs); + typedef void CALLSTYLE glFrameBufferTexture2D_t(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void CALLSTYLE glDrawBuffers_t(GLsizei n, const GLenum* bufs); + typedef void CALLSTYLE glBlendFuncSeparate_t(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + + + +#if OLC_HOST == OLC_HOST_WINDOWS + typedef void CALLSTYLE glSwapInterval_t(GLsizei n); +#endif + + // A little GL class (singleton) + class gl + { + public: + static gl& Get(); + + private: + gl() = default; + static bool bLoaded; + + private: + bool LoadAll(); + + public: + glCreateShader_t* glCreateShader = nullptr; + glCreateProgram_t* glCreateProgram = nullptr; + glDeleteShader_t* glDeleteShader = nullptr; + glCompileShader_t* glCompileShader = nullptr; + glLinkProgram_t* glLinkProgram = nullptr; + glDeleteProgram_t* glDeleteProgram = nullptr; + glAttachShader_t* glAttachShader = nullptr; + glBindBuffer_t* glBindBuffer = nullptr; + glBufferData_t* glBufferData = nullptr; + glGenBuffers_t* glGenBuffers = nullptr; + glVertexAttribPointer_t* glVertexAttribPointer = nullptr; + glEnableVertexAttribArray_t* glEnableVertexAttribArray = nullptr; + glUseProgram_t* glUseProgram = nullptr; + glBindVertexArray_t* glBindVertexArray = nullptr; + glGenVertexArrays_t* glGenVertexArrays = nullptr; + glGetShaderInfoLog_t* glGetShaderInfoLog = nullptr; + glGetUniformLocation_t* glGetUniformLocation = nullptr; + glUniform1f_t* glUniform1f = nullptr; + glUniform1i_t* glUniform1i = nullptr; + glUniform2fv_t* glUniform2fv = nullptr; + glUniform4fv_t* glUniform4fv = nullptr; + glUniformMatrix4fv_t* glUniformMatrix4fv = nullptr; + glActiveTexture_t* glActiveTexture = nullptr; + glGenFrameBuffers_t* glGenFrameBuffers = nullptr; + glBindFrameBuffer_t* glBindFrameBuffer = nullptr; + glCheckFrameBufferStatus_t* glCheckFrameBufferStatus = nullptr; + glDeleteFrameBuffers_t* glDeleteFrameBuffers = nullptr; + glFrameBufferTexture2D_t* glFrameBufferTexture2D = nullptr; + glDrawBuffers_t* glDrawBuffers = nullptr; + glBlendFuncSeparate_t* glBlendFuncSeparate = nullptr; + }; + } + + + +//#if defined(OLC_PLATFORM_X11) +// typedef int(locSwapInterval_t)(X11::Display* dpy, X11::GLXDrawable drawable, int interval); +//#endif +// +//#if defined(OLC_PLATFORM_EMSCRIPTEN) +// typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length); +// typedef EGLBoolean(locSwapInterval_t)(EGLDisplay display, EGLint interval); +//#else +// typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar** string, const GLint* length); +//#endif + +} // olc namespace + +#if !defined(PGE_RENDERER_OPENGL33_DECLARED) +namespace olc +{ + namespace gpu + { + class Renderer_OGL33 : public olc::gpu::Renderer + { + public: // Device Stuff + // Constructs a GPU Device interface + bool CreateDevice(std::vector params, const RendererConfig& cfg) override; + // Destroys a GPU device interface + bool DestroyDevice() override; + + public: // Texture Resource Stuff + // Allocates a new texture resource in VRAM, returns handle + uint32_t CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg = olc::ImageConfig()) override; + // Writes to / updates an existing texture resource in VRAM, using existing Image in SRAM + bool WriteTexture(const uint32_t texid, olc::Image& image) override; + // Writes to / updates an existing Image in SRAM, from existing texture resource in VRAM + bool ReadTexture(const uint32_t texid, olc::Image& image) override; + // Destroys and releases texture resource for given handle + bool DeleteTexture(const uint32_t texid) override; + // Makes active the given texture resource (for subsequent sampling operations) + bool AssignTextureSource(const uint32_t slot, const uint32_t texid) override; + // Makes active the given texture resource (for subsequent rendering operations) + bool AssignTextureTarget(const uint32_t slot, const uint32_t texid) override; + + protected: // These may need some thinking about re multiple window + olc::apis::opengl::glDeviceContext_t glDeviceContext = 0; + olc::apis::opengl::glRenderContext_t glRenderContext = 0; + + }; + } +} +#define PGE_RENDERER_OPENGL33_DECLARED 1 +#endif +#endif + +#if !defined(PGE_GPUTASK_DECLARED) +namespace olc +{ + namespace pgeguts + { + // This is the default "packet" of work that is sent to + // a GPU for drawing. Various drawing operations throughout + // PGE create GPUTasks which are stored and dispatched when + // appropriate. It's contents are purposefully abstract + // to ensure rendering tools do not need to be reliant + // upon PGE structures + struct GPUTask + { + struct Vertex + { + float p[6]; // x, y, z, w, u, v + olc::Pixel c; // 32-bit colour + }; + + // Simple vertex buffer + std::vector vertexBuffer; + + // Model-View-Projection Matrix for shader + std::array mvpMatrix = { { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + } }; + + // Use depth components + bool bDepth = false; + + // Use hardware wire drawing + bool bWireframe = false; + + // Overall biasing colour (great for blends) + olc::Pixel tint = olc::Colour::WHITE; + + // Define super structure to be drawn + enum class Structure : uint8_t + { + // Vertex buffer is a series of points + Point = 0, + // Vertex buffer is a series of line segments + Line, + // Vertex buffer is a fan of triangles + Fan, + // Vertex buffer is a strip of adjacent triangles + Strip, + // Vertex buffer is a series of discrete triangles + List + } structure = Structure::Fan; + + // Define if GPU should face cull based on winding order + enum class CullMode : uint8_t + { + // No + None = 0, + // Cull if vertices are listed in clockwise order + ClockWise, + // Cull if vertices are listed in anticlockwise order + CounterClockWise + } cullmode = CullMode::None; + }; + + + + //// Construct a wireframe polygon + //GPUTask DrawPolygon2D( + // const GPUTask::Structure format, + // const olc::tf2d& transform, + // const std::vector& vPoints, + // const std::vector& vColours, + // const olc::Pixel tint = olc::Colour::WHITE) + //{ + // GPUTask task; + // task.structure = format; + // task.bDepth = false; + // task.tint = tint; + // task.cullmode = GPUTask::CullMode::None; + // task.bWireframe = true; + + // const auto& m = transform.forward_matrix().m; + // task.mvpMatrix = { { + // m[0], m[1], m[2], 0.0f, + // m[3], m[4], m[5], 0.0f, + // m[6], m[7], m[8], 0.0f, + // 0.0f, 0.0f, 0.0f, 1.0f + // } }; + + // // Pseudo-zip construct vertex buffer + // size_t nFullVerts = std::min({ vPoints.size(), vColours.size() }); + // task.vertexBuffer.resize(nFullVerts); + // for (size_t i = 0; i < nFullVerts; i++) + // { + // task.vertexBuffer[i] = { vPoints[i].x, vPoints[i].y, 0.0f, 1.0f, 0.0f, 0.0f, vColours[i] }; + // } + + // return task; + //} + } +} +#define PGE_GPUTASK_DECLARED 1 +#endif + + + + #if defined(OLC_PGE_APPLICATION) && !defined(PGE_WINDOW_IMPLEMENTED) namespace olc { - Window::Window() + Window::Window() : std::enable_shared_from_this() { + nUniqueID = pgeguts::CreateUID(); } Window::~Window() { } + void Window::ConnecToHost(olc::host::Host* host) + { + pHost = host; + sFrameTitle = "OneLoneCoder.com - Pixel Game Engine 3"; + pHost->UpdateWindowFrameTitle(this); + } + bool Window::OnUserCreate() { return false; @@ -1181,6 +1850,37 @@ namespace olc return false; } + bool Window::olc_OnMouseButton(const uint8_t nButton, const bool bPressed) + { + return false; + } + + bool Window::olc_OnMouseMove(const olc::vi2d& vMousePos) + { + volatile_vMousePos = vMousePos; + return true; + } + + bool Window::olc_OnMouseWheel(const int32_t nScroll) + { + return false; + } + + bool Window::olc_OnMouseFocus(const bool bHasFocus) + { + return false; + } + + bool Window::olc_OnWindowPosition(const olc::vi2d& vWindowPos) + { + return false; + } + + bool Window::olc_OnWindowSize(const olc::vi2d& vWindowSize) + { + return false; + } + bool Window::olc_ShouldRemove() const { return bShouldRemove; @@ -1203,14 +1903,521 @@ namespace olc return false; } + + const size_t Window::GetUID() const + { + return nUniqueID; + } + + const olc::vi2d& Window::GetSize() const + { + return vWindowSize; + } + + const olc::vi2d& Window::GetPosition() const + { + return vWindowPos; + } + + bool Window::SetSize(const olc::vi2d& vSize) + { + // Request host to change geometry of window object + + vWindowSize = vSize; + return true; + } + + bool Window::SetPosition(const olc::vi2d& vPosition) + { + // Request host to change geometry of window object + + vWindowPos = vPosition; + return true; + } + + const std::string& Window::GetTitle() const + { + return sFrameTitle; + } + + bool Window::SetTitle(const std::string& sTitle) + { + sFrameTitle = sTitle; + pHost->UpdateWindowFrameTitle(this); + return false; + } + + bool Window::olc_PrimaryWindowInit() + { + return false; + } }; #define PGE_WINDOW_IMPLEMENTED 1 #endif +#if defined(OLC_PGE_APPLICATION) && !defined(PGE_HOST_IMPLEMENTED) +#if OLC_HOST == OLC_HOST_WINDOWS +namespace olc::host +{ + // Forward Declaration + static LRESULT CALLBACK WINAPI_EventHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + bool Host_Windows_WinAPI::StartSystemEventLoop() + { + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return true; + } + + // Static linkage to lpfnWndProc - the hWnd is tagged with meta-info to get + // access to the actual host instance, which can more conveninetly process + // the event across multiple window instances + static LRESULT CALLBACK WINAPI_EventHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + // CreateWindowEx will fire a WM_CREATE event at the window, which we're + // not interested in, and this will occur before we've populated our + // linkage maps. We can detect for this condition here which means + // subsequent look-ups dont fail. NOTE: Do not assume WM_CREATE is the + // first message that is sent, its just one that we know is reliably sent. + if (uMsg == WM_CREATE) + { + // Associate the window's little blob of user memory with host iface + auto cfg = ((CREATESTRUCT*)lParam)->lpCreateParams; + SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)cfg); + } + else + { + // If this returns a value, then the host iface should be responsible + // for handling the message. + auto host = (Host_Windows_WinAPI*)GetWindowLongPtr(hWnd, GWLP_USERDATA); + if (host) + return host->OnWindowEvent(hWnd, uMsg, wParam, lParam); + } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } + + std::wstring Host_Windows_WinAPI::ConvertS2W(std::string s) + { +#ifdef __MINGW32__ + wchar_t* buffer = new wchar_t[s.length() + 1]; + mbstowcs(buffer, s.c_str(), s.length()); + buffer[s.length()] = L'\0'; +#else + int count = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); + wchar_t* buffer = new wchar_t[count]; + MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, buffer, count); +#endif + std::wstring w(buffer); + delete[] buffer; + return w; + } + + bool Host_Windows_WinAPI::AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen) + { + // The user created olc::Window object is the SSoT for what a window + // should look like, so get that sort of thing from there + olc::vi2d vWinPos = pWindow->GetPosition(); + olc::vi2d vWinSize = pWindow->GetSize(); + + // Define WindowClass + WNDCLASS wc = { 0 }; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpfnWndProc = WINAPI_EventHandler; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0;// sizeof(this); // For static meta-info + wc.lpszMenuName = nullptr; + wc.hbrBackground = nullptr; + wc.lpszClassName = olcT("OLC_PIXEL_GAME_ENGINE3"); + RegisterClass(&wc); + + // Define window furniture + DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD dwStyle = WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_THICKFRAME; + + olc::vi2d vTopLeft = vWindowPos; + + //// Handle Fullscreen + //if (bFullScreen) + //{ + // dwExStyle = 0; + // dwStyle = WS_VISIBLE | WS_POPUP; + // HMONITOR hmon = MonitorFromWindow(olc_hWnd, MONITOR_DEFAULTTONEAREST); + // MONITORINFO mi = { sizeof(mi) }; + // if (!GetMonitorInfo(hmon, &mi)) return olc::rcode::FAIL; + // vWindowSize = { mi.rcMonitor.right, mi.rcMonitor.bottom }; + // vTopLeft.x = 0; + // vTopLeft.y = 0; + //} + + // Keep client size as requested + RECT rWndRect = { 0, 0, vWindowSize.x, vWindowSize.y }; + AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle); + int width = rWndRect.right - rWndRect.left; + int height = rWndRect.bottom - rWndRect.top; + + // Create the actual OS window, return a handle + HWND hWnd = CreateWindowEx(dwExStyle, olcT("OLC_PIXEL_GAME_ENGINE3"), olcT(""), dwStyle, + vTopLeft.x, vTopLeft.y, width, height, NULL, NULL, GetModuleHandle(nullptr), this); + + // Now... awkwardly, the above has already fired off some window messages + // and they arent necessarily in a consistent order. Whereas one might + // assume WM_CREATE would be the first, there are some others on more + // modern systems. This is awkward because we havent yet associated the + // source window with a long_ptr to this class, and therefore we can't + // call the appropriate event handler. + + + // Store the link bewteen host resource and window + mapUID2HWND.insert_or_assign(pWindow->GetUID(), hWnd); + mapHWND2PTR.insert_or_assign(hWnd, pWindow); + + pWindow->ConnecToHost(this); + + + //DragAcceptFiles(olc_hWnd, true); + + return true; + } + + bool Host_Windows_WinAPI::UpdateWindowFrameTitle(olc::Window* pWindow) + { +#ifdef UNICODE + SetWindowText(mapUID2HWND.at(pWindow->GetUID()), ConvertS2W(pWindow->GetTitle()).c_str()); +#else + SetWindowText(mapUID2HWND.at(pWindow->GetUID()), sTitle.c_str()); +#endif + return true; + } + + bool Host_Windows_WinAPI::ConnectHostResourceToRenderer() + { + return false; + } + + LRESULT Host_Windows_WinAPI::OnWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + if (!mapHWND2PTR.contains(hWnd)) + return false; + + // Get target olc::Window + const auto& window = mapHWND2PTR.at(hWnd); + + switch (uMsg) + { + case WM_MOUSEMOVE: + { + uint16_t x = lParam & 0xFFFF; + uint16_t y = (lParam >> 16) & 0xFFFF; + int16_t ix = *(int16_t*)&x; + int16_t iy = *(int16_t*)&y; + window->olc_OnMouseMove(olc::vi2d{ ix, iy }); + return 0; + } + break; + + + // case WM_MOVE: vWinPos = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowPos(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; + // case WM_SIZE: vWinSize = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; + // case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0; + // case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0; + // case WM_SETFOCUS: ptrPGE->olc_UpdateKeyFocus(true); return 0; + // case WM_KILLFOCUS: ptrPGE->olc_UpdateKeyFocus(false); return 0; + // case WM_KEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; + // case WM_KEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; + // case WM_SYSKEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; + // case WM_SYSKEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; + // case WM_LBUTTONDOWN:ptrPGE->olc_UpdateMouseState(0, true); return 0; + // case WM_LBUTTONUP: ptrPGE->olc_UpdateMouseState(0, false); return 0; + // case WM_RBUTTONDOWN:ptrPGE->olc_UpdateMouseState(1, true); return 0; + // case WM_RBUTTONUP: ptrPGE->olc_UpdateMouseState(1, false); return 0; + // case WM_MBUTTONDOWN:ptrPGE->olc_UpdateMouseState(2, true); return 0; + // case WM_MBUTTONUP: ptrPGE->olc_UpdateMouseState(2, false); return 0; + // case WM_DROPFILES: + // { + // // This is all eww... + // HDROP drop = (HDROP)wParam; + // + // uint32_t nFiles = DragQueryFile(drop, 0xFFFFFFFF, nullptr, 0); + // std::vector vFiles; + // for (uint32_t i = 0; i < nFiles; i++) + // { + // TCHAR dfbuffer[256]{}; + // uint32_t len = DragQueryFile(drop, i, nullptr, 0); + // DragQueryFile(drop, i, dfbuffer, 256); + //#ifdef UNICODE + //#ifdef __MINGW32__ + // char* buffer = new char[len + 1]; + // wcstombs(buffer, dfbuffer, len); + // buffer[len] = '\0'; + //#else + // int count = WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, NULL, 0, NULL, NULL); + // char* buffer = new char[count]; + // WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, buffer, count, NULL, NULL); + //#endif + // vFiles.push_back(std::string(buffer)); + // delete[] buffer; + //#else + // vFiles.push_back(std::string(dfbuffer)); + //#endif + // } + // + // // Even more eww... + // POINT p; DragQueryPoint(drop, &p); + // ptrPGE->olc_DropFiles(p.x, p.y, vFiles); + // DragFinish(drop); + // return 0; + // } + // break; + // + // + // case WM_CLOSE: ptrPGE->olc_Terminate(); return 0; + case WM_DESTROY: + PostQuitMessage(0); + DestroyWindow(hWnd); + + } + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } + + // Lives in olc::host namespace + +// switch (uMsg) +// { +// case WM_MOUSEMOVE: +// { +// // Thanks @ForAbby (Discord) +// uint16_t x = lParam & 0xFFFF; uint16_t y = (lParam >> 16) & 0xFFFF; +// int16_t ix = *(int16_t*)&x; int16_t iy = *(int16_t*)&y; +// ptrPGE->olc_UpdateMouse(ix, iy); +// return 0; +// } +// case WM_MOVE: vWinPos = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowPos(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; +// case WM_SIZE: vWinSize = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; +// case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0; +// case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0; +// case WM_SETFOCUS: ptrPGE->olc_UpdateKeyFocus(true); return 0; +// case WM_KILLFOCUS: ptrPGE->olc_UpdateKeyFocus(false); return 0; +// case WM_KEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; +// case WM_KEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; +// case WM_SYSKEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; +// case WM_SYSKEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; +// case WM_LBUTTONDOWN:ptrPGE->olc_UpdateMouseState(0, true); return 0; +// case WM_LBUTTONUP: ptrPGE->olc_UpdateMouseState(0, false); return 0; +// case WM_RBUTTONDOWN:ptrPGE->olc_UpdateMouseState(1, true); return 0; +// case WM_RBUTTONUP: ptrPGE->olc_UpdateMouseState(1, false); return 0; +// case WM_MBUTTONDOWN:ptrPGE->olc_UpdateMouseState(2, true); return 0; +// case WM_MBUTTONUP: ptrPGE->olc_UpdateMouseState(2, false); return 0; +// case WM_DROPFILES: +// { +// // This is all eww... +// HDROP drop = (HDROP)wParam; +// +// uint32_t nFiles = DragQueryFile(drop, 0xFFFFFFFF, nullptr, 0); +// std::vector vFiles; +// for (uint32_t i = 0; i < nFiles; i++) +// { +// TCHAR dfbuffer[256]{}; +// uint32_t len = DragQueryFile(drop, i, nullptr, 0); +// DragQueryFile(drop, i, dfbuffer, 256); +//#ifdef UNICODE +//#ifdef __MINGW32__ +// char* buffer = new char[len + 1]; +// wcstombs(buffer, dfbuffer, len); +// buffer[len] = '\0'; +//#else +// int count = WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, NULL, 0, NULL, NULL); +// char* buffer = new char[count]; +// WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, buffer, count, NULL, NULL); +//#endif +// vFiles.push_back(std::string(buffer)); +// delete[] buffer; +//#else +// vFiles.push_back(std::string(dfbuffer)); +//#endif +// } +// +// // Even more eww... +// POINT p; DragQueryPoint(drop, &p); +// ptrPGE->olc_DropFiles(p.x, p.y, vFiles); +// DragFinish(drop); +// return 0; +// } +// break; +// +// +// case WM_CLOSE: ptrPGE->olc_Terminate(); return 0; +// case WM_DESTROY: PostQuitMessage(0); DestroyWindow(hWnd); return 0; +// } + +}; +#endif +#define PGE_HOST_IMPLEMENTED 1 +#endif + +#if defined(OLC_PGE_APPLICATION) && !defined(PGE_GPU_IMPLEMENTED) +#if OLC_GPU == OLC_GPU_OPENGL33 +namespace olc::apis::opengl +{ + bool gl::bLoaded = false; + + gl& gl::Get() + { + static olc::apis::opengl::gl me; + if (!me.bLoaded) me.LoadAll(); + return me; + } + + bool gl::LoadAll() + { + bLoaded = true; + bLoaded &= (glCreateShader = OGL_LOAD(glCreateShader)) != nullptr; + bLoaded &= (glCreateProgram = OGL_LOAD(glCreateProgram)) != nullptr; + bLoaded &= (glDeleteShader = OGL_LOAD(glDeleteShader)) != nullptr; + bLoaded &= (glCompileShader = OGL_LOAD(glCompileShader)) != nullptr; + bLoaded &= (glLinkProgram = OGL_LOAD(glLinkProgram)) != nullptr; + bLoaded &= (glDeleteProgram = OGL_LOAD(glDeleteProgram)) != nullptr; + bLoaded &= (glAttachShader = OGL_LOAD(glAttachShader)) != nullptr; + bLoaded &= (glBindBuffer = OGL_LOAD(glBindBuffer)) != nullptr; + bLoaded &= (glBufferData = OGL_LOAD(glBufferData)) != nullptr; + bLoaded &= (glGenBuffers = OGL_LOAD(glGenBuffers)) != nullptr; + bLoaded &= (glVertexAttribPointer = OGL_LOAD(glVertexAttribPointer)) != nullptr; + bLoaded &= (glEnableVertexAttribArray = OGL_LOAD(glEnableVertexAttribArray)) != nullptr; + bLoaded &= (glUseProgram = OGL_LOAD(glUseProgram)) != nullptr; + bLoaded &= (glBindVertexArray = OGL_LOAD(glBindVertexArray)) != nullptr; + bLoaded &= (glGenVertexArrays = OGL_LOAD(glGenVertexArrays)) != nullptr; + bLoaded &= (glGetShaderInfoLog = OGL_LOAD(glGetShaderInfoLog)) != nullptr; + bLoaded &= (glGetUniformLocation = OGL_LOAD(glGetUniformLocation)) != nullptr; + bLoaded &= (glUniform1f = OGL_LOAD(glUniform1f)) != nullptr; + bLoaded &= (glUniform1i = OGL_LOAD(glUniform1i)) != nullptr; + bLoaded &= (glUniform2fv = OGL_LOAD(glUniform2fv)) != nullptr; + bLoaded &= (glUniform4fv = OGL_LOAD(glUniform4fv)) != nullptr; + bLoaded &= (glUniformMatrix4fv = OGL_LOAD(glUniformMatrix4fv)) != nullptr; + bLoaded &= (glActiveTexture = OGL_LOAD(glActiveTexture)) != nullptr; + bLoaded &= (glGenFrameBuffers = OGL_LOAD(glGenFrameBuffers)) != nullptr; + bLoaded &= (glBindFrameBuffer = OGL_LOAD(glBindFrameBuffer)) != nullptr; + bLoaded &= (glCheckFrameBufferStatus = OGL_LOAD(glCheckFrameBufferStatus)) != nullptr; + bLoaded &= (glDeleteFrameBuffers = OGL_LOAD(glDeleteFrameBuffers)) != nullptr; + bLoaded &= (glFrameBufferTexture2D = OGL_LOAD(glFrameBufferTexture2D)) != nullptr; + bLoaded &= (glDrawBuffers = OGL_LOAD(glDrawBuffers)) != nullptr; + bLoaded &= (glBlendFuncSeparate = OGL_LOAD(glBlendFuncSeparate)) != nullptr; + return bLoaded; + } +} +namespace olc::gpu +{ + bool Renderer_OGL33::CreateDevice(std::vector params, const RendererConfig& cfg) + { + // Create OpenGL Device Context +#if OLC_HOST == OLC_HOST_WINDOWS + glDeviceContext = GetDC((HWND)(params[0])); + + PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + PFD_MAIN_PLANE, 0, 0, 0, 0 + }; + + int pf = 0; + if (!(pf = ChoosePixelFormat(glDeviceContext, &pfd))) + { + lastError = RendererError::InvalidDCPixelFormat; + return false; + } + + if (!SetPixelFormat(glDeviceContext, pf, &pfd)) + { + lastError = RendererError::FailedToSetDCPixelFormat; + return false; + } + + if (!(glRenderContext = wglCreateContext(glDeviceContext))) + { + lastError = RendererError::FailedToCreateRenderContext; + return false; + } + + if (!wglMakeCurrent(glDeviceContext, glRenderContext)) + { + lastError = RendererError::FailedToSwitchRenderContext; + return false; + } + + //// Set Vertical Sync + //glSwapInterval = OGL_LOAD(glSwapInterval); + //if (locSwapInterval && !bVSYNC) locSwapInterval(0); + //bSync = bVSYNC; + +#endif + + // Can't load OpenGL API until context is loaded + auto& gl = olc::apis::opengl::gl::Get(); + + + lastError = RendererError::None; + return true; + } + + bool Renderer_OGL33::DestroyDevice() + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + uint32_t Renderer_OGL33::CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg) + { + auto& gl = olc::apis::opengl::gl::Get(); + return 0; + } + + bool Renderer_OGL33::WriteTexture(const uint32_t texid, olc::Image& image) + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + bool Renderer_OGL33::ReadTexture(const uint32_t texid, olc::Image& image) + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + bool Renderer_OGL33::DeleteTexture(const uint32_t texid) + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + bool Renderer_OGL33::AssignTextureSource(const uint32_t slot, const uint32_t texid) + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + bool Renderer_OGL33::AssignTextureTarget(const uint32_t slot, const uint32_t texid) + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } +} +#endif +#define PGE_GPU_IMPLEMENTED 1 +#endif + #if defined(OLC_PGE_APPLICATION) && !defined(PGE_CORE_IMPLEMENTED) namespace olc { - PixelGameEngine::PixelGameEngine() + PixelGameEngine::PixelGameEngine() : Window() { } @@ -1224,7 +2431,7 @@ namespace olc config.vPixelSize = vPixelSize; config.bFullScreen = bFullScreen; - return false; + return true; } bool PixelGameEngine::Construct(const PGEConfig& cfg) @@ -1236,7 +2443,25 @@ namespace olc bool PixelGameEngine::Start() { - return false; + // Initialise Host Interface + host = std::make_unique(); + + + // Link this olc::Window to a host resource + if (host) + host->AddWindowFrame(this, { 30,30 }, { 250, 240 }, false); + + coreActive = true; + coreThread = std::thread(&PixelGameEngine::EngineThread, this); + + // This will block depending upon the system + host->StartSystemEventLoop(); + + // Gracefully terminate the main engine thread + coreActive = false; + coreThread.join(); + + return true; } bool PixelGameEngine::OnUserCreate() @@ -1254,8 +2479,30 @@ namespace olc return false; } + bool PixelGameEngine::olc_PrimaryWindowInit() + { + // Called by window to initialise core systems. Sub windows should ignore this + return false; + } + void PixelGameEngine::EngineThread() { + using namespace std::chrono_literals; + timeFrame2 = std::chrono::steady_clock::now(); + timeFrame1 = std::chrono::steady_clock::now(); + + + // Initialise GPU Interface - This thread is the context + olc::gpu::RendererConfig cfgRenderer; + gpu = std::make_unique(); + gpu->CreateDevice({ nullptr }, cfgRenderer); + + + // Initialise Input Devices + + + durationFrameCount = 0s; + while (true) { // Frame Delta Timing - "ElapsedTime" since last core update @@ -1272,12 +2519,23 @@ namespace olc // and resource management. That is not olc::PGE. timeFrame1 = std::chrono::steady_clock::now(); - durationFrame = timeFrame2 - timeFrame1; - timeFrame1 = timeFrame2; + durationFrame = timeFrame1 - timeFrame2; + timeFrame2 = timeFrame1; // Our time per frame coefficient float fDT = durationFrame.count(); + frameCount++; + durationFrameCount += durationFrame; + + if (durationFrameCount >= 1s) + { + durationFrameCount -= 1s; + std::string sTitle = "OneLoneCoder.com - Pixel Game Engine 3 - Test - FPS: " + std::to_string(frameCount); + SetTitle(sTitle); + frameCount = 0; + } + // Primary Window olc_WindowUpdate(fDT); @@ -1286,14 +2544,71 @@ namespace olc winChild->olc_WindowUpdate(fDT); // Remove child windows that have requested closure - deqChildWindows.erase(std::remove_if(deqChildWindows.begin(), deqChildWindows.end(), - [](const std::unique_ptr& w) - { - return w->olc_ShouldRemove(); - } - )); + if (!deqChildWindows.empty()) + { + deqChildWindows.erase(std::remove_if(deqChildWindows.begin(), deqChildWindows.end(), + [](const std::shared_ptr& w) + { + return w->olc_ShouldRemove(); + } + )); + } } } } #define PGE_CORE_IMPLEMENTED 1 #endif + +#if defined(OLC_PGE_APPLICATION) && !defined(PGE_IMAGE_IMPLEMENTED) +namespace olc +{ + ImageCPU::ImageCPU(const olc::vi2d& size) : dimensions(size) + { + pixels.resize(dimensions.area(), olc::Pixel(255, 165, 0)); + } + + ImageCPU::~ImageCPU() + { + pixels.clear(); + } + + const olc::vi2d& ImageCPU::Size() const + { + return dimensions; + } + + olc::Pixel* ImageCPU::Data() + { + return pixels.data(); + } + + + Image::Image(const olc::vi2d& size, const bool onCPU, const bool onGPU) + { + // Always create CPU resident image + imCPU = std::make_unique(size); + + if (onGPU) + { + + } + + if (!onCPU) + imCPU.reset(); + } + + bool Image::InCPU() const + { + return imCPU != nullptr; + } + + bool Image::InGPU() const + { + return imGPU != nullptr; + } + +}; +#define PGE_IMAGE_IMPLEMENTED 1 +#endif + + diff --git a/dev/src/api_opengl.cpp b/dev/src/api_opengl.cpp new file mode 100644 index 00000000..49e5258d --- /dev/null +++ b/dev/src/api_opengl.cpp @@ -0,0 +1,52 @@ +#include "api_opengl.h" + + +//! START IMPLEMENTATION +namespace olc::apis::opengl +{ + bool gl::bLoaded = false; + + gl& gl::Get() + { + static olc::apis::opengl::gl me; + if (!me.bLoaded) me.LoadAll(); + return me; + } + + bool gl::LoadAll() + { + bLoaded = true; + bLoaded &= (glCreateShader = OGL_LOAD(glCreateShader)) != nullptr; + bLoaded &= (glCreateProgram = OGL_LOAD(glCreateProgram)) != nullptr; + bLoaded &= (glDeleteShader = OGL_LOAD(glDeleteShader)) != nullptr; + bLoaded &= (glCompileShader = OGL_LOAD(glCompileShader)) != nullptr; + bLoaded &= (glLinkProgram = OGL_LOAD(glLinkProgram)) != nullptr; + bLoaded &= (glDeleteProgram = OGL_LOAD(glDeleteProgram)) != nullptr; + bLoaded &= (glAttachShader = OGL_LOAD(glAttachShader)) != nullptr; + bLoaded &= (glBindBuffer = OGL_LOAD(glBindBuffer)) != nullptr; + bLoaded &= (glBufferData = OGL_LOAD(glBufferData)) != nullptr; + bLoaded &= (glGenBuffers = OGL_LOAD(glGenBuffers)) != nullptr; + bLoaded &= (glVertexAttribPointer = OGL_LOAD(glVertexAttribPointer)) != nullptr; + bLoaded &= (glEnableVertexAttribArray = OGL_LOAD(glEnableVertexAttribArray)) != nullptr; + bLoaded &= (glUseProgram = OGL_LOAD(glUseProgram)) != nullptr; + bLoaded &= (glBindVertexArray = OGL_LOAD(glBindVertexArray)) != nullptr; + bLoaded &= (glGenVertexArrays = OGL_LOAD(glGenVertexArrays)) != nullptr; + bLoaded &= (glGetShaderInfoLog = OGL_LOAD(glGetShaderInfoLog)) != nullptr; + bLoaded &= (glGetUniformLocation = OGL_LOAD(glGetUniformLocation)) != nullptr; + bLoaded &= (glUniform1f = OGL_LOAD(glUniform1f)) != nullptr; + bLoaded &= (glUniform1i = OGL_LOAD(glUniform1i)) != nullptr; + bLoaded &= (glUniform2fv = OGL_LOAD(glUniform2fv)) != nullptr; + bLoaded &= (glUniform4fv = OGL_LOAD(glUniform4fv)) != nullptr; + bLoaded &= (glUniformMatrix4fv = OGL_LOAD(glUniformMatrix4fv)) != nullptr; + bLoaded &= (glActiveTexture = OGL_LOAD(glActiveTexture)) != nullptr; + bLoaded &= (glGenFrameBuffers = OGL_LOAD(glGenFrameBuffers)) != nullptr; + bLoaded &= (glBindFrameBuffer = OGL_LOAD(glBindFrameBuffer)) != nullptr; + bLoaded &= (glCheckFrameBufferStatus = OGL_LOAD(glCheckFrameBufferStatus)) != nullptr; + bLoaded &= (glDeleteFrameBuffers = OGL_LOAD(glDeleteFrameBuffers)) != nullptr; + bLoaded &= (glFrameBufferTexture2D = OGL_LOAD(glFrameBufferTexture2D)) != nullptr; + bLoaded &= (glDrawBuffers = OGL_LOAD(glDrawBuffers)) != nullptr; + bLoaded &= (glBlendFuncSeparate = OGL_LOAD(glBlendFuncSeparate)) != nullptr; + return bLoaded; + } +} +//! END IMPLEMENTATION \ No newline at end of file diff --git a/dev/src/api_opengl.h b/dev/src/api_opengl.h new file mode 100644 index 00000000..d7b4fd7b --- /dev/null +++ b/dev/src/api_opengl.h @@ -0,0 +1,166 @@ +#pragma once + + +//! START CUSTOMHEADER +#include "config.h" +//! END CUSTOMHEADER + + +//! START OPENGL_CONFIG +#define CALLSTYLE +#if OLC_HOST == OLC_HOST_WINDOWS + #include + #pragma comment(lib, "gdi32.lib") + #pragma comment(lib, "opengl32.lib") + #include + #define CALLSTYLE __stdcall + #define OGL_LOAD(t) (t##_t*)wglGetProcAddress(#t) +#endif + +#if OLC_HOST == OLC_HOST_LINUX_X11 || OLC_HOST == OLC_HOST_LINUX_WAYLAND + #include + #if OLC_HOST == OLC_HOST_LINUX_X11 + namespace X11 + { + #include + } + #endif +#endif + +#if OLC_HOST == OLC_HOST_MACOS + #define GL_SILENCE_DEPRECATION + #include + #include + #include +#endif + +#if OLC_HOST == OLC_HOST_EMSCRIPTEN + #include + #include + #define GL_GLEXT_PROTOTYPES + #include + #include + #define CALLSTYLE + #define GL_CLAMP GL_CLAMP_TO_EDGE +#endif +//! END OPENGL_CONFIG + +//! START DECLARATION +namespace olc +{ + namespace apis::opengl + { +#if OLC_HOST == OLC_HOST_WINDOWS + // "Target Host Window Handle" + typedef HDC glDeviceContext_t; + // "State of OpenGL Machinary" + typedef HGLRC glRenderContext_t; +#endif + + typedef char GLchar; + typedef ptrdiff_t GLsizeiptr; + + typedef GLuint CALLSTYLE glCreateShader_t(GLenum type); + typedef GLuint CALLSTYLE glCreateProgram_t(void); + typedef void CALLSTYLE glDeleteShader_t(GLuint shader); + typedef void CALLSTYLE glCompileShader_t(GLuint shader); + typedef void CALLSTYLE glLinkProgram_t(GLuint program); + typedef void CALLSTYLE glDeleteProgram_t(GLuint program); + typedef void CALLSTYLE glAttachShader_t(GLuint program, GLuint shader); + typedef void CALLSTYLE glBindBuffer_t(GLenum target, GLuint buffer); + typedef void CALLSTYLE glBufferData_t(GLenum target, GLsizeiptr size, const void* data, GLenum usage); + typedef void CALLSTYLE glGenBuffers_t(GLsizei n, GLuint* buffers); + typedef void CALLSTYLE glVertexAttribPointer_t(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer); + typedef void CALLSTYLE glEnableVertexAttribArray_t(GLuint index); + typedef void CALLSTYLE glUseProgram_t(GLuint program); + typedef void CALLSTYLE glBindVertexArray_t(GLuint array); + typedef void CALLSTYLE glGenVertexArrays_t(GLsizei n, GLuint* arrays); + typedef void CALLSTYLE glGetShaderInfoLog_t(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog); + typedef GLint CALLSTYLE glGetUniformLocation_t(GLuint program, const GLchar* name); + typedef void CALLSTYLE glUniform1f_t(GLint location, GLfloat v0); + typedef void CALLSTYLE glUniform1i_t(GLint location, GLint v0); + typedef void CALLSTYLE glUniform2fv_t(GLint location, GLsizei count, const GLfloat* value); + typedef void CALLSTYLE glUniform4fv_t(GLint location, GLsizei count, const GLfloat* value); + typedef void CALLSTYLE glUniformMatrix4fv_t(GLint location, GLsizei count, GLboolean trasnpose, const GLfloat* value); + typedef void CALLSTYLE glActiveTexture_t(GLenum texture); + typedef void CALLSTYLE glGenFrameBuffers_t(GLsizei n, GLuint* ids); + typedef void CALLSTYLE glBindFrameBuffer_t(GLenum target, GLuint fb); + typedef GLenum CALLSTYLE glCheckFrameBufferStatus_t(GLenum target); + typedef void CALLSTYLE glDeleteFrameBuffers_t(GLsizei n, const GLuint* fbs); + typedef void CALLSTYLE glFrameBufferTexture2D_t(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + typedef void CALLSTYLE glDrawBuffers_t(GLsizei n, const GLenum* bufs); + typedef void CALLSTYLE glBlendFuncSeparate_t(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); + + + +#if OLC_HOST == OLC_HOST_WINDOWS + typedef void CALLSTYLE glSwapInterval_t(GLsizei n); +#endif + + // A little GL class (singleton) + class gl + { + public: + static gl& Get(); + + private: + gl() = default; + static bool bLoaded; + + private: + bool LoadAll(); + + public: + glCreateShader_t* glCreateShader = nullptr; + glCreateProgram_t* glCreateProgram = nullptr; + glDeleteShader_t* glDeleteShader = nullptr; + glCompileShader_t* glCompileShader = nullptr; + glLinkProgram_t* glLinkProgram = nullptr; + glDeleteProgram_t* glDeleteProgram = nullptr; + glAttachShader_t* glAttachShader = nullptr; + glBindBuffer_t* glBindBuffer = nullptr; + glBufferData_t* glBufferData = nullptr; + glGenBuffers_t* glGenBuffers = nullptr; + glVertexAttribPointer_t* glVertexAttribPointer = nullptr; + glEnableVertexAttribArray_t* glEnableVertexAttribArray = nullptr; + glUseProgram_t* glUseProgram = nullptr; + glBindVertexArray_t* glBindVertexArray = nullptr; + glGenVertexArrays_t* glGenVertexArrays = nullptr; + glGetShaderInfoLog_t* glGetShaderInfoLog = nullptr; + glGetUniformLocation_t* glGetUniformLocation = nullptr; + glUniform1f_t* glUniform1f = nullptr; + glUniform1i_t* glUniform1i = nullptr; + glUniform2fv_t* glUniform2fv = nullptr; + glUniform4fv_t* glUniform4fv = nullptr; + glUniformMatrix4fv_t* glUniformMatrix4fv = nullptr; + glActiveTexture_t* glActiveTexture = nullptr; + glGenFrameBuffers_t* glGenFrameBuffers = nullptr; + glBindFrameBuffer_t* glBindFrameBuffer = nullptr; + glCheckFrameBufferStatus_t* glCheckFrameBufferStatus = nullptr; + glDeleteFrameBuffers_t* glDeleteFrameBuffers = nullptr; + glFrameBufferTexture2D_t* glFrameBufferTexture2D = nullptr; + glDrawBuffers_t* glDrawBuffers = nullptr; + glBlendFuncSeparate_t* glBlendFuncSeparate = nullptr; + }; + } + + + +//#if defined(OLC_PLATFORM_X11) +// typedef int(locSwapInterval_t)(X11::Display* dpy, X11::GLXDrawable drawable, int interval); +//#endif +// +//#if defined(OLC_PLATFORM_EMSCRIPTEN) +// typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length); +// typedef EGLBoolean(locSwapInterval_t)(EGLDisplay display, EGLint interval); +//#else +// typedef void CALLSTYLE locShaderSource_t(GLuint shader, GLsizei count, const GLchar** string, const GLint* length); +//#endif + +} // olc namespace +//! END DECLARATION + + + + + diff --git a/dev/src/config.h b/dev/src/config.h index 5bc24669..bac80e0f 100644 --- a/dev/src/config.h +++ b/dev/src/config.h @@ -23,4 +23,53 @@ #if !defined(PGE_SPELLING) #define PGE_SPELLING PGE_SPELL_CORRECTLY #endif + + +// Choose "Operating System" +#define OLC_HOST_WINDOWS 1 +#define OLC_HOST_LINUX_X11 2 +#define OLC_HOST_LINUX_WAYLAND 3 +#define OLC_HOST_MACOS 4 +#define OLC_HOST_EMSCRIPTEN 5 +#define OLC_HOST_ANDROID 6 +#define OLC_HOST_IOS 7 + +#if !defined(OLC_HOST) + #if defined(_WIN32) + #define OLC_HOST OLC_HOST_WINDOWS + #endif + + #if defined(__linux__) || defined(__FreeBSD__) + // Note: Assumes X11 atm + #define OLC_HOST OLC_HOST_LINUX_X11 + #endif + + #if defined(__APPLE__) + #define OLC_HOST OLC_HOST_MACOS + #endif + + #if defined(__EMSCRIPTEN__) + #define OLC_HOST OLC_HOST_EMSCRIPTEN + #endif + + #if defined(__ANDROID__) + #define OLC_HOST OLC_HOST_ANDROID + #endif + + #if defined(__iOS__) + #define OLC_HOST OLC_HOST_IOS + #endif +#endif + + +// Choose "GPU Interface" +#define OLC_GPU_NONE 1 +#define OLC_GPU_OPENGL33 2 + +#if !defined(OLC_GPU) + #define OLC_GPU OLC_GPU_OPENGL33 +#endif + +#define LICENCE_DEFAULT "OneLoneCoder.com - Pixel Game Engine 3 - " + //! END CONFIGURATION \ No newline at end of file diff --git a/dev/src/core.cpp b/dev/src/core.cpp index 214fd726..563b52a4 100644 --- a/dev/src/core.cpp +++ b/dev/src/core.cpp @@ -1,9 +1,12 @@ #include "core.h" +#include "gpu_opengl33.h" +#include "host_win_winapi.h" + //! START IMPLEMENTATION namespace olc { - PixelGameEngine::PixelGameEngine() + PixelGameEngine::PixelGameEngine() : Window() { } @@ -17,7 +20,7 @@ namespace olc config.vPixelSize = vPixelSize; config.bFullScreen = bFullScreen; - return false; + return true; } bool PixelGameEngine::Construct(const PGEConfig& cfg) @@ -29,7 +32,25 @@ namespace olc bool PixelGameEngine::Start() { - return false; + // Initialise Host Interface + host = std::make_unique(); + + + // Link this olc::Window to a host resource + if (host) + host->AddWindowFrame(this, { 30,30 }, { 250, 240 }, false); + + coreActive = true; + coreThread = std::thread(&PixelGameEngine::EngineThread, this); + + // This will block depending upon the system + host->StartSystemEventLoop(); + + // Gracefully terminate the main engine thread + coreActive = false; + coreThread.join(); + + return true; } bool PixelGameEngine::OnUserCreate() @@ -47,8 +68,30 @@ namespace olc return false; } + bool PixelGameEngine::olc_PrimaryWindowInit() + { + // Called by window to initialise core systems. Sub windows should ignore this + return false; + } + void PixelGameEngine::EngineThread() { + using namespace std::chrono_literals; + timeFrame2 = std::chrono::steady_clock::now(); + timeFrame1 = std::chrono::steady_clock::now(); + + + // Initialise GPU Interface - This thread is the context + olc::gpu::RendererConfig cfgRenderer; + gpu = std::make_unique(); + gpu->CreateDevice({ nullptr }, cfgRenderer); + + + // Initialise Input Devices + + + durationFrameCount = 0s; + while (true) { // Frame Delta Timing - "ElapsedTime" since last core update @@ -65,12 +108,23 @@ namespace olc // and resource management. That is not olc::PGE. timeFrame1 = std::chrono::steady_clock::now(); - durationFrame = timeFrame2 - timeFrame1; - timeFrame1 = timeFrame2; + durationFrame = timeFrame1 - timeFrame2; + timeFrame2 = timeFrame1; // Our time per frame coefficient float fDT = durationFrame.count(); + frameCount++; + durationFrameCount += durationFrame; + + if (durationFrameCount >= 1s) + { + durationFrameCount -= 1s; + std::string sTitle = "OneLoneCoder.com - Pixel Game Engine 3 - Test - FPS: " + std::to_string(frameCount); + SetTitle(sTitle); + frameCount = 0; + } + // Primary Window olc_WindowUpdate(fDT); @@ -79,12 +133,15 @@ namespace olc winChild->olc_WindowUpdate(fDT); // Remove child windows that have requested closure - deqChildWindows.erase(std::remove_if(deqChildWindows.begin(), deqChildWindows.end(), - [](const std::unique_ptr& w) - { - return w->olc_ShouldRemove(); - } - )); + if (!deqChildWindows.empty()) + { + deqChildWindows.erase(std::remove_if(deqChildWindows.begin(), deqChildWindows.end(), + [](const std::shared_ptr& w) + { + return w->olc_ShouldRemove(); + } + )); + } } } } diff --git a/dev/src/core.h b/dev/src/core.h index d9f766b1..d74d0733 100644 --- a/dev/src/core.h +++ b/dev/src/core.h @@ -9,6 +9,7 @@ #include #include #include +#include //! END STDHEADER GLOBAL //! START CUSTOMHEADER GLOBAL @@ -16,6 +17,8 @@ #include "pixel.h" #include "vector2d.h" #include "window.h" +#include "gpu_iface.h" +#include "host_iface.h" //! END CUSTOMHEADER GLOBAL //! START DECLARATION @@ -67,18 +70,29 @@ namespace olc bool OnUserUpdate(float fElapsedTime) override; bool OnUserDestroy() override; + protected: + bool olc_PrimaryWindowInit() override; + private: void EngineThread(); private: - std::deque> deqChildWindows; + std::deque> deqChildWindows; // Frame Timing & Overall Clocking std::chrono::steady_clock::time_point timeFrame1; std::chrono::steady_clock::time_point timeFrame2; std::chrono::duration durationFrame{ 0 }; + std::chrono::duration durationFrameCount{ 0 }; + size_t frameCount = 0; PGEConfig config; + + std::thread coreThread; + std::atomic coreActive; + + std::unique_ptr gpu; + std::unique_ptr host; }; } #define PGE_CORE_DECLARED 1 diff --git a/dev/src/draw2d.h b/dev/src/draw2d.h index 4643fc0c..666e06eb 100644 --- a/dev/src/draw2d.h +++ b/dev/src/draw2d.h @@ -16,7 +16,7 @@ //! END CUSTOMHEADER //! START DECLARATION -#if !defined(PGE_IMAGE_DECLARED) +#if !defined(PGE_DRAW2D_DECLARED) namespace olc { class Draw2D diff --git a/dev/src/gpu_iface.h b/dev/src/gpu_iface.h new file mode 100644 index 00000000..023a2b77 --- /dev/null +++ b/dev/src/gpu_iface.h @@ -0,0 +1,74 @@ +#pragma once + +//! START STDHEADER GLOBAL +#include +#include +//! END STDHEADER + +//! START CUSTOMHEADER +#include "config.h" +#include "pixel.h" +#include "vector2d.h" +#include "gputask.h" +#include "image.h" +//! END CUSTOMHEADER + +//! START DECLARATION +#if !defined(PGE_RENDERER_IFACE_DECLARED) +namespace olc +{ + namespace gpu + { + struct RendererConfig + { + bool FullScreen = false; + bool VerticalSync = false; + }; + + enum class RendererError + { + None, + InvalidDCPixelFormat, + FailedToSetDCPixelFormat, + FailedToCreateRenderContext, + FailedToSwitchRenderContext, + }; + + class Renderer + { + public: + Renderer() = default; + virtual ~Renderer() {}; + + public: + // Check/Get last error + const RendererError GetLastError() const { return lastError; } + + public: // Device Stuff + // Constructs a GPU Device interface + virtual bool CreateDevice(std::vector params, const RendererConfig& cfg) = 0; + // Destroys a GPU device interface + virtual bool DestroyDevice() = 0; + + public: // Texture Resource Stuff + // Allocates a new texture resource in VRAM, returns handle + virtual uint32_t CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg = olc::ImageConfig()) = 0; + // Writes to / updates an existing texture resource in VRAM, using existing Image in SRAM + virtual bool WriteTexture(const uint32_t texid, olc::Image& image) = 0; + // Writes to / updates an existing Image in SRAM, from existing texture resource in VRAM + virtual bool ReadTexture(const uint32_t texid, olc::Image& image) = 0; + // Destroys and releases texture resource for given handle + virtual bool DeleteTexture(const uint32_t texid) = 0; + // Makes active the given texture resource (for subsequent sampling operations) + virtual bool AssignTextureSource(const uint32_t slot, const uint32_t texid) = 0; + // Makes active the given texture resource (for subsequent rendering operations) + virtual bool AssignTextureTarget(const uint32_t slot, const uint32_t texid) = 0; + + protected: + RendererError lastError = RendererError::None; + }; + } +} +#define PGE_RENDERER_IFACE_DECLARED 1 +#endif +//! END DECLARATION \ No newline at end of file diff --git a/dev/src/gpu_opengl33.cpp b/dev/src/gpu_opengl33.cpp new file mode 100644 index 00000000..f609c0b4 --- /dev/null +++ b/dev/src/gpu_opengl33.cpp @@ -0,0 +1,102 @@ +#include "gpu_opengl33.h" + +//! START IMPLEMENTATION +namespace olc::gpu +{ + bool Renderer_OGL33::CreateDevice(std::vector params, const RendererConfig& cfg) + { + // Create OpenGL Device Context +#if OLC_HOST == OLC_HOST_WINDOWS + glDeviceContext = GetDC((HWND)(params[0])); + + PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + PFD_MAIN_PLANE, 0, 0, 0, 0 + }; + + int pf = 0; + if (!(pf = ChoosePixelFormat(glDeviceContext, &pfd))) + { + lastError = RendererError::InvalidDCPixelFormat; + return false; + } + + if (!SetPixelFormat(glDeviceContext, pf, &pfd)) + { + lastError = RendererError::FailedToSetDCPixelFormat; + return false; + } + + if (!(glRenderContext = wglCreateContext(glDeviceContext))) + { + lastError = RendererError::FailedToCreateRenderContext; + return false; + } + + if (!wglMakeCurrent(glDeviceContext, glRenderContext)) + { + lastError = RendererError::FailedToSwitchRenderContext; + return false; + } + + //// Set Vertical Sync + //glSwapInterval = OGL_LOAD(glSwapInterval); + //if (locSwapInterval && !bVSYNC) locSwapInterval(0); + //bSync = bVSYNC; + +#endif + + // Can't load OpenGL API until context is loaded + auto& gl = olc::apis::opengl::gl::Get(); + + + lastError = RendererError::None; + return true; + } + + bool Renderer_OGL33::DestroyDevice() + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + uint32_t Renderer_OGL33::CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg) + { + auto& gl = olc::apis::opengl::gl::Get(); + return 0; + } + + bool Renderer_OGL33::WriteTexture(const uint32_t texid, olc::Image& image) + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + bool Renderer_OGL33::ReadTexture(const uint32_t texid, olc::Image& image) + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + bool Renderer_OGL33::DeleteTexture(const uint32_t texid) + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + bool Renderer_OGL33::AssignTextureSource(const uint32_t slot, const uint32_t texid) + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + bool Renderer_OGL33::AssignTextureTarget(const uint32_t slot, const uint32_t texid) + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } +} +//! END IMPLEMENTATION \ No newline at end of file diff --git a/dev/src/gpu_opengl33.h b/dev/src/gpu_opengl33.h new file mode 100644 index 00000000..a23c201d --- /dev/null +++ b/dev/src/gpu_opengl33.h @@ -0,0 +1,44 @@ +#pragma once + +#include "gpu_iface.h" +#include "api_opengl.h" + +//! START DECLARATION +#if !defined(PGE_RENDERER_OPENGL33_DECLARED) +namespace olc +{ + namespace gpu + { + class Renderer_OGL33 : public olc::gpu::Renderer + { + public: // Device Stuff + // Constructs a GPU Device interface + bool CreateDevice(std::vector params, const RendererConfig& cfg) override; + // Destroys a GPU device interface + bool DestroyDevice() override; + + public: // Texture Resource Stuff + // Allocates a new texture resource in VRAM, returns handle + uint32_t CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg = olc::ImageConfig()) override; + // Writes to / updates an existing texture resource in VRAM, using existing Image in SRAM + bool WriteTexture(const uint32_t texid, olc::Image& image) override; + // Writes to / updates an existing Image in SRAM, from existing texture resource in VRAM + bool ReadTexture(const uint32_t texid, olc::Image& image) override; + // Destroys and releases texture resource for given handle + bool DeleteTexture(const uint32_t texid) override; + // Makes active the given texture resource (for subsequent sampling operations) + bool AssignTextureSource(const uint32_t slot, const uint32_t texid) override; + // Makes active the given texture resource (for subsequent rendering operations) + bool AssignTextureTarget(const uint32_t slot, const uint32_t texid) override; + + protected: // These may need some thinking about re multiple window + olc::apis::opengl::glDeviceContext_t glDeviceContext = 0; + olc::apis::opengl::glRenderContext_t glRenderContext = 0; + + }; + } +} +#define PGE_RENDERER_OPENGL33_DECLARED 1 +#endif +//! END DECLARATION + diff --git a/dev/src/gputask.h b/dev/src/gputask.h index 8c1626d5..2f30ab7b 100644 --- a/dev/src/gputask.h +++ b/dev/src/gputask.h @@ -10,6 +10,7 @@ //! START CUSTOMHEADER #include "config.h" #include "pixel.h" +#include "transform2d.h" //! END CUSTOMHEADER //! START DECLARATION @@ -46,6 +47,9 @@ namespace olc // Use depth components bool bDepth = false; + // Use hardware wire drawing + bool bWireframe = false; + // Overall biasing colour (great for blends) olc::Pixel tint = olc::Colour::WHITE; @@ -75,6 +79,42 @@ namespace olc CounterClockWise } cullmode = CullMode::None; }; + + + + //// Construct a wireframe polygon + //GPUTask DrawPolygon2D( + // const GPUTask::Structure format, + // const olc::tf2d& transform, + // const std::vector& vPoints, + // const std::vector& vColours, + // const olc::Pixel tint = olc::Colour::WHITE) + //{ + // GPUTask task; + // task.structure = format; + // task.bDepth = false; + // task.tint = tint; + // task.cullmode = GPUTask::CullMode::None; + // task.bWireframe = true; + + // const auto& m = transform.forward_matrix().m; + // task.mvpMatrix = { { + // m[0], m[1], m[2], 0.0f, + // m[3], m[4], m[5], 0.0f, + // m[6], m[7], m[8], 0.0f, + // 0.0f, 0.0f, 0.0f, 1.0f + // } }; + + // // Pseudo-zip construct vertex buffer + // size_t nFullVerts = std::min({ vPoints.size(), vColours.size() }); + // task.vertexBuffer.resize(nFullVerts); + // for (size_t i = 0; i < nFullVerts; i++) + // { + // task.vertexBuffer[i] = { vPoints[i].x, vPoints[i].y, 0.0f, 1.0f, 0.0f, 0.0f, vColours[i] }; + // } + + // return task; + //} } } #define PGE_GPUTASK_DECLARED 1 diff --git a/dev/src/host_iface.h b/dev/src/host_iface.h new file mode 100644 index 00000000..72232f33 --- /dev/null +++ b/dev/src/host_iface.h @@ -0,0 +1,72 @@ +#pragma once + +//! START STDHEADER GLOBAL +#include +#include +#include +//! END STDHEADER + +//! START CUSTOMHEADER +#include "config.h" +#include "pixel.h" +#include "vector2d.h" +#include "window.h" +//! END CUSTOMHEADER + +//! START DECLARATION +#if !defined(PGE_HOST_IFACE_DECLARED) +namespace olc +{ + namespace pgeguts + { + inline static size_t uuid = 0; + + inline constexpr size_t CreateUID() + { + return uuid++; + } + } + + namespace host + { + + + + struct HostConfig + { + + }; + + enum class HostError + { + None, + + }; + + class Host + { + public: + Host() = default; + virtual ~Host() {}; + + public: + // Check/Get last error + const HostError GetLastError() const { return lastError; } + + public: + virtual bool StartSystemEventLoop() = 0; + virtual bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen) = 0; + + virtual bool UpdateWindowFrameTitle(olc::Window* pWindow) = 0; + + + virtual bool ConnectHostResourceToRenderer() = 0; + + protected: + HostError lastError = HostError::None; + }; + } +} +#define PGE_HOST_IFACE_DECLARED 1 +#endif +//! END DECLARATION \ No newline at end of file diff --git a/dev/src/host_win_winapi.cpp b/dev/src/host_win_winapi.cpp new file mode 100644 index 00000000..971a0561 --- /dev/null +++ b/dev/src/host_win_winapi.cpp @@ -0,0 +1,307 @@ +#include "host_win_winapi.h" + +//! START IMPLEMENTATION +namespace olc::host +{ + // Forward Declaration + static LRESULT CALLBACK WINAPI_EventHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + bool Host_Windows_WinAPI::StartSystemEventLoop() + { + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return true; + } + + // Static linkage to lpfnWndProc - the hWnd is tagged with meta-info to get + // access to the actual host instance, which can more conveninetly process + // the event across multiple window instances + static LRESULT CALLBACK WINAPI_EventHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + // CreateWindowEx will fire a WM_CREATE event at the window, which we're + // not interested in, and this will occur before we've populated our + // linkage maps. We can detect for this condition here which means + // subsequent look-ups dont fail. NOTE: Do not assume WM_CREATE is the + // first message that is sent, its just one that we know is reliably sent. + if (uMsg == WM_CREATE) + { + // Associate the window's little blob of user memory with host iface + auto cfg = ((CREATESTRUCT*)lParam)->lpCreateParams; + SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)cfg); + } + else + { + // If this returns a value, then the host iface should be responsible + // for handling the message. + auto host = (Host_Windows_WinAPI*)GetWindowLongPtr(hWnd, GWLP_USERDATA); + if (host) + return host->OnWindowEvent(hWnd, uMsg, wParam, lParam); + } + + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } + + std::wstring Host_Windows_WinAPI::ConvertS2W(std::string s) + { +#ifdef __MINGW32__ + wchar_t* buffer = new wchar_t[s.length() + 1]; + mbstowcs(buffer, s.c_str(), s.length()); + buffer[s.length()] = L'\0'; +#else + int count = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); + wchar_t* buffer = new wchar_t[count]; + MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, buffer, count); +#endif + std::wstring w(buffer); + delete[] buffer; + return w; + } + + bool Host_Windows_WinAPI::AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen) + { + // The user created olc::Window object is the SSoT for what a window + // should look like, so get that sort of thing from there + olc::vi2d vWinPos = pWindow->GetPosition(); + olc::vi2d vWinSize = pWindow->GetSize(); + + // Define WindowClass + WNDCLASS wc = { 0 }; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpfnWndProc = WINAPI_EventHandler; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0;// sizeof(this); // For static meta-info + wc.lpszMenuName = nullptr; + wc.hbrBackground = nullptr; + wc.lpszClassName = olcT("OLC_PIXEL_GAME_ENGINE3"); + RegisterClass(&wc); + + // Define window furniture + DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD dwStyle = WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_THICKFRAME; + + olc::vi2d vTopLeft = vWindowPos; + + //// Handle Fullscreen + //if (bFullScreen) + //{ + // dwExStyle = 0; + // dwStyle = WS_VISIBLE | WS_POPUP; + // HMONITOR hmon = MonitorFromWindow(olc_hWnd, MONITOR_DEFAULTTONEAREST); + // MONITORINFO mi = { sizeof(mi) }; + // if (!GetMonitorInfo(hmon, &mi)) return olc::rcode::FAIL; + // vWindowSize = { mi.rcMonitor.right, mi.rcMonitor.bottom }; + // vTopLeft.x = 0; + // vTopLeft.y = 0; + //} + + // Keep client size as requested + RECT rWndRect = { 0, 0, vWindowSize.x, vWindowSize.y }; + AdjustWindowRectEx(&rWndRect, dwStyle, FALSE, dwExStyle); + int width = rWndRect.right - rWndRect.left; + int height = rWndRect.bottom - rWndRect.top; + + // Create the actual OS window, return a handle + HWND hWnd = CreateWindowEx(dwExStyle, olcT("OLC_PIXEL_GAME_ENGINE3"), olcT(""), dwStyle, + vTopLeft.x, vTopLeft.y, width, height, NULL, NULL, GetModuleHandle(nullptr), this); + + // Now... awkwardly, the above has already fired off some window messages + // and they arent necessarily in a consistent order. Whereas one might + // assume WM_CREATE would be the first, there are some others on more + // modern systems. This is awkward because we havent yet associated the + // source window with a long_ptr to this class, and therefore we can't + // call the appropriate event handler. + + + // Store the link bewteen host resource and window + mapUID2HWND.insert_or_assign(pWindow->GetUID(), hWnd); + mapHWND2PTR.insert_or_assign(hWnd, pWindow); + + pWindow->ConnecToHost(this); + + + //DragAcceptFiles(olc_hWnd, true); + + return true; + } + + bool Host_Windows_WinAPI::UpdateWindowFrameTitle(olc::Window* pWindow) + { +#ifdef UNICODE + SetWindowText(mapUID2HWND.at(pWindow->GetUID()), ConvertS2W(pWindow->GetTitle()).c_str()); +#else + SetWindowText(mapUID2HWND.at(pWindow->GetUID()), sTitle.c_str()); +#endif + return true; + } + + bool Host_Windows_WinAPI::ConnectHostResourceToRenderer() + { + return false; + } + + LRESULT Host_Windows_WinAPI::OnWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + if (!mapHWND2PTR.contains(hWnd)) + return false; + + // Get target olc::Window + const auto& window = mapHWND2PTR.at(hWnd); + + switch (uMsg) + { + case WM_MOUSEMOVE: + { + uint16_t x = lParam & 0xFFFF; + uint16_t y = (lParam >> 16) & 0xFFFF; + int16_t ix = *(int16_t*)&x; + int16_t iy = *(int16_t*)&y; + window->olc_OnMouseMove(olc::vi2d{ ix, iy }); + return 0; + } + break; + + + // case WM_MOVE: vWinPos = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowPos(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; + // case WM_SIZE: vWinSize = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; + // case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0; + // case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0; + // case WM_SETFOCUS: ptrPGE->olc_UpdateKeyFocus(true); return 0; + // case WM_KILLFOCUS: ptrPGE->olc_UpdateKeyFocus(false); return 0; + // case WM_KEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; + // case WM_KEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; + // case WM_SYSKEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; + // case WM_SYSKEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; + // case WM_LBUTTONDOWN:ptrPGE->olc_UpdateMouseState(0, true); return 0; + // case WM_LBUTTONUP: ptrPGE->olc_UpdateMouseState(0, false); return 0; + // case WM_RBUTTONDOWN:ptrPGE->olc_UpdateMouseState(1, true); return 0; + // case WM_RBUTTONUP: ptrPGE->olc_UpdateMouseState(1, false); return 0; + // case WM_MBUTTONDOWN:ptrPGE->olc_UpdateMouseState(2, true); return 0; + // case WM_MBUTTONUP: ptrPGE->olc_UpdateMouseState(2, false); return 0; + // case WM_DROPFILES: + // { + // // This is all eww... + // HDROP drop = (HDROP)wParam; + // + // uint32_t nFiles = DragQueryFile(drop, 0xFFFFFFFF, nullptr, 0); + // std::vector vFiles; + // for (uint32_t i = 0; i < nFiles; i++) + // { + // TCHAR dfbuffer[256]{}; + // uint32_t len = DragQueryFile(drop, i, nullptr, 0); + // DragQueryFile(drop, i, dfbuffer, 256); + //#ifdef UNICODE + //#ifdef __MINGW32__ + // char* buffer = new char[len + 1]; + // wcstombs(buffer, dfbuffer, len); + // buffer[len] = '\0'; + //#else + // int count = WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, NULL, 0, NULL, NULL); + // char* buffer = new char[count]; + // WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, buffer, count, NULL, NULL); + //#endif + // vFiles.push_back(std::string(buffer)); + // delete[] buffer; + //#else + // vFiles.push_back(std::string(dfbuffer)); + //#endif + // } + // + // // Even more eww... + // POINT p; DragQueryPoint(drop, &p); + // ptrPGE->olc_DropFiles(p.x, p.y, vFiles); + // DragFinish(drop); + // return 0; + // } + // break; + // + // + // case WM_CLOSE: ptrPGE->olc_Terminate(); return 0; + case WM_DESTROY: + PostQuitMessage(0); + DestroyWindow(hWnd); + + } + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } + + // Lives in olc::host namespace + +// switch (uMsg) +// { +// case WM_MOUSEMOVE: +// { +// // Thanks @ForAbby (Discord) +// uint16_t x = lParam & 0xFFFF; uint16_t y = (lParam >> 16) & 0xFFFF; +// int16_t ix = *(int16_t*)&x; int16_t iy = *(int16_t*)&y; +// ptrPGE->olc_UpdateMouse(ix, iy); +// return 0; +// } +// case WM_MOVE: vWinPos = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowPos(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; +// case WM_SIZE: vWinSize = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; +// case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0; +// case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0; +// case WM_SETFOCUS: ptrPGE->olc_UpdateKeyFocus(true); return 0; +// case WM_KILLFOCUS: ptrPGE->olc_UpdateKeyFocus(false); return 0; +// case WM_KEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; +// case WM_KEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; +// case WM_SYSKEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; +// case WM_SYSKEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; +// case WM_LBUTTONDOWN:ptrPGE->olc_UpdateMouseState(0, true); return 0; +// case WM_LBUTTONUP: ptrPGE->olc_UpdateMouseState(0, false); return 0; +// case WM_RBUTTONDOWN:ptrPGE->olc_UpdateMouseState(1, true); return 0; +// case WM_RBUTTONUP: ptrPGE->olc_UpdateMouseState(1, false); return 0; +// case WM_MBUTTONDOWN:ptrPGE->olc_UpdateMouseState(2, true); return 0; +// case WM_MBUTTONUP: ptrPGE->olc_UpdateMouseState(2, false); return 0; +// case WM_DROPFILES: +// { +// // This is all eww... +// HDROP drop = (HDROP)wParam; +// +// uint32_t nFiles = DragQueryFile(drop, 0xFFFFFFFF, nullptr, 0); +// std::vector vFiles; +// for (uint32_t i = 0; i < nFiles; i++) +// { +// TCHAR dfbuffer[256]{}; +// uint32_t len = DragQueryFile(drop, i, nullptr, 0); +// DragQueryFile(drop, i, dfbuffer, 256); +//#ifdef UNICODE +//#ifdef __MINGW32__ +// char* buffer = new char[len + 1]; +// wcstombs(buffer, dfbuffer, len); +// buffer[len] = '\0'; +//#else +// int count = WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, NULL, 0, NULL, NULL); +// char* buffer = new char[count]; +// WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, buffer, count, NULL, NULL); +//#endif +// vFiles.push_back(std::string(buffer)); +// delete[] buffer; +//#else +// vFiles.push_back(std::string(dfbuffer)); +//#endif +// } +// +// // Even more eww... +// POINT p; DragQueryPoint(drop, &p); +// ptrPGE->olc_DropFiles(p.x, p.y, vFiles); +// DragFinish(drop); +// return 0; +// } +// break; +// +// +// case WM_CLOSE: ptrPGE->olc_Terminate(); return 0; +// case WM_DESTROY: PostQuitMessage(0); DestroyWindow(hWnd); return 0; +// } + +}; +//! END IMPLEMENTATION + diff --git a/dev/src/host_win_winapi.h b/dev/src/host_win_winapi.h index e69de29b..ab48ebde 100644 --- a/dev/src/host_win_winapi.h +++ b/dev/src/host_win_winapi.h @@ -0,0 +1,85 @@ +#pragma once + +//! START STDHEADER GLOBAL +#include +#include +#include +//! END STDHEADER + +//! START CUSTOMHEADER +#include "host_iface.h" +//! END CUSTOMHEADER + +//! START WINAPI_CONFIG +#if defined(UNICODE) || defined(_UNICODE) + #define olcT(s) L##s +#else + #define olcT(s) s +#endif + +#define _WINSOCKAPI_ + +#if !defined(VC_EXTRALEAN) +#define VC_EXTRALEAN +#endif + +#if !defined(NOMINMAX) +#define NOMINMAX +#endif + +// In Code::Blocks +#if !defined(_WIN32_WINNT) + #ifdef HAVE_MSMF + #define _WIN32_WINNT 0x0600 // Windows Vista + #else + #define _WIN32_WINNT 0x0500 // Windows 2000 + #endif +#endif + +// Embrace MSVC superiority +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "gdi32.lib") + +#include +#undef _WINSOCKAPI_ +//! END WINAPI_CONFIG + +//! START DECLARATION +namespace olc +{ + namespace host + { + + + + class Host_Windows_WinAPI : public olc::host::Host + { + + + public: + Host_Windows_WinAPI() = default; + virtual ~Host_Windows_WinAPI() {}; + + + public: + bool StartSystemEventLoop(); + bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen); + + bool UpdateWindowFrameTitle(olc::Window* pWindow); + + bool ConnectHostResourceToRenderer(); + + LRESULT OnWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + std::string test; + + private: + std::unordered_map mapUID2HWND; + std::unordered_map mapHWND2PTR; + + std::wstring ConvertS2W(std::string s); + + }; + } +} +//! END DECLARATION \ No newline at end of file diff --git a/dev/src/image.cpp b/dev/src/image.cpp new file mode 100644 index 00000000..664057b2 --- /dev/null +++ b/dev/src/image.cpp @@ -0,0 +1,52 @@ +#include "image.h" + +//! START IMPLEMENTATION +namespace olc +{ + ImageCPU::ImageCPU(const olc::vi2d& size) : dimensions(size) + { + pixels.resize(dimensions.area(), olc::Pixel(255, 165, 0)); + } + + ImageCPU::~ImageCPU() + { + pixels.clear(); + } + + const olc::vi2d& ImageCPU::Size() const + { + return dimensions; + } + + olc::Pixel* ImageCPU::Data() + { + return pixels.data(); + } + + + Image::Image(const olc::vi2d& size, const bool onCPU, const bool onGPU) + { + // Always create CPU resident image + imCPU = std::make_unique(size); + + if (onGPU) + { + + } + + if (!onCPU) + imCPU.reset(); + } + + bool Image::InCPU() const + { + return imCPU != nullptr; + } + + bool Image::InGPU() const + { + return imGPU != nullptr; + } + +}; +//! END IMPLEMENTATION \ No newline at end of file diff --git a/dev/src/image.h b/dev/src/image.h index ae20e8dc..a8cd842a 100644 --- a/dev/src/image.h +++ b/dev/src/image.h @@ -1,50 +1,40 @@ #pragma once -//! STDHEADER START +//! START STDHEADER #include #include #include #include #include #include -//! STDHEADER END +//! END STDHEADER -//! CUSTOMHEADER START +//! START CUSTOMHEADER #include "config.h" #include "vector2d.h" #include "pixel.h" -//! CUSTOMHEADER END +//! END CUSTOMHEADER -//! DECLARATION START +//! START DECLARATION #if !defined(PGE_IMAGE_DECLARED) namespace olc { + struct ImageConfig + { + bool Filtered = false; + bool Clamp = false; + }; + class ImageCPU { public: ImageCPU() = default; - - ImageCPU(const olc::vi2d& size) : dimensions(size) - { - pixels.resize(dimensions.area(), olc::Pixel(255,165,0)); - } - - virtual ~ImageCPU() - { - pixels.clear(); - } - + ImageCPU(const olc::vi2d& size); + virtual ~ImageCPU(); public: - const olc::vi2d& Size() const - { - return dimensions; - } - - olc::Pixel* Data() - { - return pixels.data(); - } + const olc::vi2d& Size() const; + olc::Pixel* Data(); public: olc::vi2d dimensions; @@ -62,31 +52,12 @@ namespace olc { public: // Constructs a general purpose image - Image(const olc::vi2d& size, const bool onCPU = true, const bool onGPU = true) - { - // Always create CPU resident image - imCPU = std::make_unique(size); - - if (onGPU) - { - - } - - if (!onCPU) - imCPU.reset(); - } - + Image(const olc::vi2d& size, const bool onCPU = true, const bool onGPU = true); public: - inline constexpr bool InCPU() const - { - return imCPU != nullptr; - } + bool InCPU() const; + bool InGPU() const; - inline constexpr bool InGPU() const - { - return imGPU != nullptr; - } protected: std::unique_ptr imCPU = nullptr; @@ -95,4 +66,4 @@ namespace olc } #define PGE_IMAGE_DECLARED 1 #endif -//! DECLARATION END +//! END DECLARATION diff --git a/dev/src/olcpge3.h b/dev/src/olcpge3.h new file mode 100644 index 00000000..90d21e0d --- /dev/null +++ b/dev/src/olcpge3.h @@ -0,0 +1,17 @@ +#pragma once + +#include "config.h" +#include "pixel.h" +#include "vector2d.h" +#include "matrix3d.h" +#include "transform2d.h" +#include "window.h" +#include "core.h" +#include "image.h" +#include "gputask.h" +#include "gpu_iface.h" +#include "gpu_opengl33.h" + + + + diff --git a/dev/src/polyform.h b/dev/src/polyform.h index 611ad3a0..c12006b3 100644 --- a/dev/src/polyform.h +++ b/dev/src/polyform.h @@ -9,6 +9,9 @@ //! START CUSTOMHEADER #include "config.h" +#include "pixel.h" +#include "vector2d.h" +#include "gputask.h" //! END CUSTOMHEADER //! START DECLARATION @@ -17,7 +20,17 @@ namespace olc { namespace polyformer { + using namespace pgeguts; + //namespace internal + //{ + // auto vectorzip(const auto& src1, const auto& src2) + // { + + // } + //} + + } } #define PGE_POLYFORM_DECLARED 1 diff --git a/dev/src/sh_template.h b/dev/src/sh_template.h index 614061f9..9ce365ce 100644 --- a/dev/src/sh_template.h +++ b/dev/src/sh_template.h @@ -18,18 +18,70 @@ //! GRAB matrix3d.h DECLARATION //! GRAB transform2d.h DECLARATION + +//! GRAB image.h DECLARATION //! GRAB window.h DECLARATION + +//! GRAB gpu_iface.h DECLARATION + +//! GRAB host_iface.h DECLARATION //! GRAB core.h DECLARATION + + + + + +#if OLC_HOST == OLC_HOST_WINDOWS +//! GRAB host_win_winapi.h WINAPI_CONFIG + +//! GRAB host_win_winapi.h DECLARATION +#endif + +#if OLC_GPU == OLC_GPU_OPENGL33 +//! GRAB api_opengl.h OPENGL_CONFIG + +//! GRAB api_opengl.h DECLARATION + +//! GRAB gpu_opengl33.h DECLARATION +#endif + +//! GRAB gputask.h DECLARATION + + + + #if defined(OLC_PGE_APPLICATION) && !defined(PGE_WINDOW_IMPLEMENTED) //! GRAB window.cpp IMPLEMENTATION #define PGE_WINDOW_IMPLEMENTED 1 #endif +#if defined(OLC_PGE_APPLICATION) && !defined(PGE_HOST_IMPLEMENTED) +#if OLC_HOST == OLC_HOST_WINDOWS +//! GRAB host_win_winapi.cpp IMPLEMENTATION +#endif +#define PGE_HOST_IMPLEMENTED 1 +#endif + +#if defined(OLC_PGE_APPLICATION) && !defined(PGE_GPU_IMPLEMENTED) +#if OLC_GPU == OLC_GPU_OPENGL33 +//! GRAB api_opengl.cpp IMPLEMENTATION +//! GRAB gpu_opengl33.cpp IMPLEMENTATION +#endif +#define PGE_GPU_IMPLEMENTED 1 +#endif + #if defined(OLC_PGE_APPLICATION) && !defined(PGE_CORE_IMPLEMENTED) //! GRAB core.cpp IMPLEMENTATION #define PGE_CORE_IMPLEMENTED 1 #endif + +#if defined(OLC_PGE_APPLICATION) && !defined(PGE_IMAGE_IMPLEMENTED) +//! GRAB image.cpp IMPLEMENTATION +#define PGE_IMAGE_IMPLEMENTED 1 +#endif + + diff --git a/dev/src/window.cpp b/dev/src/window.cpp index 92d30e00..e311d5d7 100644 --- a/dev/src/window.cpp +++ b/dev/src/window.cpp @@ -1,16 +1,26 @@ #include "window.h" +#include "host_iface.h" + //! START IMPLEMENTATION namespace olc { - Window::Window() + Window::Window() : std::enable_shared_from_this() { + nUniqueID = pgeguts::CreateUID(); } Window::~Window() { } + void Window::ConnecToHost(olc::host::Host* host) + { + pHost = host; + sFrameTitle = "OneLoneCoder.com - Pixel Game Engine 3"; + pHost->UpdateWindowFrameTitle(this); + } + bool Window::OnUserCreate() { return false; @@ -26,6 +36,37 @@ namespace olc return false; } + bool Window::olc_OnMouseButton(const uint8_t nButton, const bool bPressed) + { + return false; + } + + bool Window::olc_OnMouseMove(const olc::vi2d& vMousePos) + { + volatile_vMousePos = vMousePos; + return true; + } + + bool Window::olc_OnMouseWheel(const int32_t nScroll) + { + return false; + } + + bool Window::olc_OnMouseFocus(const bool bHasFocus) + { + return false; + } + + bool Window::olc_OnWindowPosition(const olc::vi2d& vWindowPos) + { + return false; + } + + bool Window::olc_OnWindowSize(const olc::vi2d& vWindowSize) + { + return false; + } + bool Window::olc_ShouldRemove() const { return bShouldRemove; @@ -48,5 +89,53 @@ namespace olc return false; } + + const size_t Window::GetUID() const + { + return nUniqueID; + } + + const olc::vi2d& Window::GetSize() const + { + return vWindowSize; + } + + const olc::vi2d& Window::GetPosition() const + { + return vWindowPos; + } + + bool Window::SetSize(const olc::vi2d& vSize) + { + // Request host to change geometry of window object + + vWindowSize = vSize; + return true; + } + + bool Window::SetPosition(const olc::vi2d& vPosition) + { + // Request host to change geometry of window object + + vWindowPos = vPosition; + return true; + } + + const std::string& Window::GetTitle() const + { + return sFrameTitle; + } + + bool Window::SetTitle(const std::string& sTitle) + { + sFrameTitle = sTitle; + pHost->UpdateWindowFrameTitle(this); + return false; + } + + bool Window::olc_PrimaryWindowInit() + { + return false; + } }; //! END IMPLEMENTATION \ No newline at end of file diff --git a/dev/src/window.h b/dev/src/window.h index 1ec3abd1..46831784 100644 --- a/dev/src/window.h +++ b/dev/src/window.h @@ -7,35 +7,95 @@ #include #include #include + //! END STDHEADER //! START CUSTOMHEADER GLOBAL #include "config.h" #include "pixel.h" #include "vector2d.h" +#include "gpu_iface.h" //! END CUSTOMHEADER //! START DECLARATION #if !defined(PGE_WINDOW_DECLARED) + +#if OLC_HOST == OLC_HOST_WINDOWS + #define FRIENDLY_HOST Host_Windows_WinAPI +#endif + namespace olc { - class Window + namespace host + { + class FRIENDLY_HOST; + class Host; + } + + class Window : public std::enable_shared_from_this { + friend class olc::host::FRIENDLY_HOST; + public: Window(); virtual ~Window(); + void ConnecToHost(olc::host::Host* host); + public: virtual bool OnUserCreate(); virtual bool OnUserUpdate(float fElapsedTime); virtual bool OnUserDestroy(); + private: + // Set Mouse Device State + bool olc_OnMouseButton(const uint8_t nButton, const bool bPressed); + bool olc_OnMouseMove(const olc::vi2d& vMousePos); + bool olc_OnMouseWheel(const int32_t nScroll); + bool olc_OnMouseFocus(const bool bHasFocus); + + // Set Window State + bool olc_OnWindowPosition(const olc::vi2d& vWindowPos); + bool olc_OnWindowSize(const olc::vi2d& vWindowSize); + + // Set Keyboard State + + + public: bool olc_ShouldRemove() const; bool olc_WindowUpdate(const float fElapsedTime); + public: + const size_t GetUID() const; + + const olc::vi2d& GetSize() const; + bool SetSize(const olc::vi2d& vSize); + + const olc::vi2d& GetPosition() const; + bool SetPosition(const olc::vi2d& vPosition); + + const std::string& GetTitle() const; + bool SetTitle(const std::string& sTitle); + + + protected: + virtual bool olc_PrimaryWindowInit(); + protected: bool bShouldRemove = false; + + protected: + size_t nUniqueID = -1; + olc::vi2d vWindowPos; + olc::vi2d vWindowSize; + std::string sFrameTitle; + + private: + olc::vi2d volatile_vMousePos; + + olc::host::Host* pHost = nullptr; + //olc::gpu::Renderer* pRenderer = nullptr; }; } #define PGE_WINDOW_DECLARED 1 diff --git a/dev/tests/test_main.cpp b/dev/tests/test_main.cpp index daec38ff..31b680cd 100644 --- a/dev/tests/test_main.cpp +++ b/dev/tests/test_main.cpp @@ -3,11 +3,18 @@ extern "C" void test_pixels(); extern "C" void test_vector2d(); extern "C" void test_matrices(); +#include "api_opengl.h" + int main() { test_pixels(); test_vector2d(); test_matrices(); + + auto& gl = olc::apis::opengl::gl::Get(); + + gl.glBindBuffer(0, 0); + return 0; } \ No newline at end of file diff --git a/dev/tests/test_mh.cpp b/dev/tests/test_mh.cpp new file mode 100644 index 00000000..8a08b7b3 --- /dev/null +++ b/dev/tests/test_mh.cpp @@ -0,0 +1,31 @@ +#define OLC_PGE_APPLICATION +#include "olcpge3.h" + +class Example : public olc::PixelGameEngine +{ +public: + Example() + { + + } + +public: + bool OnUserCreate() override + { + return true; + } + + bool OnUserUpdate(float fElapsedTime) override + { + return true; + } +}; + +int main() +{ + Example demo; + if (demo.Construct({ 256, 240 }, { 4, 4 })) + demo.Start(); + + return 0; +} \ No newline at end of file From 90a9998b85b11a5f18751ef49b96de68c41dd64e Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Tue, 2 Sep 2025 21:45:54 +0100 Subject: [PATCH 11/11] It displays orange. Basically completed now. --- .../olcPGE3_BuildSH/olcPixelGameEngine3.h | 767 ++++++++++++------ dev/src/api_opengl.cpp | 52 ++ dev/src/api_opengl.h | 24 +- dev/src/core.cpp | 102 ++- dev/src/core.h | 20 +- dev/src/gpu_iface.h | 20 +- dev/src/gpu_opengl33.cpp | 87 +- dev/src/gpu_opengl33.h | 15 + dev/src/host_iface.h | 7 +- dev/src/host_win_winapi.cpp | 93 +-- dev/src/host_win_winapi.h | 11 +- dev/src/image.cpp | 40 +- dev/src/image.h | 58 +- dev/src/sh_template.h | 4 +- dev/src/window.cpp | 14 +- dev/src/window.h | 14 +- dev/tests/test_mh.cpp | 3 + 17 files changed, 928 insertions(+), 403 deletions(-) diff --git a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h index a5abe704..6ac0a1a4 100644 --- a/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h +++ b/dev/msvc/olcPGE3_BuildSH/olcPixelGameEngine3.h @@ -1123,45 +1123,47 @@ namespace olc { bool Filtered = false; bool Clamp = false; - }; - - class ImageCPU - { - public: - ImageCPU() = default; - ImageCPU(const olc::vi2d& size); - virtual ~ImageCPU(); - - public: - const olc::vi2d& Size() const; - olc::Pixel* Data(); - - public: - olc::vi2d dimensions; - std::vector pixels; - }; - - struct ImageGPU - { - olc::vf2d size; - olc::vf2d invsize; - int32_t resourceID = -1; + bool InRAM = true; + bool InVRAM = true; }; class Image { public: // Constructs a general purpose image - Image(const olc::vi2d& size, const bool onCPU = true, const bool onGPU = true); + Image() = default; + virtual ~Image() = default; public: - bool InCPU() const; - bool InGPU() const; + bool Create(const olc::vi2d& size, const ImageConfig& cfg = olc::ImageConfig()); + public: + // Returns size (x, y) in pixels + const olc::vi2d& Size() const; + // Returns read/write pointer to start of 1D stream of pixel data + olc::Pixel* Data(); + // Returns how this image was configured upon creation + const ImageConfig& GetConfig() const; + // Return GPU Resource ID + int32_t GetGPUID() const; + // Set GPU Resource ID (0 to eliminate) + void SetGPUID(const int32_t id); + + // True if data mirror has in-front RAM image + bool HasWetCPU() const; + // True if data mirror has in-front VRAM image + bool HasWetGPU() const; + + void PrimeCPU(); + void PrimeGPU(); protected: - std::unique_ptr imCPU = nullptr; - std::unique_ptr imGPU = nullptr; + ImageConfig config; + olc::vi2d dimensions; + std::vector pixels; + int32_t gpuResourceID = 0; + bool gpuWet = false; + bool cpuWet = true; }; } #define PGE_IMAGE_DECLARED 1 @@ -1181,7 +1183,12 @@ namespace olc class Host; } - class Window : public std::enable_shared_from_this + namespace gpu + { + class Renderer; + } + + class Window { friend class olc::host::FRIENDLY_HOST; @@ -1192,8 +1199,11 @@ namespace olc void ConnecToHost(olc::host::Host* host); public: + // Return true if window is to continue virtual bool OnUserCreate(); + // Return true if window is to continue virtual bool OnUserUpdate(float fElapsedTime); + // Return true if window is to close virtual bool OnUserDestroy(); private: @@ -1213,10 +1223,10 @@ namespace olc public: bool olc_ShouldRemove() const; - bool olc_WindowUpdate(const float fElapsedTime); + bool olc_WindowUpdate(const float fElapsedTime, olc::gpu::Renderer* const gpu); public: - const size_t GetUID() const; + size_t GetUID() const; const olc::vi2d& GetSize() const; bool SetSize(const olc::vi2d& vSize); @@ -1250,6 +1260,112 @@ namespace olc #define PGE_WINDOW_DECLARED 1 #endif +#if !defined(PGE_GPUTASK_DECLARED) +namespace olc +{ + namespace pgeguts + { + // This is the default "packet" of work that is sent to + // a GPU for drawing. Various drawing operations throughout + // PGE create GPUTasks which are stored and dispatched when + // appropriate. It's contents are purposefully abstract + // to ensure rendering tools do not need to be reliant + // upon PGE structures + struct GPUTask + { + struct Vertex + { + float p[6]; // x, y, z, w, u, v + olc::Pixel c; // 32-bit colour + }; + + // Simple vertex buffer + std::vector vertexBuffer; + + // Model-View-Projection Matrix for shader + std::array mvpMatrix = { { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + } }; + + // Use depth components + bool bDepth = false; + + // Use hardware wire drawing + bool bWireframe = false; + + // Overall biasing colour (great for blends) + olc::Pixel tint = olc::Colour::WHITE; + + // Define super structure to be drawn + enum class Structure : uint8_t + { + // Vertex buffer is a series of points + Point = 0, + // Vertex buffer is a series of line segments + Line, + // Vertex buffer is a fan of triangles + Fan, + // Vertex buffer is a strip of adjacent triangles + Strip, + // Vertex buffer is a series of discrete triangles + List + } structure = Structure::Fan; + + // Define if GPU should face cull based on winding order + enum class CullMode : uint8_t + { + // No + None = 0, + // Cull if vertices are listed in clockwise order + ClockWise, + // Cull if vertices are listed in anticlockwise order + CounterClockWise + } cullmode = CullMode::None; + }; + + + + //// Construct a wireframe polygon + //GPUTask DrawPolygon2D( + // const GPUTask::Structure format, + // const olc::tf2d& transform, + // const std::vector& vPoints, + // const std::vector& vColours, + // const olc::Pixel tint = olc::Colour::WHITE) + //{ + // GPUTask task; + // task.structure = format; + // task.bDepth = false; + // task.tint = tint; + // task.cullmode = GPUTask::CullMode::None; + // task.bWireframe = true; + + // const auto& m = transform.forward_matrix().m; + // task.mvpMatrix = { { + // m[0], m[1], m[2], 0.0f, + // m[3], m[4], m[5], 0.0f, + // m[6], m[7], m[8], 0.0f, + // 0.0f, 0.0f, 0.0f, 1.0f + // } }; + + // // Pseudo-zip construct vertex buffer + // size_t nFullVerts = std::min({ vPoints.size(), vColours.size() }); + // task.vertexBuffer.resize(nFullVerts); + // for (size_t i = 0; i < nFullVerts; i++) + // { + // task.vertexBuffer[i] = { vPoints[i].x, vPoints[i].y, 0.0f, 1.0f, 0.0f, 0.0f, vColours[i] }; + // } + + // return task; + //} + } +} +#define PGE_GPUTASK_DECLARED 1 +#endif + #if !defined(PGE_RENDERER_IFACE_DECLARED) namespace olc { @@ -1278,7 +1394,8 @@ namespace olc public: // Check/Get last error - const RendererError GetLastError() const { return lastError; } + inline RendererError GetLastError() const { return lastError; } + inline const RendererConfig& GetConfig() const { return config; } public: // Device Stuff // Constructs a GPU Device interface @@ -1300,7 +1417,24 @@ namespace olc // Makes active the given texture resource (for subsequent rendering operations) virtual bool AssignTextureTarget(const uint32_t slot, const uint32_t texid) = 0; + public: // Shader Construction Stuff + + public: // GPU Task Processing Stuff + virtual bool DoGPUTask(const olc::pgeguts::GPUTask& task) = 0; + + public: // Swap Chain Stuff + // Clears the viewport to a specific colour and depth + virtual bool ClearViewport(const olc::Pixel col, bool bDepth, bool bStencil) = 0; + // Sets the viewport area of the drawing space + virtual bool SetViewport(const olc::vf2d& pos, const olc::vf2d& size) = 0; + // Configures defaults prior to drawing + virtual bool DisplayPrepare() = 0; + // Displays the final output + virtual bool DisplayDraw(bool bVerticalSyncNow = false) = 0; + + protected: + RendererConfig config; RendererError lastError = RendererError::None; }; } @@ -1345,17 +1479,22 @@ namespace olc public: // Check/Get last error - const HostError GetLastError() const { return lastError; } + HostError GetLastError() const { return lastError; } public: virtual bool StartSystemEventLoop() = 0; virtual bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen) = 0; virtual bool UpdateWindowFrameTitle(olc::Window* pWindow) = 0; + + virtual std::vector GetHostWindowDescriptor(olc::Window* pWindow) = 0; virtual bool ConnectHostResourceToRenderer() = 0; + // Wait for entire host desktop refresh (for smooooth vsync) + virtual bool SyncWithDesktopComposite() = 0; + protected: HostError lastError = HostError::None; }; @@ -1407,11 +1546,29 @@ namespace olc public: bool Start(); - public: + public: // The "Overridables" + // Called once when the engine is ready and the window is created bool OnUserCreate() override; + // Called every frame while the engine is running bool OnUserUpdate(float fElapsedTime) override; + // Called when something requests the engine shut down bool OnUserDestroy() override; + + public: // olc::Image Handling + // Create an image resource + bool CreateImage(olc::Image& image, const olc::vi2d& size, const ImageConfig& cfg = olc::ImageConfig()); + // Create an image resource based on an image file asset on disk + bool CreateImageFromFile(olc::Image& image, const std::string& sFileName, const ImageConfig& cfg = olc::ImageConfig()); + // Create an image resource based on an image file asset in memory + bool CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes, const ImageConfig& cfg = olc::ImageConfig()); + // Store an image as a file asset on disk + bool WriteImageToFile(const olc::Image& image, const std::string& sFileName); + // Store an image as a file asset in memory + bool WriteImageToMemory(const olc::Image& image, std::vector bytes, const std::string& sFileName); + // Destroy an image + void DestroyImage(olc::Image& image); + protected: bool olc_PrimaryWindowInit() override; @@ -1475,7 +1632,9 @@ namespace olc // Embrace MSVC superiority #pragma comment(lib, "user32.lib") #pragma comment(lib, "gdi32.lib") +#pragma comment(lib, "Dwmapi.lib") +#include #include #undef _WINSOCKAPI_ @@ -1497,12 +1656,16 @@ namespace olc public: bool StartSystemEventLoop(); - bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen); - + bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen); bool UpdateWindowFrameTitle(olc::Window* pWindow); + std::vector GetHostWindowDescriptor(olc::Window* pWindow); bool ConnectHostResourceToRenderer(); + + // Wait for entire host desktop refresh (for smooooth vsync) + bool SyncWithDesktopComposite(); + LRESULT OnWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); std::string test; @@ -1510,7 +1673,6 @@ namespace olc private: std::unordered_map mapUID2HWND; std::unordered_map mapHWND2PTR; - std::wstring ConvertS2W(std::string s); }; @@ -1519,14 +1681,15 @@ namespace olc #endif #if OLC_GPU == OLC_GPU_OPENGL33 -#define CALLSTYLE + #if OLC_HOST == OLC_HOST_WINDOWS #include #pragma comment(lib, "gdi32.lib") #pragma comment(lib, "opengl32.lib") #include #define CALLSTYLE __stdcall - #define OGL_LOAD(t) (t##_t*)wglGetProcAddress(#t) + // ooof... was getting a bunch of spurious C4191 from MSVC 17.14.9, so round trip via void-town + #define OGL_LOAD(t) reinterpret_cast(reinterpret_cast(wglGetProcAddress(#t))) #endif #if OLC_HOST == OLC_HOST_LINUX_X11 || OLC_HOST == OLC_HOST_LINUX_WAYLAND @@ -1552,10 +1715,15 @@ namespace olc #define GL_GLEXT_PROTOTYPES #include #include - #define CALLSTYLE #define GL_CLAMP GL_CLAMP_TO_EDGE #endif +#if !defined(CALLSTYLE) + #define CALLSTYLE +#endif + + + namespace olc { namespace apis::opengl @@ -1651,6 +1819,18 @@ namespace olc glFrameBufferTexture2D_t* glFrameBufferTexture2D = nullptr; glDrawBuffers_t* glDrawBuffers = nullptr; glBlendFuncSeparate_t* glBlendFuncSeparate = nullptr; + + // OpenGL1.2 Proxies (just keeps things tidy imo) + void glGenTextures(GLsizei n, GLuint* textures); + void glBindTexture(GLenum target, GLuint texture); + void glTexParameteri(GLenum target, GLenum pname, GLint param); + void glTexEnvf(GLenum target, GLenum pname, GLfloat param); + void glDeleteTextures(GLsizei n, const GLuint* textures); + void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels); + void glClear(GLbitfield mask); + void glViewport(GLint x, GLint y, GLsizei width, GLsizei height); + void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + }; } @@ -1695,6 +1875,21 @@ namespace olc bool AssignTextureSource(const uint32_t slot, const uint32_t texid) override; // Makes active the given texture resource (for subsequent rendering operations) bool AssignTextureTarget(const uint32_t slot, const uint32_t texid) override; + + + public: // GPU Task Stuff + virtual bool DoGPUTask(const olc::pgeguts::GPUTask& task) override; + + public: // Swap Chain Stuff + // Clears the viewport to a specific colour and depth + virtual bool ClearViewport(const olc::Pixel col, bool bDepth, bool bStencil) override; + // Sets the viewport area of the drawing space + virtual bool SetViewport(const olc::vf2d& pos, const olc::vf2d& size) override; + // Configures defaults prior to drawing + virtual bool DisplayPrepare() override; + // Displays the final output + virtual bool DisplayDraw(bool bVerticalSyncNow) override; + protected: // These may need some thinking about re multiple window olc::apis::opengl::glDeviceContext_t glDeviceContext = 0; @@ -1707,111 +1902,7 @@ namespace olc #endif #endif -#if !defined(PGE_GPUTASK_DECLARED) -namespace olc -{ - namespace pgeguts - { - // This is the default "packet" of work that is sent to - // a GPU for drawing. Various drawing operations throughout - // PGE create GPUTasks which are stored and dispatched when - // appropriate. It's contents are purposefully abstract - // to ensure rendering tools do not need to be reliant - // upon PGE structures - struct GPUTask - { - struct Vertex - { - float p[6]; // x, y, z, w, u, v - olc::Pixel c; // 32-bit colour - }; - - // Simple vertex buffer - std::vector vertexBuffer; - // Model-View-Projection Matrix for shader - std::array mvpMatrix = { { - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - } }; - - // Use depth components - bool bDepth = false; - - // Use hardware wire drawing - bool bWireframe = false; - - // Overall biasing colour (great for blends) - olc::Pixel tint = olc::Colour::WHITE; - - // Define super structure to be drawn - enum class Structure : uint8_t - { - // Vertex buffer is a series of points - Point = 0, - // Vertex buffer is a series of line segments - Line, - // Vertex buffer is a fan of triangles - Fan, - // Vertex buffer is a strip of adjacent triangles - Strip, - // Vertex buffer is a series of discrete triangles - List - } structure = Structure::Fan; - - // Define if GPU should face cull based on winding order - enum class CullMode : uint8_t - { - // No - None = 0, - // Cull if vertices are listed in clockwise order - ClockWise, - // Cull if vertices are listed in anticlockwise order - CounterClockWise - } cullmode = CullMode::None; - }; - - - - //// Construct a wireframe polygon - //GPUTask DrawPolygon2D( - // const GPUTask::Structure format, - // const olc::tf2d& transform, - // const std::vector& vPoints, - // const std::vector& vColours, - // const olc::Pixel tint = olc::Colour::WHITE) - //{ - // GPUTask task; - // task.structure = format; - // task.bDepth = false; - // task.tint = tint; - // task.cullmode = GPUTask::CullMode::None; - // task.bWireframe = true; - - // const auto& m = transform.forward_matrix().m; - // task.mvpMatrix = { { - // m[0], m[1], m[2], 0.0f, - // m[3], m[4], m[5], 0.0f, - // m[6], m[7], m[8], 0.0f, - // 0.0f, 0.0f, 0.0f, 1.0f - // } }; - - // // Pseudo-zip construct vertex buffer - // size_t nFullVerts = std::min({ vPoints.size(), vColours.size() }); - // task.vertexBuffer.resize(nFullVerts); - // for (size_t i = 0; i < nFullVerts; i++) - // { - // task.vertexBuffer[i] = { vPoints[i].x, vPoints[i].y, 0.0f, 1.0f, 0.0f, 0.0f, vColours[i] }; - // } - - // return task; - //} - } -} -#define PGE_GPUTASK_DECLARED 1 -#endif @@ -1819,7 +1910,7 @@ namespace olc #if defined(OLC_PGE_APPLICATION) && !defined(PGE_WINDOW_IMPLEMENTED) namespace olc { - Window::Window() : std::enable_shared_from_this() + Window::Window() { nUniqueID = pgeguts::CreateUID(); } @@ -1886,10 +1977,13 @@ namespace olc return bShouldRemove; } - bool Window::olc_WindowUpdate(const float fElapsedTime) + bool Window::olc_WindowUpdate(const float fElapsedTime, olc::gpu::Renderer* const gpu) { // Environmental changes + gpu->SetViewport({ 0,0 }, GetSize()); + gpu->ClearViewport(olc::Colour::TANGERINE, false, false); + // User Update if (!OnUserUpdate(fElapsedTime)) { @@ -1901,10 +1995,13 @@ namespace olc } } - return false; + + gpu->DisplayDraw(); + + return true; } - const size_t Window::GetUID() const + size_t Window::GetUID() const { return nUniqueID; } @@ -1962,6 +2059,8 @@ namespace olc::host // Forward Declaration static LRESULT CALLBACK WINAPI_EventHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + // Windows app needs an event loop somewhere. This is blocking of course. This loop handles + // all windows created for this host. bool Host_Windows_WinAPI::StartSystemEventLoop() { MSG msg; @@ -2098,11 +2197,21 @@ namespace olc::host return true; } + std::vector Host_Windows_WinAPI::GetHostWindowDescriptor(olc::Window* pWindow) + { + return { mapUID2HWND[pWindow->GetUID()] }; + } + bool Host_Windows_WinAPI::ConnectHostResourceToRenderer() { return false; } + bool Host_Windows_WinAPI::SyncWithDesktopComposite() + { + return DwmFlush() == S_OK; + } + LRESULT Host_Windows_WinAPI::OnWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (!mapHWND2PTR.contains(hWnd)) @@ -2111,14 +2220,19 @@ namespace olc::host // Get target olc::Window const auto& window = mapHWND2PTR.at(hWnd); + // Many WinAPI events are literally ancient these days, so need some interpretation + // to get to the useful data. + switch (uMsg) { - case WM_MOUSEMOVE: + case WM_MOUSEMOVE: // Mouse has moved within a window { - uint16_t x = lParam & 0xFFFF; - uint16_t y = (lParam >> 16) & 0xFFFF; + // Extract mouse X & Y + uint16_t x = uint16_t(lParam & 0xFFFF); + uint16_t y = uint16_t((lParam >> 16) & 0xFFFF); int16_t ix = *(int16_t*)&x; int16_t iy = *(int16_t*)&y; + // Tell window new mouse location window->olc_OnMouseMove(olc::vi2d{ ix, iy }); return 0; } @@ -2188,76 +2302,6 @@ namespace olc::host return DefWindowProc(hWnd, uMsg, wParam, lParam); } - // Lives in olc::host namespace - -// switch (uMsg) -// { -// case WM_MOUSEMOVE: -// { -// // Thanks @ForAbby (Discord) -// uint16_t x = lParam & 0xFFFF; uint16_t y = (lParam >> 16) & 0xFFFF; -// int16_t ix = *(int16_t*)&x; int16_t iy = *(int16_t*)&y; -// ptrPGE->olc_UpdateMouse(ix, iy); -// return 0; -// } -// case WM_MOVE: vWinPos = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowPos(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; -// case WM_SIZE: vWinSize = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; -// case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0; -// case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0; -// case WM_SETFOCUS: ptrPGE->olc_UpdateKeyFocus(true); return 0; -// case WM_KILLFOCUS: ptrPGE->olc_UpdateKeyFocus(false); return 0; -// case WM_KEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; -// case WM_KEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; -// case WM_SYSKEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; -// case WM_SYSKEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; -// case WM_LBUTTONDOWN:ptrPGE->olc_UpdateMouseState(0, true); return 0; -// case WM_LBUTTONUP: ptrPGE->olc_UpdateMouseState(0, false); return 0; -// case WM_RBUTTONDOWN:ptrPGE->olc_UpdateMouseState(1, true); return 0; -// case WM_RBUTTONUP: ptrPGE->olc_UpdateMouseState(1, false); return 0; -// case WM_MBUTTONDOWN:ptrPGE->olc_UpdateMouseState(2, true); return 0; -// case WM_MBUTTONUP: ptrPGE->olc_UpdateMouseState(2, false); return 0; -// case WM_DROPFILES: -// { -// // This is all eww... -// HDROP drop = (HDROP)wParam; -// -// uint32_t nFiles = DragQueryFile(drop, 0xFFFFFFFF, nullptr, 0); -// std::vector vFiles; -// for (uint32_t i = 0; i < nFiles; i++) -// { -// TCHAR dfbuffer[256]{}; -// uint32_t len = DragQueryFile(drop, i, nullptr, 0); -// DragQueryFile(drop, i, dfbuffer, 256); -//#ifdef UNICODE -//#ifdef __MINGW32__ -// char* buffer = new char[len + 1]; -// wcstombs(buffer, dfbuffer, len); -// buffer[len] = '\0'; -//#else -// int count = WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, NULL, 0, NULL, NULL); -// char* buffer = new char[count]; -// WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, buffer, count, NULL, NULL); -//#endif -// vFiles.push_back(std::string(buffer)); -// delete[] buffer; -//#else -// vFiles.push_back(std::string(dfbuffer)); -//#endif -// } -// -// // Even more eww... -// POINT p; DragQueryPoint(drop, &p); -// ptrPGE->olc_DropFiles(p.x, p.y, vFiles); -// DragFinish(drop); -// return 0; -// } -// break; -// -// -// case WM_CLOSE: ptrPGE->olc_Terminate(); return 0; -// case WM_DESTROY: PostQuitMessage(0); DestroyWindow(hWnd); return 0; -// } - }; #endif #define PGE_HOST_IMPLEMENTED 1 @@ -2278,6 +2322,7 @@ namespace olc::apis::opengl bool gl::LoadAll() { + // Load all the OpenGL API entry points bLoaded = true; bLoaded &= (glCreateShader = OGL_LOAD(glCreateShader)) != nullptr; bLoaded &= (glCreateProgram = OGL_LOAD(glCreateProgram)) != nullptr; @@ -2309,15 +2354,70 @@ namespace olc::apis::opengl bLoaded &= (glFrameBufferTexture2D = OGL_LOAD(glFrameBufferTexture2D)) != nullptr; bLoaded &= (glDrawBuffers = OGL_LOAD(glDrawBuffers)) != nullptr; bLoaded &= (glBlendFuncSeparate = OGL_LOAD(glBlendFuncSeparate)) != nullptr; + + if (!bLoaded) + return false; // API load has failed + + + return bLoaded; } + + void gl::glGenTextures(GLsizei n, GLuint* textures) + { + ::glGenTextures(n, textures); + } + + void gl::glBindTexture(GLenum target, GLuint texture) + { + ::glBindTexture(target, texture); + } + + void gl::glTexParameteri(GLenum target, GLenum pname, GLint param) + { + ::glTexParameteri(target, pname, param); + } + + void gl::glTexEnvf(GLenum target, GLenum pname, GLfloat param) + { + ::glTexEnvf(target, pname, param); + } + + void gl::glDeleteTextures(GLsizei n, const GLuint* textures) + { + ::glDeleteTextures(n, textures); + } + + void gl::glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) + { + ::glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); + } + + void gl::glClear(GLbitfield mask) + { + ::glClear(mask); + } + + void gl::glViewport(GLint x, GLint y, GLsizei width, GLsizei height) + { + ::glViewport(x, y, width, height); + } + + void gl::glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) + { + ::glClearColor(red, green, blue, alpha); + } } namespace olc::gpu { bool Renderer_OGL33::CreateDevice(std::vector params, const RendererConfig& cfg) { + config = cfg; + + // Create OpenGL Device Context #if OLC_HOST == OLC_HOST_WINDOWS + // "wgl*" all live in WinGDI glDeviceContext = GetDC((HWND)(params[0])); PIXELFORMATDESCRIPTOR pfd = @@ -2371,18 +2471,57 @@ namespace olc::gpu bool Renderer_OGL33::DestroyDevice() { auto& gl = olc::apis::opengl::gl::Get(); + +#if OLC_HOST == OLC_HOST_WINDOWS + wglDeleteContext(glRenderContext); +#endif return false; } uint32_t Renderer_OGL33::CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg) { auto& gl = olc::apis::opengl::gl::Get(); - return 0; + + // Curiously OpenGL doesnt actually care about the size of the texture + // as part of its creation. This matters later when we Write to texture + // resources on GPU + + uint32_t id = 0; + gl.glGenTextures(1, &id); + gl.glBindTexture(GL_TEXTURE_2D, id); + + if (cfg.Filtered) + { + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + if (cfg.Clamp) + { + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } + else + { + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + +#if OLC_HOST != OLC_HOST_EMSCRIPTEN + gl.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +#endif + return id; } bool Renderer_OGL33::WriteTexture(const uint32_t texid, olc::Image& image) { auto& gl = olc::apis::opengl::gl::Get(); + gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.Size().x, image.Size().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.Data()); return false; } @@ -2395,7 +2534,8 @@ namespace olc::gpu bool Renderer_OGL33::DeleteTexture(const uint32_t texid) { auto& gl = olc::apis::opengl::gl::Get(); - return false; + gl.glDeleteTextures(1, &texid); + return true; } bool Renderer_OGL33::AssignTextureSource(const uint32_t slot, const uint32_t texid) @@ -2409,6 +2549,45 @@ namespace olc::gpu auto& gl = olc::apis::opengl::gl::Get(); return false; } + + bool Renderer_OGL33::DoGPUTask(const olc::pgeguts::GPUTask& task) + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + bool Renderer_OGL33::ClearViewport(const olc::Pixel col, bool bDepth, bool bStencil) + { + auto& gl = olc::apis::opengl::gl::Get(); + gl.glClearColor(float(col.r) / 255.0f, float(col.g) / 255.0f, float(col.b) / 255.0f, float(col.a) / 255.0f); + gl.glClear(GL_COLOR_BUFFER_BIT | (bDepth ? GL_DEPTH_BUFFER_BIT : 0) | (bStencil ? GL_STENCIL_BUFFER_BIT : 0)); + return true; + } + + bool Renderer_OGL33::SetViewport(const olc::vf2d& pos, const olc::vf2d& size) + { + auto& gl = olc::apis::opengl::gl::Get(); + gl.glViewport(int(pos.x), int(pos.y), int(size.x), int(size.y)); + return false; + } + + bool Renderer_OGL33::DisplayPrepare() + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + bool Renderer_OGL33::DisplayDraw(bool bVerticalSyncNow) + { + auto& gl = olc::apis::opengl::gl::Get(); + + +#if OLC_HOST == OLC_HOST_WINDOWS + SwapBuffers(glDeviceContext); +#endif + + return false; + } } #endif #define PGE_GPU_IMPLEMENTED 1 @@ -2479,6 +2658,58 @@ namespace olc return false; } + bool PixelGameEngine::CreateImage(olc::Image& image, const olc::vi2d& size, const ImageConfig& cfg) + { + // Create CPU Image + if (!image.Create(size, cfg)) + return false; + + // Create GPU Image + auto id = gpu->CreateTexture(image.Size(), cfg); + if (id == 0) + { + image.Create({ 0,0 }); + return false; + } + + // Associate CPU object with GPU Resource + image.SetGPUID(id); + return true; + } + + bool PixelGameEngine::CreateImageFromFile(olc::Image& image, const std::string& sFileName, const ImageConfig& cfg) + { + return false; + } + + bool PixelGameEngine::CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes, const ImageConfig& cfg) + { + return false; + } + + bool PixelGameEngine::WriteImageToFile(const olc::Image& image, const std::string& sFileName) + { + return false; + } + + bool PixelGameEngine::WriteImageToMemory(const olc::Image& image, std::vector bytes, const std::string& sFileName) + { + return false; + } + + void PixelGameEngine::DestroyImage(olc::Image& image) + { + // If image has gpu resource, remove it + if (image.GetGPUID() != 0) + { + gpu->DeleteTexture(image.GetGPUID()); + image.SetGPUID(0); + } + + // Free any cpu memory associated with image + image.Create({ 0,0 }); + } + bool PixelGameEngine::olc_PrimaryWindowInit() { // Called by window to initialise core systems. Sub windows should ignore this @@ -2495,7 +2726,24 @@ namespace olc // Initialise GPU Interface - This thread is the context olc::gpu::RendererConfig cfgRenderer; gpu = std::make_unique(); - gpu->CreateDevice({ nullptr }, cfgRenderer); + + // The GPU device can be based upon the primary window configuration. This + // gives us completed gpu and host objects to pass to other windows as and + // when required + gpu->CreateDevice(host->GetHostWindowDescriptor(this), cfgRenderer); + if (gpu->GetLastError() != olc::gpu::RendererError::None) + { + const auto e = gpu->GetLastError(); // For debug visibility + std::cout << "Error: Could not create Renderer\n"; + return; + } + + if (!OnUserCreate()) + { + // Creation process signalled abort + return; + } + // Initialise Input Devices @@ -2503,7 +2751,7 @@ namespace olc durationFrameCount = 0s; - while (true) + while (coreActive) { // Frame Delta Timing - "ElapsedTime" since last core update // ~~~~~~~~~~~~~~~~~~ @@ -2535,13 +2783,21 @@ namespace olc SetTitle(sTitle); frameCount = 0; } - - // Primary Window - olc_WindowUpdate(fDT); - - // Child Windows + + // Update Child Windows (if any) for (auto& winChild : deqChildWindows) - winChild->olc_WindowUpdate(fDT); + winChild->olc_WindowUpdate(fDT, gpu.get()); + + // Update Primary Window + olc_WindowUpdate(fDT, gpu.get()); + + // Wait for vertical sync if required. + // Note: Child windows will never vsync as waiting for each buffer swap with vsync + // divides up teh frame rate budget across the windows. + if (gpu->GetConfig().VerticalSync) + { + host->SyncWithDesktopComposite(); + } // Remove child windows that have requested closure if (!deqChildWindows.empty()) @@ -2553,6 +2809,15 @@ namespace olc } )); } + + // Primary Window + if (olc_ShouldRemove()) + { + // Application is to be terminated + coreActive = false; + } + + } } } @@ -2562,49 +2827,45 @@ namespace olc #if defined(OLC_PGE_APPLICATION) && !defined(PGE_IMAGE_IMPLEMENTED) namespace olc { - ImageCPU::ImageCPU(const olc::vi2d& size) : dimensions(size) + /*Image::Image(const olc::vi2d& size, const ImageConfig& cfg) { - pixels.resize(dimensions.area(), olc::Pixel(255, 165, 0)); } - ImageCPU::~ImageCPU() + Image::~Image() + { + }*/ + + bool Image::Create(const olc::vi2d& size, const ImageConfig& cfg) { - pixels.clear(); + dimensions = size; + config = cfg; + pixels.resize(dimensions.area(), olc::Pixel(255, 165, 0)); + return true; } - const olc::vi2d& ImageCPU::Size() const + const olc::vi2d& Image::Size() const { return dimensions; } - olc::Pixel* ImageCPU::Data() + olc::Pixel* Image::Data() { return pixels.data(); } - - Image::Image(const olc::vi2d& size, const bool onCPU, const bool onGPU) + const ImageConfig& Image::GetConfig() const { - // Always create CPU resident image - imCPU = std::make_unique(size); - - if (onGPU) - { - - } - - if (!onCPU) - imCPU.reset(); + return config; } - bool Image::InCPU() const + int32_t Image::GetGPUID() const { - return imCPU != nullptr; + return gpuResourceID; } - bool Image::InGPU() const + void Image::SetGPUID(const int32_t id) { - return imGPU != nullptr; + gpuResourceID = id; } }; diff --git a/dev/src/api_opengl.cpp b/dev/src/api_opengl.cpp index 49e5258d..c1b0b222 100644 --- a/dev/src/api_opengl.cpp +++ b/dev/src/api_opengl.cpp @@ -15,6 +15,7 @@ namespace olc::apis::opengl bool gl::LoadAll() { + // Load all the OpenGL API entry points bLoaded = true; bLoaded &= (glCreateShader = OGL_LOAD(glCreateShader)) != nullptr; bLoaded &= (glCreateProgram = OGL_LOAD(glCreateProgram)) != nullptr; @@ -46,7 +47,58 @@ namespace olc::apis::opengl bLoaded &= (glFrameBufferTexture2D = OGL_LOAD(glFrameBufferTexture2D)) != nullptr; bLoaded &= (glDrawBuffers = OGL_LOAD(glDrawBuffers)) != nullptr; bLoaded &= (glBlendFuncSeparate = OGL_LOAD(glBlendFuncSeparate)) != nullptr; + + if (!bLoaded) + return false; // API load has failed + + + return bLoaded; } + + void gl::glGenTextures(GLsizei n, GLuint* textures) + { + ::glGenTextures(n, textures); + } + + void gl::glBindTexture(GLenum target, GLuint texture) + { + ::glBindTexture(target, texture); + } + + void gl::glTexParameteri(GLenum target, GLenum pname, GLint param) + { + ::glTexParameteri(target, pname, param); + } + + void gl::glTexEnvf(GLenum target, GLenum pname, GLfloat param) + { + ::glTexEnvf(target, pname, param); + } + + void gl::glDeleteTextures(GLsizei n, const GLuint* textures) + { + ::glDeleteTextures(n, textures); + } + + void gl::glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) + { + ::glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); + } + + void gl::glClear(GLbitfield mask) + { + ::glClear(mask); + } + + void gl::glViewport(GLint x, GLint y, GLsizei width, GLsizei height) + { + ::glViewport(x, y, width, height); + } + + void gl::glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) + { + ::glClearColor(red, green, blue, alpha); + } } //! END IMPLEMENTATION \ No newline at end of file diff --git a/dev/src/api_opengl.h b/dev/src/api_opengl.h index d7b4fd7b..d7bd033f 100644 --- a/dev/src/api_opengl.h +++ b/dev/src/api_opengl.h @@ -7,14 +7,15 @@ //! START OPENGL_CONFIG -#define CALLSTYLE + #if OLC_HOST == OLC_HOST_WINDOWS #include #pragma comment(lib, "gdi32.lib") #pragma comment(lib, "opengl32.lib") #include #define CALLSTYLE __stdcall - #define OGL_LOAD(t) (t##_t*)wglGetProcAddress(#t) + // ooof... was getting a bunch of spurious C4191 from MSVC 17.14.9, so round trip via void-town + #define OGL_LOAD(t) reinterpret_cast(reinterpret_cast(wglGetProcAddress(#t))) #endif #if OLC_HOST == OLC_HOST_LINUX_X11 || OLC_HOST == OLC_HOST_LINUX_WAYLAND @@ -40,9 +41,14 @@ #define GL_GLEXT_PROTOTYPES #include #include - #define CALLSTYLE #define GL_CLAMP GL_CLAMP_TO_EDGE #endif + +#if !defined(CALLSTYLE) + #define CALLSTYLE +#endif + + //! END OPENGL_CONFIG //! START DECLARATION @@ -141,6 +147,18 @@ namespace olc glFrameBufferTexture2D_t* glFrameBufferTexture2D = nullptr; glDrawBuffers_t* glDrawBuffers = nullptr; glBlendFuncSeparate_t* glBlendFuncSeparate = nullptr; + + // OpenGL1.2 Proxies (just keeps things tidy imo) + void glGenTextures(GLsizei n, GLuint* textures); + void glBindTexture(GLenum target, GLuint texture); + void glTexParameteri(GLenum target, GLenum pname, GLint param); + void glTexEnvf(GLenum target, GLenum pname, GLfloat param); + void glDeleteTextures(GLsizei n, const GLuint* textures); + void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels); + void glClear(GLbitfield mask); + void glViewport(GLint x, GLint y, GLsizei width, GLsizei height); + void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + }; } diff --git a/dev/src/core.cpp b/dev/src/core.cpp index 563b52a4..22175bb4 100644 --- a/dev/src/core.cpp +++ b/dev/src/core.cpp @@ -68,6 +68,58 @@ namespace olc return false; } + bool PixelGameEngine::CreateImage(olc::Image& image, const olc::vi2d& size, const ImageConfig& cfg) + { + // Create CPU Image + if (!image.Create(size, cfg)) + return false; + + // Create GPU Image + auto id = gpu->CreateTexture(image.Size(), cfg); + if (id == 0) + { + image.Create({ 0,0 }); + return false; + } + + // Associate CPU object with GPU Resource + image.SetGPUID(id); + return true; + } + + bool PixelGameEngine::CreateImageFromFile(olc::Image& image, const std::string& sFileName, const ImageConfig& cfg) + { + return false; + } + + bool PixelGameEngine::CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes, const ImageConfig& cfg) + { + return false; + } + + bool PixelGameEngine::WriteImageToFile(const olc::Image& image, const std::string& sFileName) + { + return false; + } + + bool PixelGameEngine::WriteImageToMemory(const olc::Image& image, std::vector bytes, const std::string& sFileName) + { + return false; + } + + void PixelGameEngine::DestroyImage(olc::Image& image) + { + // If image has gpu resource, remove it + if (image.GetGPUID() != 0) + { + gpu->DeleteTexture(image.GetGPUID()); + image.SetGPUID(0); + } + + // Free any cpu memory associated with image + image.Create({ 0,0 }); + } + bool PixelGameEngine::olc_PrimaryWindowInit() { // Called by window to initialise core systems. Sub windows should ignore this @@ -84,7 +136,24 @@ namespace olc // Initialise GPU Interface - This thread is the context olc::gpu::RendererConfig cfgRenderer; gpu = std::make_unique(); - gpu->CreateDevice({ nullptr }, cfgRenderer); + + // The GPU device can be based upon the primary window configuration. This + // gives us completed gpu and host objects to pass to other windows as and + // when required + gpu->CreateDevice(host->GetHostWindowDescriptor(this), cfgRenderer); + if (gpu->GetLastError() != olc::gpu::RendererError::None) + { + const auto e = gpu->GetLastError(); // For debug visibility + std::cout << "Error: Could not create Renderer\n"; + return; + } + + if (!OnUserCreate()) + { + // Creation process signalled abort + return; + } + // Initialise Input Devices @@ -92,7 +161,7 @@ namespace olc durationFrameCount = 0s; - while (true) + while (coreActive) { // Frame Delta Timing - "ElapsedTime" since last core update // ~~~~~~~~~~~~~~~~~~ @@ -124,13 +193,21 @@ namespace olc SetTitle(sTitle); frameCount = 0; } - - // Primary Window - olc_WindowUpdate(fDT); - - // Child Windows + + // Update Child Windows (if any) for (auto& winChild : deqChildWindows) - winChild->olc_WindowUpdate(fDT); + winChild->olc_WindowUpdate(fDT, gpu.get()); + + // Update Primary Window + olc_WindowUpdate(fDT, gpu.get()); + + // Wait for vertical sync if required. + // Note: Child windows will never vsync as waiting for each buffer swap with vsync + // divides up teh frame rate budget across the windows. + if (gpu->GetConfig().VerticalSync) + { + host->SyncWithDesktopComposite(); + } // Remove child windows that have requested closure if (!deqChildWindows.empty()) @@ -142,6 +219,15 @@ namespace olc } )); } + + // Primary Window + if (olc_ShouldRemove()) + { + // Application is to be terminated + coreActive = false; + } + + } } } diff --git a/dev/src/core.h b/dev/src/core.h index d74d0733..d75429b7 100644 --- a/dev/src/core.h +++ b/dev/src/core.h @@ -65,11 +65,29 @@ namespace olc public: bool Start(); - public: + public: // The "Overridables" + // Called once when the engine is ready and the window is created bool OnUserCreate() override; + // Called every frame while the engine is running bool OnUserUpdate(float fElapsedTime) override; + // Called when something requests the engine shut down bool OnUserDestroy() override; + + public: // olc::Image Handling + // Create an image resource + bool CreateImage(olc::Image& image, const olc::vi2d& size, const ImageConfig& cfg = olc::ImageConfig()); + // Create an image resource based on an image file asset on disk + bool CreateImageFromFile(olc::Image& image, const std::string& sFileName, const ImageConfig& cfg = olc::ImageConfig()); + // Create an image resource based on an image file asset in memory + bool CreateImageFromMemory(olc::Image& image, const uint8_t* data, const size_t bytes, const ImageConfig& cfg = olc::ImageConfig()); + // Store an image as a file asset on disk + bool WriteImageToFile(const olc::Image& image, const std::string& sFileName); + // Store an image as a file asset in memory + bool WriteImageToMemory(const olc::Image& image, std::vector bytes, const std::string& sFileName); + // Destroy an image + void DestroyImage(olc::Image& image); + protected: bool olc_PrimaryWindowInit() override; diff --git a/dev/src/gpu_iface.h b/dev/src/gpu_iface.h index 023a2b77..74a60f46 100644 --- a/dev/src/gpu_iface.h +++ b/dev/src/gpu_iface.h @@ -42,7 +42,8 @@ namespace olc public: // Check/Get last error - const RendererError GetLastError() const { return lastError; } + inline RendererError GetLastError() const { return lastError; } + inline const RendererConfig& GetConfig() const { return config; } public: // Device Stuff // Constructs a GPU Device interface @@ -64,7 +65,24 @@ namespace olc // Makes active the given texture resource (for subsequent rendering operations) virtual bool AssignTextureTarget(const uint32_t slot, const uint32_t texid) = 0; + public: // Shader Construction Stuff + + public: // GPU Task Processing Stuff + virtual bool DoGPUTask(const olc::pgeguts::GPUTask& task) = 0; + + public: // Swap Chain Stuff + // Clears the viewport to a specific colour and depth + virtual bool ClearViewport(const olc::Pixel col, bool bDepth, bool bStencil) = 0; + // Sets the viewport area of the drawing space + virtual bool SetViewport(const olc::vf2d& pos, const olc::vf2d& size) = 0; + // Configures defaults prior to drawing + virtual bool DisplayPrepare() = 0; + // Displays the final output + virtual bool DisplayDraw(bool bVerticalSyncNow = false) = 0; + + protected: + RendererConfig config; RendererError lastError = RendererError::None; }; } diff --git a/dev/src/gpu_opengl33.cpp b/dev/src/gpu_opengl33.cpp index f609c0b4..d9ffd94b 100644 --- a/dev/src/gpu_opengl33.cpp +++ b/dev/src/gpu_opengl33.cpp @@ -5,8 +5,12 @@ namespace olc::gpu { bool Renderer_OGL33::CreateDevice(std::vector params, const RendererConfig& cfg) { + config = cfg; + + // Create OpenGL Device Context #if OLC_HOST == OLC_HOST_WINDOWS + // "wgl*" all live in WinGDI glDeviceContext = GetDC((HWND)(params[0])); PIXELFORMATDESCRIPTOR pfd = @@ -60,18 +64,57 @@ namespace olc::gpu bool Renderer_OGL33::DestroyDevice() { auto& gl = olc::apis::opengl::gl::Get(); + +#if OLC_HOST == OLC_HOST_WINDOWS + wglDeleteContext(glRenderContext); +#endif return false; } uint32_t Renderer_OGL33::CreateTexture(const olc::vi2d& vSize, const olc::ImageConfig& cfg) { auto& gl = olc::apis::opengl::gl::Get(); - return 0; + + // Curiously OpenGL doesnt actually care about the size of the texture + // as part of its creation. This matters later when we Write to texture + // resources on GPU + + uint32_t id = 0; + gl.glGenTextures(1, &id); + gl.glBindTexture(GL_TEXTURE_2D, id); + + if (cfg.Filtered) + { + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + if (cfg.Clamp) + { + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } + else + { + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + +#if OLC_HOST != OLC_HOST_EMSCRIPTEN + gl.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +#endif + return id; } bool Renderer_OGL33::WriteTexture(const uint32_t texid, olc::Image& image) { auto& gl = olc::apis::opengl::gl::Get(); + gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.Size().x, image.Size().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.Data()); return false; } @@ -84,7 +127,8 @@ namespace olc::gpu bool Renderer_OGL33::DeleteTexture(const uint32_t texid) { auto& gl = olc::apis::opengl::gl::Get(); - return false; + gl.glDeleteTextures(1, &texid); + return true; } bool Renderer_OGL33::AssignTextureSource(const uint32_t slot, const uint32_t texid) @@ -98,5 +142,44 @@ namespace olc::gpu auto& gl = olc::apis::opengl::gl::Get(); return false; } + + bool Renderer_OGL33::DoGPUTask(const olc::pgeguts::GPUTask& task) + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + bool Renderer_OGL33::ClearViewport(const olc::Pixel col, bool bDepth, bool bStencil) + { + auto& gl = olc::apis::opengl::gl::Get(); + gl.glClearColor(float(col.r) / 255.0f, float(col.g) / 255.0f, float(col.b) / 255.0f, float(col.a) / 255.0f); + gl.glClear(GL_COLOR_BUFFER_BIT | (bDepth ? GL_DEPTH_BUFFER_BIT : 0) | (bStencil ? GL_STENCIL_BUFFER_BIT : 0)); + return true; + } + + bool Renderer_OGL33::SetViewport(const olc::vf2d& pos, const olc::vf2d& size) + { + auto& gl = olc::apis::opengl::gl::Get(); + gl.glViewport(int(pos.x), int(pos.y), int(size.x), int(size.y)); + return false; + } + + bool Renderer_OGL33::DisplayPrepare() + { + auto& gl = olc::apis::opengl::gl::Get(); + return false; + } + + bool Renderer_OGL33::DisplayDraw(bool bVerticalSyncNow) + { + auto& gl = olc::apis::opengl::gl::Get(); + + +#if OLC_HOST == OLC_HOST_WINDOWS + SwapBuffers(glDeviceContext); +#endif + + return false; + } } //! END IMPLEMENTATION \ No newline at end of file diff --git a/dev/src/gpu_opengl33.h b/dev/src/gpu_opengl33.h index a23c201d..49d57bcb 100644 --- a/dev/src/gpu_opengl33.h +++ b/dev/src/gpu_opengl33.h @@ -30,6 +30,21 @@ namespace olc bool AssignTextureSource(const uint32_t slot, const uint32_t texid) override; // Makes active the given texture resource (for subsequent rendering operations) bool AssignTextureTarget(const uint32_t slot, const uint32_t texid) override; + + + public: // GPU Task Stuff + virtual bool DoGPUTask(const olc::pgeguts::GPUTask& task) override; + + public: // Swap Chain Stuff + // Clears the viewport to a specific colour and depth + virtual bool ClearViewport(const olc::Pixel col, bool bDepth, bool bStencil) override; + // Sets the viewport area of the drawing space + virtual bool SetViewport(const olc::vf2d& pos, const olc::vf2d& size) override; + // Configures defaults prior to drawing + virtual bool DisplayPrepare() override; + // Displays the final output + virtual bool DisplayDraw(bool bVerticalSyncNow) override; + protected: // These may need some thinking about re multiple window olc::apis::opengl::glDeviceContext_t glDeviceContext = 0; diff --git a/dev/src/host_iface.h b/dev/src/host_iface.h index 72232f33..52db6956 100644 --- a/dev/src/host_iface.h +++ b/dev/src/host_iface.h @@ -51,17 +51,22 @@ namespace olc public: // Check/Get last error - const HostError GetLastError() const { return lastError; } + HostError GetLastError() const { return lastError; } public: virtual bool StartSystemEventLoop() = 0; virtual bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen) = 0; virtual bool UpdateWindowFrameTitle(olc::Window* pWindow) = 0; + + virtual std::vector GetHostWindowDescriptor(olc::Window* pWindow) = 0; virtual bool ConnectHostResourceToRenderer() = 0; + // Wait for entire host desktop refresh (for smooooth vsync) + virtual bool SyncWithDesktopComposite() = 0; + protected: HostError lastError = HostError::None; }; diff --git a/dev/src/host_win_winapi.cpp b/dev/src/host_win_winapi.cpp index 971a0561..dad6a113 100644 --- a/dev/src/host_win_winapi.cpp +++ b/dev/src/host_win_winapi.cpp @@ -6,6 +6,8 @@ namespace olc::host // Forward Declaration static LRESULT CALLBACK WINAPI_EventHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + // Windows app needs an event loop somewhere. This is blocking of course. This loop handles + // all windows created for this host. bool Host_Windows_WinAPI::StartSystemEventLoop() { MSG msg; @@ -142,11 +144,21 @@ namespace olc::host return true; } + std::vector Host_Windows_WinAPI::GetHostWindowDescriptor(olc::Window* pWindow) + { + return { mapUID2HWND[pWindow->GetUID()] }; + } + bool Host_Windows_WinAPI::ConnectHostResourceToRenderer() { return false; } + bool Host_Windows_WinAPI::SyncWithDesktopComposite() + { + return DwmFlush() == S_OK; + } + LRESULT Host_Windows_WinAPI::OnWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (!mapHWND2PTR.contains(hWnd)) @@ -155,14 +167,19 @@ namespace olc::host // Get target olc::Window const auto& window = mapHWND2PTR.at(hWnd); + // Many WinAPI events are literally ancient these days, so need some interpretation + // to get to the useful data. + switch (uMsg) { - case WM_MOUSEMOVE: + case WM_MOUSEMOVE: // Mouse has moved within a window { - uint16_t x = lParam & 0xFFFF; - uint16_t y = (lParam >> 16) & 0xFFFF; + // Extract mouse X & Y + uint16_t x = uint16_t(lParam & 0xFFFF); + uint16_t y = uint16_t((lParam >> 16) & 0xFFFF); int16_t ix = *(int16_t*)&x; int16_t iy = *(int16_t*)&y; + // Tell window new mouse location window->olc_OnMouseMove(olc::vi2d{ ix, iy }); return 0; } @@ -232,76 +249,6 @@ namespace olc::host return DefWindowProc(hWnd, uMsg, wParam, lParam); } - // Lives in olc::host namespace - -// switch (uMsg) -// { -// case WM_MOUSEMOVE: -// { -// // Thanks @ForAbby (Discord) -// uint16_t x = lParam & 0xFFFF; uint16_t y = (lParam >> 16) & 0xFFFF; -// int16_t ix = *(int16_t*)&x; int16_t iy = *(int16_t*)&y; -// ptrPGE->olc_UpdateMouse(ix, iy); -// return 0; -// } -// case WM_MOVE: vWinPos = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowPos(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; -// case WM_SIZE: vWinSize = olc::vi2d(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; -// case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0; -// case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0; -// case WM_SETFOCUS: ptrPGE->olc_UpdateKeyFocus(true); return 0; -// case WM_KILLFOCUS: ptrPGE->olc_UpdateKeyFocus(false); return 0; -// case WM_KEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; -// case WM_KEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; -// case WM_SYSKEYDOWN: ptrPGE->olc_UpdateKeyState(int32_t(wParam), true); return 0; -// case WM_SYSKEYUP: ptrPGE->olc_UpdateKeyState(int32_t(wParam), false); return 0; -// case WM_LBUTTONDOWN:ptrPGE->olc_UpdateMouseState(0, true); return 0; -// case WM_LBUTTONUP: ptrPGE->olc_UpdateMouseState(0, false); return 0; -// case WM_RBUTTONDOWN:ptrPGE->olc_UpdateMouseState(1, true); return 0; -// case WM_RBUTTONUP: ptrPGE->olc_UpdateMouseState(1, false); return 0; -// case WM_MBUTTONDOWN:ptrPGE->olc_UpdateMouseState(2, true); return 0; -// case WM_MBUTTONUP: ptrPGE->olc_UpdateMouseState(2, false); return 0; -// case WM_DROPFILES: -// { -// // This is all eww... -// HDROP drop = (HDROP)wParam; -// -// uint32_t nFiles = DragQueryFile(drop, 0xFFFFFFFF, nullptr, 0); -// std::vector vFiles; -// for (uint32_t i = 0; i < nFiles; i++) -// { -// TCHAR dfbuffer[256]{}; -// uint32_t len = DragQueryFile(drop, i, nullptr, 0); -// DragQueryFile(drop, i, dfbuffer, 256); -//#ifdef UNICODE -//#ifdef __MINGW32__ -// char* buffer = new char[len + 1]; -// wcstombs(buffer, dfbuffer, len); -// buffer[len] = '\0'; -//#else -// int count = WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, NULL, 0, NULL, NULL); -// char* buffer = new char[count]; -// WideCharToMultiByte(CP_UTF8, 0, dfbuffer, -1, buffer, count, NULL, NULL); -//#endif -// vFiles.push_back(std::string(buffer)); -// delete[] buffer; -//#else -// vFiles.push_back(std::string(dfbuffer)); -//#endif -// } -// -// // Even more eww... -// POINT p; DragQueryPoint(drop, &p); -// ptrPGE->olc_DropFiles(p.x, p.y, vFiles); -// DragFinish(drop); -// return 0; -// } -// break; -// -// -// case WM_CLOSE: ptrPGE->olc_Terminate(); return 0; -// case WM_DESTROY: PostQuitMessage(0); DestroyWindow(hWnd); return 0; -// } - }; //! END IMPLEMENTATION diff --git a/dev/src/host_win_winapi.h b/dev/src/host_win_winapi.h index ab48ebde..eb6e94af 100644 --- a/dev/src/host_win_winapi.h +++ b/dev/src/host_win_winapi.h @@ -39,7 +39,9 @@ // Embrace MSVC superiority #pragma comment(lib, "user32.lib") #pragma comment(lib, "gdi32.lib") +#pragma comment(lib, "Dwmapi.lib") +#include #include #undef _WINSOCKAPI_ //! END WINAPI_CONFIG @@ -63,12 +65,16 @@ namespace olc public: bool StartSystemEventLoop(); - bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen); - + bool AddWindowFrame(olc::Window* pWindow, const olc::vi2d& vWindowPos, const olc::vi2d& vWindowSize, const bool bFullScreen); bool UpdateWindowFrameTitle(olc::Window* pWindow); + std::vector GetHostWindowDescriptor(olc::Window* pWindow); bool ConnectHostResourceToRenderer(); + + // Wait for entire host desktop refresh (for smooooth vsync) + bool SyncWithDesktopComposite(); + LRESULT OnWindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); std::string test; @@ -76,7 +82,6 @@ namespace olc private: std::unordered_map mapUID2HWND; std::unordered_map mapHWND2PTR; - std::wstring ConvertS2W(std::string s); }; diff --git a/dev/src/image.cpp b/dev/src/image.cpp index 664057b2..7da56313 100644 --- a/dev/src/image.cpp +++ b/dev/src/image.cpp @@ -3,49 +3,45 @@ //! START IMPLEMENTATION namespace olc { - ImageCPU::ImageCPU(const olc::vi2d& size) : dimensions(size) + /*Image::Image(const olc::vi2d& size, const ImageConfig& cfg) { - pixels.resize(dimensions.area(), olc::Pixel(255, 165, 0)); } - ImageCPU::~ImageCPU() + Image::~Image() + { + }*/ + + bool Image::Create(const olc::vi2d& size, const ImageConfig& cfg) { - pixels.clear(); + dimensions = size; + config = cfg; + pixels.resize(dimensions.area(), olc::Pixel(255, 165, 0)); + return true; } - const olc::vi2d& ImageCPU::Size() const + const olc::vi2d& Image::Size() const { return dimensions; } - olc::Pixel* ImageCPU::Data() + olc::Pixel* Image::Data() { return pixels.data(); } - - Image::Image(const olc::vi2d& size, const bool onCPU, const bool onGPU) + const ImageConfig& Image::GetConfig() const { - // Always create CPU resident image - imCPU = std::make_unique(size); - - if (onGPU) - { - - } - - if (!onCPU) - imCPU.reset(); + return config; } - bool Image::InCPU() const + int32_t Image::GetGPUID() const { - return imCPU != nullptr; + return gpuResourceID; } - bool Image::InGPU() const + void Image::SetGPUID(const int32_t id) { - return imGPU != nullptr; + gpuResourceID = id; } }; diff --git a/dev/src/image.h b/dev/src/image.h index a8cd842a..3bba1b93 100644 --- a/dev/src/image.h +++ b/dev/src/image.h @@ -23,45 +23,47 @@ namespace olc { bool Filtered = false; bool Clamp = false; - }; - - class ImageCPU - { - public: - ImageCPU() = default; - ImageCPU(const olc::vi2d& size); - virtual ~ImageCPU(); - - public: - const olc::vi2d& Size() const; - olc::Pixel* Data(); - - public: - olc::vi2d dimensions; - std::vector pixels; - }; - - struct ImageGPU - { - olc::vf2d size; - olc::vf2d invsize; - int32_t resourceID = -1; + bool InRAM = true; + bool InVRAM = true; }; class Image { public: // Constructs a general purpose image - Image(const olc::vi2d& size, const bool onCPU = true, const bool onGPU = true); + Image() = default; + virtual ~Image() = default; + + public: + bool Create(const olc::vi2d& size, const ImageConfig& cfg = olc::ImageConfig()); public: - bool InCPU() const; - bool InGPU() const; + // Returns size (x, y) in pixels + const olc::vi2d& Size() const; + // Returns read/write pointer to start of 1D stream of pixel data + olc::Pixel* Data(); + // Returns how this image was configured upon creation + const ImageConfig& GetConfig() const; + // Return GPU Resource ID + int32_t GetGPUID() const; + // Set GPU Resource ID (0 to eliminate) + void SetGPUID(const int32_t id); + + // True if data mirror has in-front RAM image + bool HasWetCPU() const; + // True if data mirror has in-front VRAM image + bool HasWetGPU() const; + void PrimeCPU(); + void PrimeGPU(); protected: - std::unique_ptr imCPU = nullptr; - std::unique_ptr imGPU = nullptr; + ImageConfig config; + olc::vi2d dimensions; + std::vector pixels; + int32_t gpuResourceID = 0; + bool gpuWet = false; + bool cpuWet = true; }; } #define PGE_IMAGE_DECLARED 1 diff --git a/dev/src/sh_template.h b/dev/src/sh_template.h index 9ce365ce..f334ebcd 100644 --- a/dev/src/sh_template.h +++ b/dev/src/sh_template.h @@ -23,6 +23,8 @@ //! GRAB window.h DECLARATION +//! GRAB gputask.h DECLARATION + //! GRAB gpu_iface.h DECLARATION //! GRAB host_iface.h DECLARATION @@ -49,7 +51,7 @@ //! GRAB gpu_opengl33.h DECLARATION #endif -//! GRAB gputask.h DECLARATION + diff --git a/dev/src/window.cpp b/dev/src/window.cpp index e311d5d7..d1e3e1a0 100644 --- a/dev/src/window.cpp +++ b/dev/src/window.cpp @@ -5,7 +5,7 @@ //! START IMPLEMENTATION namespace olc { - Window::Window() : std::enable_shared_from_this() + Window::Window() { nUniqueID = pgeguts::CreateUID(); } @@ -72,10 +72,13 @@ namespace olc return bShouldRemove; } - bool Window::olc_WindowUpdate(const float fElapsedTime) + bool Window::olc_WindowUpdate(const float fElapsedTime, olc::gpu::Renderer* const gpu) { // Environmental changes + gpu->SetViewport({ 0,0 }, GetSize()); + gpu->ClearViewport(olc::Colour::TANGERINE, false, false); + // User Update if (!OnUserUpdate(fElapsedTime)) { @@ -87,10 +90,13 @@ namespace olc } } - return false; + + gpu->DisplayDraw(); + + return true; } - const size_t Window::GetUID() const + size_t Window::GetUID() const { return nUniqueID; } diff --git a/dev/src/window.h b/dev/src/window.h index 46831784..6139c8c3 100644 --- a/dev/src/window.h +++ b/dev/src/window.h @@ -32,7 +32,12 @@ namespace olc class Host; } - class Window : public std::enable_shared_from_this + namespace gpu + { + class Renderer; + } + + class Window { friend class olc::host::FRIENDLY_HOST; @@ -43,8 +48,11 @@ namespace olc void ConnecToHost(olc::host::Host* host); public: + // Return true if window is to continue virtual bool OnUserCreate(); + // Return true if window is to continue virtual bool OnUserUpdate(float fElapsedTime); + // Return true if window is to close virtual bool OnUserDestroy(); private: @@ -64,10 +72,10 @@ namespace olc public: bool olc_ShouldRemove() const; - bool olc_WindowUpdate(const float fElapsedTime); + bool olc_WindowUpdate(const float fElapsedTime, olc::gpu::Renderer* const gpu); public: - const size_t GetUID() const; + size_t GetUID() const; const olc::vi2d& GetSize() const; bool SetSize(const olc::vi2d& vSize); diff --git a/dev/tests/test_mh.cpp b/dev/tests/test_mh.cpp index 8a08b7b3..c4d64faa 100644 --- a/dev/tests/test_mh.cpp +++ b/dev/tests/test_mh.cpp @@ -9,9 +9,12 @@ class Example : public olc::PixelGameEngine } + olc::Image imTest; + public: bool OnUserCreate() override { + CreateImage(imTest, { 128,128 }); return true; }