diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..ce4cc1b
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,40 @@
+# Use the Google style in this project.
+BasedOnStyle: Google
+
+# Some folks prefer to write "int& foo" while others prefer "int &foo". The
+# Google Style Guide only asks for consistency within a project, we chose
+# "int& foo" for this project:
+DerivePointerAlignment: false
+PointerAlignment: Left
+
+SortIncludes: false
+
+IncludeBlocks: Merge
+IncludeCategories:
+# Matches common headers first, but sorts them after project includes
+- Regex: '^\"google/cloud/internal/disable_deprecation_warnings.inc\"$'
+ Priority: -1
+- Regex: '^\"google/cloud/(internal/|grpc_utils/|testing_util/|[^/]+\.h)'
+ Priority: 1000
+- Regex: '^\"google/cloud/' # project includes should sort first
+ Priority: 500
+- Regex: '^\"'
+ Priority: 1500
+- Regex: '^'
+ Priority: 5000
+
+# Format raw string literals with a `pb` or `proto` tag as proto.
+RawStringFormats:
+- Language: TextProto
+ Delimiters:
+ - 'pb'
+ - 'proto'
+ BasedOnStyle: Google
+
+CommentPragmas: '(@copydoc)'
\ No newline at end of file
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..b63d312
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,5 @@
+*.elf
+.git/
+.vscode/
+docs/
+tools/
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..9103071
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Ignore all differences in line endings
+* -crlf
\ No newline at end of file
diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml
deleted file mode 100644
index 1e35fb2..0000000
--- a/.github/workflows/compilation.yml
+++ /dev/null
@@ -1,60 +0,0 @@
-# Based on https://github.com/ps2dev/gsKit/blob/master/.github/workflows/compilation.yml
-# Thanks fjtrujy!
-
-name: CI
-
-on:
- push:
- pull_request:
- repository_dispatch:
- types: [run_build]
-
-jobs:
- build:
- runs-on: ubuntu-latest
- container: ps2dev/ps2sdk-ports:latest
- steps:
- - uses: actions/checkout@v2
-
- - name: Install dependencies
- run: |
- apk add build-base git
-
- - name: Compile engine
- working-directory: ./
- env:
- ZLIB: ${PS2SDK}/ports
- LIBTIFF: ${PS2SDK}/ports
- LIBJPEG: ${PS2SDK}/ports
- LIBPNG: ${PS2SDK}/ports
- TYRA: ${GITHUB_WORKSPACE}
- run: |
- make
-
- - name: Compile unit test project
- working-directory: ./
- env:
- TYRA: ${GITHUB_WORKSPACE}
- run: |
- make tests
-
- - name: Compile cube project
- working-directory: ./
- env:
- TYRA: ${GITHUB_WORKSPACE}
- run: |
- make cube
-
- - name: Compile floors project
- working-directory: ./
- env:
- TYRA: ${GITHUB_WORKSPACE}
- run: |
- make floors
-
- - name: Compile dolphin project
- working-directory: ./
- env:
- TYRA: ${GITHUB_WORKSPACE}
- run: |
- make dolphin
diff --git a/.gitignore b/.gitignore
index 489b289..77ef3f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,38 +1,33 @@
-.vscode/
-.idea/
-
-# ---
-
-# Prerequisites
-*.d
-
-# Compiled Object files
-*.slo
-*.lo
-*.o
-
-# Precompiled Headers
-*.gch
-*.pch
-
-# Compiled Dynamic libraries
-*.so
-*.dylib
-*.dll
-
-# Fortran module files
-*.mod
-*.smod
-
-# Compiled Static libraries
-*.lai
-*.la
-*.a
-*.lib
-
-# Executables
-*.exe
-*.out
-*.app
-*.erl
-*.elf
+# ---
+
+# Prerequisites
+*.d
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.vsm
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+*.erl
+*.elf
+*.irx
diff --git a/.vscode/.gitignore b/.vscode/.gitignore
new file mode 100644
index 0000000..dae89fb
--- /dev/null
+++ b/.vscode/.gitignore
@@ -0,0 +1,6 @@
+*.log
+
+# Configuration files
+/c_cpp_properties.json
+/tasks.json
+/settings.json
diff --git a/.vscode/configurations/windows-docker/c_cpp_properties.json b/.vscode/configurations/windows-docker/c_cpp_properties.json
new file mode 100644
index 0000000..57181bd
--- /dev/null
+++ b/.vscode/configurations/windows-docker/c_cpp_properties.json
@@ -0,0 +1,41 @@
+{
+ "configurations": [
+ {
+ "name": "Docker PS2SDK",
+ "includePath": [
+ "${workspaceFolder}/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/ps2sdk/common/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/ps2sdk/ee/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/ps2sdk/iop/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/ps2sdk/ports/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/ee/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/ee/lib/gcc/mips64r5900el-ps2-elf/11.3.0/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/ee/lib/gcc/mips64r5900el-ps2-elf/11.3.0/include-fixed/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/ee/lib/gcc/mips64r5900el-ps2-elf/11.3.0/install-tools/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/ee/lib/gcc/mips64r5900el-ps2-elf/11.3.0/plugin/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/ee/mips64r5900el-ps2-elf/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/ee/mips64r5900el-ps2-elf/sys-include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/gsKit/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/iop/lib/gcc/mipsel-ps2-elf/11.3.0/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/iop/lib/gcc/mipsel-ps2-elf/11.3.0/install-tools/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/iop/lib/gcc/mipsel-ps2-elf/11.3.0/plugin/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/iop/lib/gcc/mipsel-ps2-elf/11.3.0/include-fixed/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/iop/lib/gcc/mipsel-ps2-irx/11.3.0/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/iop/lib/gcc/mipsel-ps2-irx/11.3.0/install-tools/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/iop/lib/gcc/mipsel-ps2-irx/11.3.0/plugin/include/**",
+ "C:/Users/YOUR-INTELLISENSE-PATH/ps2dev-intellisense/iop/lib/gcc/mipsel-ps2-irx/11.3.0/include-fixed/**"
+ ],
+ "defines": [
+ "_EE",
+ "_IOP",
+ "__SPU__",
+ "INTELLISENSE"
+ ],
+ "cppStandard": "c++20",
+ "compilerPath": "", // Should be empty
+ "intelliSenseMode": "linux-gcc-x86",
+ "configurationProvider": "ms-vscode.cpptools"
+ }
+ ],
+ "version": 4
+}
\ No newline at end of file
diff --git a/.vscode/configurations/windows-docker/settings.json b/.vscode/configurations/windows-docker/settings.json
new file mode 100644
index 0000000..56568d9
--- /dev/null
+++ b/.vscode/configurations/windows-docker/settings.json
@@ -0,0 +1,14 @@
+{
+ "buildDirectory": "demo",
+ "C_Cpp.default.intelliSenseMode": "gcc-x86",
+ "files.associations": {
+ "*.vcl": "asm-collection",
+ "*.i": "asm-collection",
+ "*.vsm": "asm-collection",
+ "*.vclpp": "asm-collection",
+ "*.hpp": "cpp",
+ "*.cpp": "cpp",
+ "*.h": "c",
+ "*.c": "c"
+ }
+}
\ No newline at end of file
diff --git a/.vscode/configurations/windows-docker/tasks.json b/.vscode/configurations/windows-docker/tasks.json
new file mode 100644
index 0000000..d5c81c8
--- /dev/null
+++ b/.vscode/configurations/windows-docker/tasks.json
@@ -0,0 +1,119 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Build & run",
+ "dependsOrder": "sequence",
+ "dependsOn": [
+ "1. Copy source code to docker volume",
+ "2. Compile via docker container",
+ "3. Copy binaries to host",
+ "4. Run PCSX2"
+ ],
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ },
+ {
+ "type": "shell",
+ "label": "1. Copy source code to docker volume",
+ "command": "docker exec -t tyra-compiler sh -c 'rsync -ac --delete --exclude \".git\" --exclude \"docs\" --exclude \".vscode\" --exclude \"tools\" --exclude \"obj\" --exclude \"bin\" /host/ /src/'"
+ },
+ {
+ "type": "shell",
+ "label": "2. Compile via docker container",
+ "command": "docker exec -t tyra-compiler sh -c 'cd ${config:buildDirectory} && make build-engine all'",
+ "problemMatcher": {
+ "base": "$gcc",
+ "fileLocation": [
+ "autoDetect"
+ ]
+ }
+ },
+ {
+ "type": "shell",
+ "label": "3. Copy binaries to host",
+ "command": "docker exec -t tyra-compiler sh -c 'rsync -zac --include=\"*/\" --include=\"bin/**\" --exclude=\"*\" /src/ /host/'"
+ },
+ {
+ "type": "shell",
+ "label": "4. Run PCSX2",
+ "options": {
+ "cwd": "${config:buildDirectory}"
+ },
+ "command": "./run.ps1"
+ },
+ {
+ "type": "shell",
+ "label": "Convert WAV to ADPCM",
+ "dependsOn": [
+ "1. Copy source code to docker volume"
+ ],
+ "command": "docker exec -t tyra-compiler sh -c 'adpenc ${input:wav2adpcmSource} ${input:wav2adpcmTarget} && rsync -zac /src/${input:wav2adpcmTarget} /host/${input:wav2adpcmTarget}'"
+ },
+ {
+ "type": "shell",
+ "label": "Cleanup",
+ "command": "docker exec -t tyra-compiler sh -c 'rm -rf /src/*'"
+ },
+ {
+ "type": "shell",
+ "label": "Send to PS2",
+ "command": "docker exec -t tyra-compiler sh -c 'cd ${config:buildDirectory}/bin && killall -v ps2client || true && ps2client -h ${input:ps2Ip} reset && ps2client -h ${input:ps2Ip} reset && ps2client -h ${input:ps2Ip} execee host:$(find . -maxdepth 1 -type f -name \"*.elf\")'"
+ },
+ {
+ "type": "shell",
+ "label": "Open PS2 output logger",
+ "command": "docker stop ps2-logs || true && docker run -t --rm --name=ps2-logs -p ${input:local2Ip}:18194:18194/udp h4570/tyra sh -c 'ps2client -h ${input:ps2Ip} listen'",
+ "runOptions": {
+ "instanceLimit": 1
+ },
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
+ }
+ },
+ {
+ "type": "shell",
+ "label": "Start docker container",
+ "command": "docker compose up -d"
+ },
+ {
+ "type": "shell",
+ "label": "Stop docker container",
+ "command": "docker compose down"
+ },
+ {
+ "type": "shell",
+ "label": "[Debug] Copy obj directory to host",
+ "command": "docker exec -t tyra-compiler sh -c 'rsync -zac --include=\"*/\" --include=\"obj/**\" --exclude=\"*\" /src/ /host/'"
+ }
+ ],
+ "inputs": [
+ {
+ "id": "wav2adpcmSource",
+ "description": "Source file path (ex. ):",
+ "default": "demo/res/sound.wav",
+ "type": "promptString"
+ },
+ {
+ "id": "wav2adpcmTarget",
+ "description": "Target file path (ex. ):",
+ "default": "demo/res/sound.adpcm",
+ "type": "promptString"
+ },
+ {
+ "id": "ps2Ip",
+ "description": "PS2link ip address:",
+ "default": "192.168.1.200",
+ "type": "promptString"
+ },
+ {
+ "id": "local2Ip",
+ "description": "Local ip address:",
+ "default": "192.168.1.245",
+ "type": "promptString"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..83ad6c7
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,8 @@
+{
+ "recommendations": [
+ "ms-vscode.cpptools-extension-pack",
+ "ryzngard.vscode-header-source",
+ "maziac.asm-code-lens",
+ "dmnsgn.vscode-wavefront"
+ ]
+}
\ No newline at end of file
diff --git a/CHANGELOG.MD b/CHANGELOG.MD
deleted file mode 100644
index 3f1f18e..0000000
--- a/CHANGELOG.MD
+++ /dev/null
@@ -1,134 +0,0 @@
-# Changelog
-All notable changes to this project will be documented in this file.
-
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
-and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-
-## [1.36.3] - 2021-05-28
-
-### Added
-- Unit test project
-- Assert
-- CI via Github workflows
-- Install tutorial as separated .MD file
-
-### Changed
-- Improved BMP loader.
-- All PRINT_LOG and PRINT_ERR to consoleLog() and assertMsg()
-- Refactored makefiles
-- **CHANGED ENV VARIABLES!**
-- Install tutorial
-- Readme
-
-## [1.31.2] - 2021-02-06
-
-### Added
-- Id to MeshFrame
-- Non textured rendering (RGBA only)
-
-### Changed
-- Moved color from mesh to mesh material.
-- OBJ Loader refactor
-- Logic of loadFrom()
-- Migrated to GCC9 (valid with latest PS2SDK)
-
-### Removed
-- Removed MeshId from TextureLink, TextureRepository
-
-## [1.29.1] - 2020-12-27
-### Added
-- Created libpacket2 in PS2SDK
-- Dolphin sample
-- 2D support (Sprite class)
-- Alpha blending (transparency)
-- PNG support
-- ADPCM support in audio class
-- AudioFinish event in audio class
-- BoundingBox class
-- A lot of useful funcs in Matrix class
-- Mock of file service
-- Array mode to draw() (highly optimized for small meshes)
-- VCLPP - Usage of VU1 Preprocessor
-
-### Changed
-- Project setup (makefile mostly)
-- Readme
-- Switched into libpacket2 in `vifsender` and `gifsender`
-- Timer class refactor
-- Engine base refactor
-- MeshTexture into Texture
-- Refactored floors sample
-- Refactored Audio class
-- Fixed Path 3 lighting
-- Refactored, optimized math classes (vector, matrix..)
-- Cleanup in camera base
-- Refactored Path 3 and Path 1 rendering
-- Synchronization functions (`renderer->waitForRender()`)
-- BMP class refactor
-- Dff Loader refactor
-- Optimized rendering data preparation
-
-### Removed
-- Ari sample
-- VU1 class
-
-## [1.1.0] - 2020-11-03
-### Added
-- A lot of useful functions to Mesh class
-- MeshFrame class
-- MeshMaterial class
-- MeshTexture class
-- TextureLink struct
-- Texture wrapping settings
-- TextureRepository class
-- PRINT_ERR()
-- Ari sample
-
-### Changed
-- Project setup (gitignore etc.)
-- Readme
-- DFF loader refactor
-- OBJ loader refactor
-- MD2 loader refactor
-- 3D data classes refactor (one interface for all)
-- Rendering refactor
-
-### Removed
-- MeshSpec class
-- Texture class
-- Md2Model class
-- ObjModel class
-- DffModel class
-
-## [1.0.0] - 2020-10-28
-### Added
-- Project setup (gitignore etc.)
-- License
-- Readme
-- BMP support
-- DFF support
-- MD2 support
-- OBJ support
-- Animation support
-- Engine base
-- AudioListener class
-- Rendering base
-- Path 1 renderer (VU1)
-- Path 3 renderer (GIF)
-- Math classes (matrix, vector, plane..)
-- Audio support (bg song only)
-- Camera base
-- Base lighting support (bugged) for Path 3
-- Pad support
-- EE Timer
-- Math class
-- String class
-- Mesh class with AABB
-- Makefile
-- Primitive floors sample
-
-[1.36.3]: https://github.com/h4570/tyra/compare/v1.31.2-alpha...v1.36.3-alpha
-[1.31.2]: https://github.com/h4570/tyra/compare/v1.29.1-alpha...v1.31.2-alpha
-[1.29.1]: https://github.com/h4570/tyra/compare/1.1.0...v1.29.1-alpha
-[1.1.0]: https://github.com/h4570/tyra/compare/1.0.0...1.1.0
-[1.0.0]: https://github.com/h4570/tyra/compare/1.0.0
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..dfd1c4e
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,67 @@
+# syntax=docker/dockerfile:1
+FROM ubuntu:20.04
+
+# Install necessary packages for PS2DEV, VCLPP and VCL.
+RUN apt-get update
+ARG DEBIAN_FRONTEND=noninteractive
+RUN apt-get install -y git make g++ texinfo bison flex gettext libgmp3-dev \
+ libmpfr-dev libmpc-dev gcc binutils cmake wget patch zlib1g-dev libgsl-dev \
+ unzip
+
+# Setup PS2DEV env
+ENV PS2DEV /usr/local/ps2dev
+RUN mkdir -p $PS2DEV
+RUN chown -R $USER: $PS2DEV
+ENV PS2SDK $PS2DEV/ps2sdk
+ENV GSKIT $PS2DEV/gsKit
+ENV PATH $PATH:${PS2DEV}/bin:${PS2DEV}/ee/bin:${PS2DEV}/iop/bin:${PS2DEV}/dvp/bin:${PS2SDK}/bin
+
+# Compile PS2DEV
+RUN mkdir -p /temp/ps2dev
+RUN git clone https://github.com/ps2dev/ps2dev.git /temp/ps2dev
+WORKDIR "/temp/ps2dev"
+RUN ./build-all.sh
+
+# Compile VCLPP
+RUN mkdir -p /temp/vclpp
+RUN git clone https://github.com/glampert/vclpp.git /temp/vclpp
+WORKDIR "/temp/vclpp"
+RUN make
+
+# Download VCL
+RUN mkdir -p /temp/vcl
+WORKDIR "/temp/vcl"
+RUN wget http://apgcglz.cluster028.hosting.ovh.net/tyra/vcl.zip
+RUN unzip vcl.zip
+
+# ------------------------------------------------------------------------------
+
+# Start from clean image
+FROM ubuntu:20.04
+
+# Set ENV variables
+ENV PS2DEV /usr/local/ps2dev
+ENV PS2SDK $PS2DEV/ps2sdk
+ENV PATH $PATH:${PS2DEV}/bin:${PS2DEV}/ee/bin:${PS2DEV}/iop/bin:${PS2DEV}/dvp/bin:${PS2SDK}/bin
+
+# Copy stuff from previous stage
+COPY --from=0 ${PS2DEV} ${PS2DEV}
+COPY --from=0 /temp/vcl/vcl /usr/bin/vcl
+COPY --from=0 /temp/vclpp/vclpp /usr/bin/vclpp
+
+# Install packages for emulating x86 and Tyra (make, libmpc, psmisc)
+RUN apt-get update
+RUN apt-get install -y make rsync libmpc-dev qemu qemu-user-static binfmt-support psmisc
+
+# Add 32bit executables support for VCL
+RUN dpkg --add-architecture i386
+RUN apt-get update
+RUN apt-get install -y libstdc++5:i386
+RUN update-binfmts --install i386 /usr/bin/qemu-i386-static --magic '\x7fELF\x01\x01\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x03\x00\x01\x00\x00\x00' --mask '\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xf8\xff\xff\xff\xff\xff\xff\xff'
+
+# Set chmod
+RUN chmod 755 /usr/bin/vclpp
+RUN chmod 755 /usr/bin/vcl
+
+WORKDIR /src
+CMD ["/bin/bash"]
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 261eeb9..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/Makefile b/Makefile
deleted file mode 100644
index e6ff12f..0000000
--- a/Makefile
+++ /dev/null
@@ -1,97 +0,0 @@
-# ______ ____ ___
-# | \/ ____| |___|
-# | | | \ | |
-#-----------------------------------------------------------------------
-# Copyright 2021, tyra - https://github.com/h4570/tyra
-# Licenced under Apache License 2.0
-# André Guilherme
-
-TYRA_DIR = ./
-
-LIB_NAME = libtyra.a
-
-# ENGINE OBJECTS
-ENGINE_OBJS = src/engine/engine.o \
- src/engine/modules/audio.o \
- src/engine/modules/camera_base.o \
- src/engine/modules/file_service.o \
- src/engine/modules/gif_sender.o \
- src/engine/modules/light.o \
- src/engine/modules/pad.o \
- src/engine/modules/renderer.o \
- src/engine/modules/texture_repository.o \
- src/engine/modules/timer.o \
- src/engine/modules/vif_sender.o \
- src/engine/models/math/matrix.o \
- src/engine/models/math/plane.o \
- src/engine/models/math/point.o \
- src/engine/models/math/vector3.o \
- src/engine/models/mesh_frame.o \
- src/engine/models/bounding_box.o \
- src/engine/models/mesh_material.o \
- src/engine/models/mesh.o \
- src/engine/models/sprite.o \
- src/engine/models/texture.o \
- src/engine/utils/math.o \
- src/engine/utils/string.o \
- src/engine/loaders/bmp_loader.o \
- src/engine/loaders/dff_loader.o \
- src/engine/loaders/md2_loader.o \
- src/engine/loaders/obj_loader.o \
- src/engine/loaders/png_loader.o \
- src/engine/vu1_progs/draw3D.o \
-
-EE_LIBS := $(EE_LIBS) -ldraw -lcdvd -lgraph -lmath3d -lpacket -ldma -lpacket2 -lpad -laudsrv -lc -lstdc++ -lpng -lz
-
-EE_INCS := -I$(TYRA)/src/engine/include -I$(PS2SDK)/ports/include $(EE_INCS)
-
-EE_LDFLAGS := -L$(PS2SDK)/ports/lib -L$(TYRA)/src/engine $(EE_LDFLAGS)
-
-EE_CXXFLAGS := -DHAVE_LIBJPEG -DHAVE_LIBTIFF -DHAVE_LIBPNG -DHAVE_ZLIB -D_XCDVD $(EE_CXXFLAGS)
-
-EE_DVP = dvp-as
-
-EE_VCL = vcl
-
-EE_VCLPP = vclpp
-
-all: $(ENGINE_OBJS)
- ar rcs $(LIB_NAME) $(ENGINE_OBJS)
- cp -f $(LIB_NAME) $(PS2SDK)/ee/lib
- rm -f $(ENGINE_OBJS)
-
-# Cube example
-cube:
- $(MAKE) -C src/samples/cube all clean
-
-# Dolphin example
-dolphin:
- $(MAKE) -C src/samples/dolphin all clean
-
-# Floors example
-floors:
- $(MAKE) -C src/samples/floors all clean
-
-# Rebuild the engine
-rebuild-engine:
- $(MAKE) -C src/engine && make && make EE_CXXFLAGS="-DNDEBUG $(EE_CXXFLAGS)"
-
-# Rebuild debug engine
-rebuild-dbg-engine:
- $(MAKE) -C src/engine && make && make
-
-# Unit tests folder
-tests:
- $(MAKE) -C src/unit_tests all clean
-
-# %.vcl: %.vclpp
-# $(EE_VCLPP) $< $@
-
-# %.vsm: %.vcl
-# $(EE_VCL) $< >> $@
-
-%.o: %.vsm
- $(EE_DVP) $< -o $@
-
-include $(PS2SDK)/samples/Makefile.pref
-include $(PS2SDK)/samples/Makefile.eeglobal
\ No newline at end of file
diff --git a/Makefile.base b/Makefile.base
new file mode 100644
index 0000000..e31c0b7
--- /dev/null
+++ b/Makefile.base
@@ -0,0 +1,117 @@
+#Compiler and Linker
+EE_PREFIX := mips64r5900el-ps2-elf-
+AR := $(EE_PREFIX)ar
+AS := $(EE_PREFIX)as
+CC := $(EE_PREFIX)gcc
+CXX := $(EE_PREFIX)g++
+STRIP := $(EE_PREFIX)strip
+BIN2S := $(PS2SDK)/bin/bin2s
+VCL := vcl
+VCLPP := vclpp
+DVP := dvp-as
+
+IRXEMBEDDEDEXT := irx-em
+
+#Flags, Libraries and Includes
+CFLAGS += -D_EE -Wall -O3
+LINKFLAGS += -O3 -Wl,-zmax-page-size=128
+ASFLAGS += -O3
+LIB += -lstdc++ -ldma -lpacket2 -lgraph -ldraw -lmath3d -lpng -lz -lpad -laudsrv -lpatches -lcdvd
+INC += -I$(PS2DEV)/ps2sdk/ee/include -I$(PS2DEV)/ps2sdk/common/include -I$(PS2DEV)/ps2sdk/ports/include
+
+#---------------------------------------------------------------------------------
+#DO NOT EDIT BELOW THIS LINE
+#---------------------------------------------------------------------------------
+SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
+OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
+
+IRXEM_SOURCES := $(shell find $(SRCDIR) -type f -name *.$(IRXEMBEDDEDEXT))
+IRXEM_OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(IRXEM_SOURCES:.$(IRXEMBEDDEDEXT)=.$(OBJEXT)))
+
+VU_SOURCES_VCL := $(shell find $(SRCDIR) -type f -name *.$(VCLEXT))
+VU_OBJECTS_VCL := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(VU_SOURCES_VCL:.$(VCLEXT)=.$(OBJEXT)))
+
+VU_SOURCES_VSM := $(shell find $(SRCDIR) -type f -name *.$(VSMEXT))
+VU_OBJECTS_VSM := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(VU_SOURCES_VSM:.$(VSMEXT)=.$(OBJEXT)))
+
+VCL_SOURCES := $(shell find $(SRCDIR) -type f -name *.$(VCLPPEXT))
+VCL_OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(VCL_SOURCES:.$(VCLPPEXT)=.$(OBJEXT)))
+
+#Default Make
+all: resources $(TARGET)
+#all: resources irxcopy $(TARGET)
+
+release:
+ $(MAKE) CFLAGS="-DNDEBUG $(CFLAGS)"
+
+#Remake
+remake: cleaner all
+
+#Copy Resources from Resources Directory to Target Directory
+resources: directories
+ @cp -r $(RESDIR)/* $(TARGETDIR)/ || true
+
+#Make the Directories
+directories:
+ @mkdir -p $(TARGETDIR)
+ @mkdir -p $(BUILDDIR)
+
+#Clean only Objects
+clean:
+ @$(RM) -rf $(BUILDDIR)
+
+#Full Clean, Objects and Binaries
+cleaner: clean
+ @$(RM) -rf $(TARGETDIR)
+ @$(RM) -rf $(BUILDDIR)
+
+#irxcopy:
+# @cp $(PS2SDK)/iop/irx/audsrv.irx $(TARGETDIR)/audsrv.irx
+
+#Pull in dependency info for *existing* .o files
+-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))
+
+#Link
+$(TARGET): $(OBJECTS) $(VCL_OBJECTS) $(VU_OBJECTS_VCL) $(VU_OBJECTS_VSM) $(IRXEM_OBJECTS)
+ $(CXX) -T$(PS2DEV)/ps2sdk/ee/startup/linkfile -o $(TARGETDIR)/$(TARGET) $^ -L$(PS2DEV)/ps2sdk/ee/lib -L$(PS2SDK)/ports/lib $(LINKFLAGS) $(LIBDIRS) $(LIB)
+ $(STRIP) --strip-all $(TARGETDIR)/$(TARGET)
+
+$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(IRXEMBEDDEDEXT)
+ @mkdir -p $(dir $@)
+ @rm -f $@.s
+ $(BIN2S) $(shell head -1 $<) $@.s $(shell sed -n '2p' $<)
+ $(AS) $(ASFLAGS) $@.s -o $@
+
+$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
+ @mkdir -p $(dir $@)
+ $(CXX) $(CFLAGS) $(INC) -c -o $@ $<
+ @$(CXX) $(CFLAGS) $(INCDEP) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT)
+ @cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp
+ @sed -e 's|.*:|$(BUILDDIR)/$*.$(OBJEXT):|' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT)
+ @sed -e 's/.*://' -e 's/\\$$//' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILDDIR)/$*.$(DEPEXT)
+ @rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp
+
+$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(VCLEXT)
+ @mkdir -p $(dir $@)
+ @rm -f $@.vsm
+ $(VCL) $< >> $@.vsm
+ $(DVP) $@.vsm -o $@
+
+$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(VSMEXT)
+ @mkdir -p $(dir $@)
+ $(DVP) $< -o $@
+
+$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(VCLPPEXT)
+ @mkdir -p $(dir $@)
+ @rm -f $@.vcl
+ @rm -f $@.vsm
+ $(VCLPP) $< $@.vcl
+ $(VCL) $@.vcl > $@.vsm
+ $(DVP) $@.vsm -o $@
+
+run-pcsx2:
+ taskkill.exe /f /t /im pcsx2x64.exe || true
+ $(WSL_LINUX_PCSX2)/pcsx2x64.exe --elf=$(WSL_MAKE_WINDOWS)$(CURDIR)/$(TARGETDIR)/$(TARGET)
+
+#Non-File Targets
+.PHONY: all remake clean cleaner resources
\ No newline at end of file
diff --git a/README.MD b/README.MD
index 494b6aa..6d604c6 100644
--- a/README.MD
+++ b/README.MD
@@ -1,190 +1,167 @@
-[![Contributors][contributors-shield]][contributors-url]
-[![MIT License][license-shield]][license-url]
-[](https://discord.gg/NhhTmg3Gm5)
-[](https://discord.gg/7dCr6ThSN5)
-
-
-
-
-
-
-
-
Tyra - alpha 1.36.3
-
-
- Open source game engine for PlayStation 2™
-
- Dolphin sample »
-
- Floors sample »
-
-
- Report Bug
- ·
- Request Feature
-
-
-
-[](https://github.com/h4570/tyra/stargazers)
-
-## Table of Contents
-
-* [About the Project](#about-the-project)
- * [Samples](#samples)
- * [Description](#description)
- * [Features](#features)
- * [Branches](#branches)
-* [Getting Started](#getting-started)
- * [Installation](#installation)
- * [Prerequisites](#prerequisites)
-* [Roadmap](#roadmap)
-* [Contributing](#contributing)
-* [License](#license)
-* [Built With](#built-with)
-* [Acronyms](#acronyms)
-* [Thanks](#thanks)
-
-
-
-## About the Project
-
-
-
-
-### Samples
-* [Dolphin](https://github.com/h4570/tyra/tree/master/src/samples/dolphin)
-* [Floors](https://github.com/h4570/tyra/tree/master/src/samples/floors)
-* [Cube](https://github.com/h4570/tyra/tree/master/src/samples/cube)
-
-### Description
-Tyra is a project that aims to facilitate the development of PlayStation 2 games. The goal is simple API which will allow you to develop some nice small homebrew games in a short period of time. Finally, (thanks to PS2DEV team) Tyra supports C++20, so we are free from the 2003's GCC which only supported C++98.
-**Project is on alpha stage, so keep in mind that there are many things to be done and to fix.**
-Unfortunately there are not many people, which want's to help with this project. **If you want to be a volunteer**, but don't have too much knowledge in this field, write to me on Discord, **I will be happy to help.**
-
-When project will be on beta stage, we plan to create some YouTube tutorials :)
-
-**Why was this project created?**
-PS2 development can be a big challenge.
-PS2 it's pretty low-level and there are a lot of stuff that you must do from scratch.
-For example, if you want to render something using only PS2DEV you must:
-- Create VU1 micro-program
-- Load micro-program and send it to VU1
-- Create parser for your 3D file
-- Create parser for texture file
-- Calculate lights, shadows, MVP matrices (3D->2D)..
-- Clip triangles ([sic!](https://github.com/h4570/tyra/issues/14))
-- Prepare DMA packets for coprocessors communication
-- Setup rasterizer (GS)
-- Send texture to GS
-- Send packets
-- Synchronize all
-
-but with Tyra you can do it with methods like `loadObj()` and `draw()` 😁.
-
-### Features
-* 3D support - VU1 "Path 1 rendering"
-* 2D support - PNG & BMP
-* Frustum culling, backface culling
-* OpenGL adaptions: Perspective projection, lookAt camera
-* Texture support - .bmp and png
-* Mesh loaders: ".obj", ".dff" (RenderWare, GTA:SA) and ".md2" (Quake II)
-* Threading support
-* Animation support for obj and md2
-* Audio support - .wav and .adpcm
-* Pad support
-
-### Branches
-* Master - monthly releases - tested on PS2 & PCSX2.
-* Develop - daily builds - can be unstable.
-
-
-
-## Getting Started
-
-### Installation
-Step by step [tutorial here](https://github.com/h4570/tyra/tree/develop/docs)
-If you need any support, feel free to ask questions on discord:
-[](https://discord.gg/NhhTmg3Gm5)
-
-### Prerequisites
-
-* PS2 DEV environment - [PS2DEV](https://github.com/ps2dev/ps2dev)
-* At least #58ffb33 PS2SDK (28.05.2021) - [PS2SDK](https://github.com/ps2dev/ps2sdk)
-* PS2 Emulator. For example [PCSX2](https://pcsx2.net/)
-
-
-
-## Extra info
-* If you want to use your own .obj file, please check "triangulate faces" in blender.
-PS2 is supporting only triangle primitive type.
-
-
-
-## Roadmap
-
-### Roadmap for next master release:
-[Next release roadmap](https://github.com/h4570/tyra/projects/4)
-
-See the [open issues](https://github.com/h4570/tyra/issues) for a list of proposed features (and known issues).
-
-
-
-## Contributing
-
-Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
-
-1. Fork the Project
-2. Create your Feature Branch (`git checkout -b feature/amazing-feature`)
-3. Commit your Changes (`git commit -m 'Add some amazing-feature'`)
-4. Push to the Branch (`git push origin feature/amazing-feature`)
-5. Open a Pull Request
-
-
-
-## License
-
-Distributed under the Apache License 2.0 License. See `LICENSE` for more information.
-
-
-
-## Built With
-
-* [PS2DEV](https://github.com/ps2dev/ps2dev)
-
-
-
-## Acronyms
-* EE (Emotion Engine) - whole set of processors and subsystems that make up the PlayStation 2
-* GS (Graphic synthesizer) - configurable rasterizer and texture mapper. Has only 2MB~ memory for textures, but for the opposite have very high transfer rate, so you can switch textures in the blink
-* VU0 - programmable (ASM) vector processor that is accessed by inline assembly code injected into C program.
-* VU1 - programmable (ASM) vector processor, which is similar to vertex shader. Accessed via DMA.
-* IOP - I/O processor which enables access to peripheral devices, such the game controller
-
-
-
-## Thanks
-
-Without these guys, everything would be harder:
-* Dr Henry Fortuna - for code sources
-* Rick Gaiser, fjtrujy - for help and new GCC!
-* Lukasz D.K. - for huge archive of PS2 stuff
-* Guilherme Lampert - for code sources
-* Jesper Svennevid, Daniel Collin - for openvcl's code samples
-* Whole PS2DEV team
-* Manieq - for nice splash screens!
-* And so many other guys. Thanks!
-
-Project Link: [https://github.com/h4570/tyra](https://github.com/h4570/tyra)
-Sandro Sobczyński - sandro.sobczynski@gmail.com
-[![LinkedIn][linkedin-shield]](https://linkedin.com/in/sandro-sobczyński-28820b15a)
-
-
-
-
-
-
-[contributors-shield]: https://img.shields.io/github/contributors/h4570/tyra.svg?style=flat-square
-[contributors-url]: https://github.com/h4570/tyra/graphs/contributors
-[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555
-[license-shield]: https://img.shields.io/github/license/h4570/tyra.svg?style=flat-square
-[license-url]: https://github.com/h4570/tyra/blob/master/LICENSE
+[![Contributors][contributors-shield]][contributors-url]
+[![MIT License][license-shield]][license-url]
+[](https://discord.gg/NhhTmg3Gm5)
+[](https://discord.gg/7dCr6ThSN5)
+
+
+
+
+
+
+
+
Tyra - 2.0
+
+
+ Open source game engine for PlayStation 2™
+
+ YouTube tutorials »
+
+
+
+ Report Bug
+ ·
+ Request Feature
+
+
+
+[](https://github.com/h4570/tyra/stargazers)
+
+## Table of Contents
+
+* [About the project](#about-the-project)
+ * [Description](#description)
+ * [Tutorials](#tutorials)
+ * [Features](#features)
+ * [Branches](#branches)
+* [Contributing](#contributing)
+* [License](#license)
+* [Built With](#built-with)
+* [Acronyms](#acronyms)
+* [Thanks](#thanks)
+
+
+
+## About the Project
+
+
+
+
+
+---
+### Description
+Tyra is a project that allows you to easily create games for the PlayStation 2 console.
+When I tried to make a game for PS2 a few years ago, I realized how many things had to be done to tame this exotic console architecture.
+Decision was simple - I need to create an engine which will handle 3D file loading, 2D/3D rendering, animation, audio, handling between PS2 CPUs, synchronization and it should be simple as possible - so people with no experience can create a simple game in a few hours.
+**And that's what Tyra is all about.**
+
+### Tutorials
+* Setup environment - [Text tutorial](https://github.com/h4570/tyra/tree/master/docs/install) or [Video (soon)](https://www.youtube.com/playlist?list=PLdQVLBcXRRWvtMi-OqccDWLTEFEisD5RH)
+* Cook game assets - [Video (soon)](https://www.youtube.com/playlist?list=PLdQVLBcXRRWvtMi-OqccDWLTEFEisD5RH)
+* Create game from template - [Video (soon)](https://www.youtube.com/playlist?list=PLdQVLBcXRRWvtMi-OqccDWLTEFEisD5RH)
+---
+* 01 - [Code](https://github.com/h4570/tyra/tree/master/tutorials/01-hello), [Video (soon)](https://www.youtube.com/playlist?list=PLdQVLBcXRRWvtMi-OqccDWLTEFEisD5RH) - Hello world
+* 02 - [Code](https://github.com/h4570/tyra/tree/master/tutorials/02-sprite), [Video (soon)](https://www.youtube.com/playlist?list=PLdQVLBcXRRWvtMi-OqccDWLTEFEisD5RH) - 2D rendering
+* 03 - [Code](https://github.com/h4570/tyra/tree/master/tutorials/03-minecraft), [Video (soon)](https://www.youtube.com/playlist?list=PLdQVLBcXRRWvtMi-OqccDWLTEFEisD5RH) - 3D minecraft blocks
+* 04 - [Code](https://github.com/h4570/tyra/tree/master/tutorials/04-de_dust2), [Video (soon)](https://www.youtube.com/playlist?list=PLdQVLBcXRRWvtMi-OqccDWLTEFEisD5RH) - `de_dust2.obj` rendering
+* 05 - [Code](https://github.com/h4570/tyra/tree/master/tutorials/05-animation), [Video (soon)](https://www.youtube.com/playlist?list=PLdQVLBcXRRWvtMi-OqccDWLTEFEisD5RH) - 3D animation
+* 06 - [Code](https://github.com/h4570/tyra/tree/master/tutorials/06-audio), [Video (soon)](https://www.youtube.com/playlist?list=PLdQVLBcXRRWvtMi-OqccDWLTEFEisD5RH) - Background music and sound effects
+* 07 - [Code](https://github.com/h4570/tyra/tree/master/tutorials/07-lighting), [Video (soon)](https://www.youtube.com/playlist?list=PLdQVLBcXRRWvtMi-OqccDWLTEFEisD5RH) - Static lightmap and dynamic directional lights
+* 08 - [Code](https://github.com/h4570/tyra/tree/master/tutorials/08-skybox-debug), [Video (soon)](https://www.youtube.com/playlist?list=PLdQVLBcXRRWvtMi-OqccDWLTEFEisD5RH) - Skybox and debug rendering
+* 09 - [Code](https://github.com/h4570/tyra/tree/master/tutorials/09-manual-mode), [Video (soon)](https://www.youtube.com/playlist?list=PLdQVLBcXRRWvtMi-OqccDWLTEFEisD5RH) - Manual rendering (a'la OpenGL)
+
+---
+
+### Features
+* Docker image with Tyra, all tools and `PS2DEV` C++20 compiler
+* 3D rendering via `VU1` coprocessor:
+ * Static rendering
+ * Morph animation rendering
+ * Super optimized, custom minecraft block renderer
+ * Debug rendering (lines, boxes, bboxes)
+ * Manual rendering (a'la `OpenGL`)
+* 2D rendering
+* Frustum culling:
+ * Simple - culling whole mesh
+ * Precise - culling only visible parts of mesh
+* Clipping:
+ * Standard "fake", but fast PS2 clipping done in `VU1`
+ * Software triangle done in `EE Core`
+* Optimized math functions via `VU0` coprocessor
+* Background music and sound effects support
+* Pad support
+* `.wav` Audio file support
+* `.obj` 3D file support
+* `.md2` 3D file support
+* `.png` Texture file support
+
+### Branches
+* `master` - stable releases - tested on PS2 & PCSX2.
+* `develop` - daily builds.
+
+See the [open issues](https://github.com/h4570/tyra/issues) for a list of proposed features (and known issues).
+
+
+
+## Contributing
+
+Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
+
+1. Fork the Project
+2. Create your Feature Branch (`git checkout -b feature/amazing-feature`)
+3. Commit your Changes (`git commit -m 'Add some amazing-feature'`)
+4. Push to the Branch (`git push origin feature/amazing-feature`)
+5. Open a Pull Request
+
+
+
+## License
+
+Distributed under the Apache License 2.0 License. See `LICENSE` for more information.
+
+
+
+## Built With
+
+* [PS2DEV](https://github.com/ps2dev/ps2dev)
+* [VCLPP](https://github.com/glampert/vclpp)
+* Sony VCL
+
+
+
+## Acronyms
+* EE (Emotion Engine) - whole set of processors and subsystems that make up the PlayStation 2
+* EE Core - main PS2 CPU (c++ done here)
+* GS (Graphic synthesizer) - configurable rasterizer and texture mapper. Has only 2MB~ memory for textures, but for the opposite have very high transfer rate, so you can switch textures in the blink
+* VU0 - programmable (ASM) vector processor that is accessed by inline assembly code injected into C program.
+* VU1 - programmable (ASM) vector processor, which is similar to vertex shader. Accessed via DMA.
+* IOP - I/O processor which enables access to peripheral devices, such the game controller
+
+
+
+## Credits
+
+Without these guys, Tyra would not happen:
+* Dr Henry Fortuna - for code sources, PS2 academy tutorials
+* Rick Gaiser, fjtrujy - for maintaining PS2DEV
+* Wellington Carvalho - for testing, contributing to Tyra and sharing cool ideas!
+* Lukasz D.K. - for huge archive of PS2 stuff
+* Guilherme Lampert - for code sources
+* Jesper Svennevid, Daniel Collin - for openvcl's code samples
+* Whole PS2DEV team
+* Manieq - for nice splash screens!
+* And so many other guys. Thanks!
+
+Project Link: [https://github.com/h4570/tyra](https://github.com/h4570/tyra)
+Sandro Sobczyński - sandro.sobczynski@gmail.com
+[![LinkedIn][linkedin-shield]](https://linkedin.com/in/sandro-sobczyński-28820b15a)
+
+
+
+
+
+
+[contributors-shield]: https://img.shields.io/github/contributors/h4570/tyra.svg?style=flat-square
+[contributors-url]: https://github.com/h4570/tyra/graphs/contributors
+[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555
+[license-shield]: https://img.shields.io/github/license/h4570/tyra.svg?style=flat-square
+[license-url]: https://github.com/h4570/tyra/blob/master/LICENSE
\ No newline at end of file
diff --git a/ROADMAP.txt b/ROADMAP.txt
new file mode 100644
index 0000000..48c8771
--- /dev/null
+++ b/ROADMAP.txt
@@ -0,0 +1,23 @@
+------------ Tyra's v2.0 roadmap to publish on GitHub ------------
+- [General] Readme Credits: clipping, obj loader
+- [Video] How to setup environment
+- [Video] How to work with Tyra
+- [Video] How to create own game
+- [Video] How to cook assets (audio, textures, obj)
+- [Video] Tutorial videos
+- [Video] How to run game from USB
+
+------------ Github issues for Tyra v2 ------------
+- [Demo] BBox constructors
+- [General] Add cpp lint checker in GitHub / static code analysis
+- [General] Smart pointers and std::array instead of raw pointers
+- [General] Control generated id to avoid duplication. Maybe Just increment from 0? Add interface IIdentificable?
+- [renderer] fog
+- [General] CI in Github via docker image
+- [MD2] Refactor
+- [2D] Add zbuffer support (z-index)
+- [3DUtility] Add zbuffer support (z-index), cupMesh->translation.translateZ(10.0F) - wylaczyc zeby widziec problem
+- [2D] Fixed font support
+- [File] Async file loading
+- [tyrdat] Custom 3D data file with precalculated bboxes with support of async loading
+- [Renderer] Interlacing, and then 640x448 resolution
diff --git a/demo/Makefile b/demo/Makefile
new file mode 100644
index 0000000..cd99cc7
--- /dev/null
+++ b/demo/Makefile
@@ -0,0 +1,33 @@
+TARGET := demo.elf
+ENGINEDIR := ../engine
+
+#The Directories, Source, Includes, Objects, Binary and Resources
+SRCDIR := src
+INCDIR := inc
+BUILDDIR := obj
+TARGETDIR := bin
+RESDIR := res
+SRCEXT := cpp
+VSMEXT := vsm
+VCLEXT := vcl
+VCLPPEXT := vclpp
+DEPEXT := d
+OBJEXT := o
+
+#Flags, Libraries and Includes
+CFLAGS :=
+LIB := -ltyra
+LIBDIRS := -L$(ENGINEDIR)/bin
+INC := -I$(INCDIR) -I$(ENGINEDIR)/inc
+INCDEP := -I$(INCDIR) -I$(ENGINEDIR)/inc
+
+include ../Makefile.base
+
+clean-engine:
+ cd $(ENGINEDIR) && $(MAKE) cleaner
+
+build-engine:
+ cd $(ENGINEDIR) && $(MAKE)
+
+build-release-engine:
+ cd $(ENGINEDIR) && $(MAKE) release
diff --git a/src/samples/floors/bin/.gitignore b/demo/bin/.gitignore
similarity index 95%
rename from src/samples/floors/bin/.gitignore
rename to demo/bin/.gitignore
index 86d0cb2..44c5ea8 100644
--- a/src/samples/floors/bin/.gitignore
+++ b/demo/bin/.gitignore
@@ -1,4 +1,4 @@
-# Ignore everything in this directory
-*
-# Except this file
+# Ignore everything in this directory
+*
+# Except this file
!.gitignore
\ No newline at end of file
diff --git a/demo/inc/demo_game.hpp b/demo/inc/demo_game.hpp
new file mode 100644
index 0000000..bae41fb
--- /dev/null
+++ b/demo/inc/demo_game.hpp
@@ -0,0 +1,34 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include "state/state_manager.hpp"
+
+using Tyra::Engine;
+using Tyra::Game;
+
+namespace Demo {
+
+class DemoGame : public Game {
+ public:
+ DemoGame(Engine* engine);
+ ~DemoGame();
+
+ void init();
+ void loop();
+
+ private:
+ Engine* engine;
+ StateManager stateManager;
+};
+
+} // namespace Demo
diff --git a/demo/inc/game_settings.hpp b/demo/inc/game_settings.hpp
new file mode 100644
index 0000000..48f8ac0
--- /dev/null
+++ b/demo/inc/game_settings.hpp
@@ -0,0 +1,17 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+namespace Demo {
+
+const bool IS_REAL_PS2 = false;
+
+} // namespace Demo
diff --git a/demo/inc/state/global_state_type.hpp b/demo/inc/state/global_state_type.hpp
new file mode 100644
index 0000000..6b34be4
--- /dev/null
+++ b/demo/inc/state/global_state_type.hpp
@@ -0,0 +1,17 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+namespace Demo {
+
+enum GlobalStateType { STATE_INTRO, STATE_LOADING, STATE_GAME, STATE_EXIT };
+
+} // namespace Demo
diff --git a/demo/inc/state/state.hpp b/demo/inc/state/state.hpp
new file mode 100644
index 0000000..1bc18c0
--- /dev/null
+++ b/demo/inc/state/state.hpp
@@ -0,0 +1,40 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+
+using Tyra::Engine;
+
+namespace Demo {
+
+template
+class State {
+ public:
+ State(Engine* t_engine) : engine(t_engine) {}
+ virtual ~State() {}
+
+ virtual const StateTypeT& getState() const = 0;
+
+ virtual const bool& wantFinish() const = 0;
+
+ virtual void onStart() = 0;
+
+ virtual void update() = 0;
+
+ /** @return Next game state */
+ virtual StateTypeT onFinish() = 0;
+
+ protected:
+ Engine* engine;
+};
+
+} // namespace Demo
diff --git a/demo/inc/state/state_manager.hpp b/demo/inc/state/state_manager.hpp
new file mode 100644
index 0000000..1bc7179
--- /dev/null
+++ b/demo/inc/state/state_manager.hpp
@@ -0,0 +1,99 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include "./state.hpp"
+#include "./global_state_type.hpp"
+
+namespace Demo {
+
+template
+class StateManager {
+ public:
+ StateManager(const StateTypeT& t_initialState,
+ const StateTypeT& t_exitState) {
+ stateInitialized = false;
+ currentState = t_initialState;
+ exitState = t_exitState;
+ }
+
+ ~StateManager() { freeAll(); }
+
+ const StateTypeT& getCurrentState() const { return currentState; }
+
+ const StateTypeT& getExitState() const { return exitState; }
+
+ bool finished() const { return currentState == exitState; }
+
+ std::vector*>* getAll() { return &states; }
+
+ State* get(const StateTypeT& stateType) {
+ for (auto& state : states) {
+ if (state->getState() == stateType) {
+ return state;
+ }
+ }
+ return nullptr;
+ }
+
+ void add(State* state) { states.push_back(state); }
+
+ /** remove (without free) */
+ void remove(const State* state) {
+ states.erase(std::remove(states.begin(), states.end(), state),
+ states.end());
+ }
+
+ /** free and remove */
+ void free(const State* state) {
+ auto* found = get(state);
+ if (!found) return;
+
+ remove(found);
+ delete found;
+ }
+
+ /** free and remove all */
+ void freeAll() {
+ for (auto& state : states) {
+ delete state;
+ }
+ states.clear();
+ }
+
+ void update() {
+ for (auto& state : states) {
+ if (state->getState() == currentState) {
+ if (state->wantFinish()) {
+ currentState = state->onFinish();
+ stateInitialized = false;
+ break;
+ }
+
+ if (!stateInitialized) {
+ state->onStart();
+ stateInitialized = true;
+ }
+
+ state->update();
+ break;
+ }
+ }
+ }
+
+ private:
+ bool stateInitialized;
+ std::vector*> states;
+ StateTypeT currentState, exitState;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/enemy/enemy.hpp b/demo/inc/states/game/enemy/enemy.hpp
new file mode 100644
index 0000000..993faec
--- /dev/null
+++ b/demo/inc/states/game/enemy/enemy.hpp
@@ -0,0 +1,63 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include "states/game/renderer/renderer_dynamic_pair.hpp"
+#include "states/game/terrain/heightmap.hpp"
+#include "states/game/player/player_shoot_action.hpp"
+#include "./enemy_info.hpp"
+
+using Tyra::AnimationSequenceCallback;
+using Tyra::Audio;
+using Tyra::DynamicMesh;
+using Tyra::DynPipOptions;
+using Tyra::Engine;
+using Tyra::Renderer;
+using Tyra::Renderer3DUtility;
+using Tyra::TextureRepository;
+using Tyra::Vec4;
+
+namespace Demo {
+
+class Enemy {
+ public:
+ Enemy(Engine* engine, const EnemyInfo& info);
+ ~Enemy();
+
+ DynamicMesh* mesh;
+ DynPipOptions* options;
+ RendererDynamicPair* pair;
+
+ void update(const Heightmap& heightmap, const Vec4& playerPosition,
+ const PlayerShootAction& shootAction);
+
+ private:
+ std::vector walkSequence;
+ std::vector fightSequence;
+
+ void allocateOptions();
+ void walk(const Heightmap& heightmap, const Vec4& positionDiff);
+ void animationCallback(const AnimationSequenceCallback& callback);
+ void fight();
+ void handlePlayerShoot(const PlayerShootAction& shootAction);
+ void setMeshToSpawn();
+
+ bool isWalking = true;
+ bool isFighting = false;
+ Vec4 terrainLeftUp;
+ Vec4 terrainRightDown;
+
+ Audio* audio;
+ EnemyInfo info;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/enemy/enemy_info.hpp b/demo/inc/states/game/enemy/enemy_info.hpp
new file mode 100644
index 0000000..2da3d85
--- /dev/null
+++ b/demo/inc/states/game/enemy/enemy_info.hpp
@@ -0,0 +1,33 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include
+
+using Tyra::DynamicMesh;
+using Tyra::Texture;
+using Tyra::Vec4;
+
+namespace Demo {
+
+struct EnemyInfo {
+ u8 adpcmChannel;
+ audsrv_adpcm_t* adpcmPunch;
+ audsrv_adpcm_t* adpcmDeath;
+ DynamicMesh* motherMesh;
+ Texture* bodyTexture;
+ Texture* clothTexture;
+ Vec4 terrainLeftUp;
+ Vec4 terrainRightDown;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/enemy/enemy_manager.hpp b/demo/inc/states/game/enemy/enemy_manager.hpp
new file mode 100644
index 0000000..acb0db2
--- /dev/null
+++ b/demo/inc/states/game/enemy/enemy_manager.hpp
@@ -0,0 +1,45 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include
+#include "states/game/terrain/heightmap.hpp"
+#include "states/game/player/player_shoot_action.hpp"
+#include "./enemy.hpp"
+
+using Tyra::DynamicMesh;
+using Tyra::Engine;
+using Tyra::Texture;
+using Tyra::TextureRepository;
+using Tyra::Vec4;
+
+namespace Demo {
+
+class EnemyManager {
+ public:
+ EnemyManager(Engine* engine, const Heightmap& heightmap);
+ ~EnemyManager();
+
+ void update(const Heightmap& heightmap, const Vec4& playerPosition,
+ const PlayerShootAction& shootAction);
+
+ std::vector getPairs() const;
+
+ private:
+ TextureRepository* textureRepo;
+ std::vector enemies;
+ DynamicMesh* motherMesh;
+ Texture* bodyTexture;
+ Texture* clothTexture;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/game_state.hpp b/demo/inc/states/game/game_state.hpp
new file mode 100644
index 0000000..800480e
--- /dev/null
+++ b/demo/inc/states/game/game_state.hpp
@@ -0,0 +1,58 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include "state/state.hpp"
+#include "state/global_state_type.hpp"
+#include "./player/player.hpp"
+#include "./renderer/game_renderer.hpp"
+#include "./terrain/terrain.hpp"
+#include "./skybox/skybox.hpp"
+#include "./ship/ship.hpp"
+#include "./hud/hud.hpp"
+#include "./enemy/enemy_manager.hpp"
+
+using std::unique_ptr;
+
+namespace Demo {
+
+class GameState : public State {
+ public:
+ GameState(Engine* t_engine);
+ ~GameState();
+
+ const GlobalStateType& getState() const { return state; }
+
+ const bool& wantFinish() const { return _wantFinish; };
+
+ void onStart();
+
+ void update();
+
+ /** @return Next game state */
+ GlobalStateType onFinish();
+
+ private:
+ GlobalStateType state;
+ bool _wantFinish;
+ bool initialized;
+ u8 fpsChecker;
+
+ GameRenderer renderer;
+ unique_ptr player;
+ unique_ptr enemyManager;
+ unique_ptr terrain;
+ unique_ptr skybox;
+ unique_ptr ship;
+ unique_ptr hud;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/hud/hud.hpp b/demo/inc/states/game/hud/hud.hpp
new file mode 100644
index 0000000..b13ac1a
--- /dev/null
+++ b/demo/inc/states/game/hud/hud.hpp
@@ -0,0 +1,39 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include
+
+using std::unique_ptr;
+using Tyra::Sprite;
+using Tyra::Texture;
+using Tyra::TextureRepository;
+
+namespace Demo {
+
+class Hud {
+ public:
+ Hud(TextureRepository* repo);
+ ~Hud();
+
+ TextureRepository* repo;
+
+ unique_ptr hpSprite;
+ unique_ptr crosshairSprite;
+ unique_ptr soldierSprite;
+
+ Texture* hpTexture;
+ Texture* crosshairTexture;
+ Texture* soldierTexture;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/player/camera.hpp b/demo/inc/states/game/player/camera.hpp
new file mode 100644
index 0000000..1e31f18
--- /dev/null
+++ b/demo/inc/states/game/player/camera.hpp
@@ -0,0 +1,40 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+
+using Tyra::CameraInfo3D;
+using Tyra::Pad;
+using Tyra::Vec4;
+
+namespace Demo {
+
+class Camera {
+ public:
+ Camera(Pad* pad);
+ ~Camera();
+
+ Vec4 lookAt;
+ Vec4 position;
+ Vec4 unitCircle;
+
+ CameraInfo3D getCameraInfo() { return CameraInfo3D(&position, &lookAt); }
+
+ void update(const Vec4& playerPosition, const float& terrainHeight);
+
+ private:
+ float circleRotation, lengthFromOrigin, height;
+
+ Pad* pad;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/player/player.hpp b/demo/inc/states/game/player/player.hpp
new file mode 100644
index 0000000..1eb4b66
--- /dev/null
+++ b/demo/inc/states/game/player/player.hpp
@@ -0,0 +1,53 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include "states/game/renderer/renderer_dynamic_pair.hpp"
+#include "states/game/renderer/renderer_static_pair.hpp"
+#include "states/game/terrain/heightmap.hpp"
+#include "./weapon.hpp"
+#include "./camera.hpp"
+#include "./player_shoot_action.hpp"
+#include
+
+using Tyra::Engine;
+using Tyra::Pad;
+using Tyra::Renderer3DUtility;
+using Tyra::Vec4;
+
+namespace Demo {
+
+class Player {
+ public:
+ Player(Engine* engine);
+ ~Player();
+
+ const Vec4& getPosition() const { return position; }
+ CameraInfo3D getCameraInfo() { return camera.getCameraInfo(); }
+
+ RendererStaticPair* pair;
+
+ void update(const Heightmap& heightmap);
+
+ PlayerShootAction getShootAction() const;
+
+ private:
+ void handlePlayerPosition(const Heightmap& heightmap,
+ const float& terrainHeight);
+
+ float speed;
+ Pad* pad;
+ Vec4 position;
+ Weapon weapon;
+ Camera camera;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/player/player_shoot_action.hpp b/demo/inc/states/game/player/player_shoot_action.hpp
new file mode 100644
index 0000000..5227866
--- /dev/null
+++ b/demo/inc/states/game/player/player_shoot_action.hpp
@@ -0,0 +1,25 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include
+
+using Tyra::Ray;
+
+namespace Demo {
+
+struct PlayerShootAction {
+ bool isShooting = false;
+ std::optional ray;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/player/weapon.hpp b/demo/inc/states/game/player/weapon.hpp
new file mode 100644
index 0000000..5a3be04
--- /dev/null
+++ b/demo/inc/states/game/player/weapon.hpp
@@ -0,0 +1,54 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+
+using Tyra::Audio;
+using Tyra::Engine;
+using Tyra::Pad;
+using Tyra::Renderer;
+using Tyra::StaPipOptions;
+using Tyra::StaticMesh;
+using Tyra::TextureRepository;
+using Tyra::Timer;
+using Tyra::Vec4;
+
+namespace Demo {
+
+class Weapon {
+ public:
+ Weapon(Engine* engine);
+ ~Weapon();
+
+ StaticMesh* mesh;
+ StaPipOptions* options;
+ bool isShooting = false;
+
+ void update();
+
+ private:
+ bool isShootAnimation1, isShootAnimation2;
+ Pad* pad;
+ Audio* audio;
+ Timer shootTimer;
+ Vec4 initialPosition;
+ u8 adpcmCurrentChannel;
+ u8 adpcmChannelsCount;
+
+ audsrv_adpcm_t* shootAdpcm;
+
+ void shoot();
+ void allocateOptions();
+ u8 getShootChannel();
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/renderer/game_renderer.hpp b/demo/inc/states/game/renderer/game_renderer.hpp
new file mode 100644
index 0000000..2fa36f2
--- /dev/null
+++ b/demo/inc/states/game/renderer/game_renderer.hpp
@@ -0,0 +1,52 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include "./renderer_static_pair.hpp"
+#include "./renderer_dynamic_pair.hpp"
+
+using Tyra::CoreBBox;
+using Tyra::DynamicPipeline;
+using Tyra::Renderer;
+using Tyra::Sprite;
+using Tyra::StaticPipeline;
+
+namespace Demo {
+
+class GameRenderer {
+ public:
+ GameRenderer(Renderer* renderer);
+ ~GameRenderer();
+
+ void add(std::vector staticPairs);
+ void add(std::vector dynamicPairs);
+ void add(RendererStaticPair* staticPair);
+ void add(RendererDynamicPair* dynamicPair);
+ void add(Sprite* sprite);
+ void add(const CoreBBox& bbox);
+
+ void clear();
+ void render();
+
+ private:
+ Renderer* renderer;
+
+ StaticPipeline stpip;
+ DynamicPipeline dypip;
+
+ std::vector staticPairs;
+ std::vector dynamicPairs;
+ std::vector sprites;
+ std::vector bboxes;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/renderer/renderer_dynamic_pair.hpp b/demo/inc/states/game/renderer/renderer_dynamic_pair.hpp
new file mode 100644
index 0000000..3e90e4e
--- /dev/null
+++ b/demo/inc/states/game/renderer/renderer_dynamic_pair.hpp
@@ -0,0 +1,25 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+
+using Tyra::DynamicMesh;
+using Tyra::DynPipOptions;
+
+namespace Demo {
+
+struct RendererDynamicPair {
+ DynamicMesh* mesh;
+ DynPipOptions* options;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/renderer/renderer_static_pair.hpp b/demo/inc/states/game/renderer/renderer_static_pair.hpp
new file mode 100644
index 0000000..2d1547c
--- /dev/null
+++ b/demo/inc/states/game/renderer/renderer_static_pair.hpp
@@ -0,0 +1,25 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+
+using Tyra::StaPipOptions;
+using Tyra::StaticMesh;
+
+namespace Demo {
+
+struct RendererStaticPair {
+ StaticMesh* mesh;
+ StaPipOptions* options;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/ship/ship.hpp b/demo/inc/states/game/ship/ship.hpp
new file mode 100644
index 0000000..4b50637
--- /dev/null
+++ b/demo/inc/states/game/ship/ship.hpp
@@ -0,0 +1,37 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include "states/game/renderer/renderer_static_pair.hpp"
+
+using Tyra::Renderer;
+using Tyra::StaPipOptions;
+using Tyra::StaticMesh;
+using Tyra::TextureRepository;
+using Tyra::Vec4;
+
+namespace Demo {
+
+class Ship {
+ public:
+ Ship(TextureRepository* repo);
+ ~Ship();
+
+ StaticMesh* mesh;
+ StaPipOptions* options;
+ RendererStaticPair* pair;
+
+ private:
+ void allocateOptions();
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/skybox/skybox.hpp b/demo/inc/states/game/skybox/skybox.hpp
new file mode 100644
index 0000000..510b590
--- /dev/null
+++ b/demo/inc/states/game/skybox/skybox.hpp
@@ -0,0 +1,39 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include "states/game/renderer/renderer_static_pair.hpp"
+
+using Tyra::Renderer;
+using Tyra::StaPipOptions;
+using Tyra::StaticMesh;
+using Tyra::TextureRepository;
+using Tyra::Vec4;
+
+namespace Demo {
+
+class Skybox {
+ public:
+ Skybox(TextureRepository* repo);
+ ~Skybox();
+
+ StaticMesh* mesh;
+ StaPipOptions* options;
+ RendererStaticPair* pair;
+
+ void update(const Vec4& playerPosition);
+
+ private:
+ void allocateOptions();
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/terrain/heightmap.hpp b/demo/inc/states/game/terrain/heightmap.hpp
new file mode 100644
index 0000000..d79bc53
--- /dev/null
+++ b/demo/inc/states/game/terrain/heightmap.hpp
@@ -0,0 +1,43 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+
+using Tyra::Vec4;
+
+namespace Demo {
+
+class Heightmap {
+ public:
+ Heightmap(const float& minHeight, const float& maxHeight, const Vec4& leftUp,
+ const Vec4& rightDown);
+ ~Heightmap();
+
+ const float& getHeightOffset(const Vec4& playerPosition) const;
+
+ bool isOutside(const Vec4& position) const;
+
+ float** map;
+ s16 mapWidth, mapHeight;
+ float minHeight, maxHeight;
+ Vec4 leftUp, rightDown;
+
+ private:
+ float getWidthPercentage(const Vec4& playerPosition) const;
+ float getHeightPercentage(const Vec4& playerPosition) const;
+ void allocateMap(const unsigned char* data, const int& width,
+ const int& height);
+ float getGameHeight(const u8& inputColor, const u8& minColor,
+ const u8& maxColor) const;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/game/terrain/terrain.hpp b/demo/inc/states/game/terrain/terrain.hpp
new file mode 100644
index 0000000..5eb5bf4
--- /dev/null
+++ b/demo/inc/states/game/terrain/terrain.hpp
@@ -0,0 +1,40 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include "states/game/renderer/renderer_static_pair.hpp"
+#include "./heightmap.hpp"
+
+using Tyra::StaPipOptions;
+using Tyra::StaticMesh;
+using Tyra::TextureRepository;
+using Tyra::Vec4;
+
+namespace Demo {
+
+class Terrain {
+ public:
+ Terrain(TextureRepository* repo);
+ ~Terrain();
+
+ Heightmap heightmap;
+ StaticMesh* mesh;
+ StaPipOptions* options;
+ RendererStaticPair* pair;
+
+ float getHeightOffset(const Vec4& playerPosition);
+
+ private:
+ void allocateOptions();
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/intro/intro_state.hpp b/demo/inc/states/intro/intro_state.hpp
new file mode 100644
index 0000000..b70d26b
--- /dev/null
+++ b/demo/inc/states/intro/intro_state.hpp
@@ -0,0 +1,44 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include "state/state.hpp"
+#include "state/global_state_type.hpp"
+#include "state/state_manager.hpp"
+#include "./intro_state_type.hpp"
+
+namespace Demo {
+
+class IntroState : public State {
+ public:
+ IntroState(Engine* t_engine);
+ ~IntroState();
+
+ const GlobalStateType& getState() const { return state; }
+
+ const bool& wantFinish() const { return _wantFinish; };
+
+ void onStart();
+
+ void update();
+
+ /** @return Next game state */
+ GlobalStateType onFinish();
+
+ private:
+ GlobalStateType state;
+ bool _wantFinish;
+ bool initialized;
+
+ StateManager stateManager;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/intro/intro_state_type.hpp b/demo/inc/states/intro/intro_state_type.hpp
new file mode 100644
index 0000000..8bb5a51
--- /dev/null
+++ b/demo/inc/states/intro/intro_state_type.hpp
@@ -0,0 +1,22 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+namespace Demo {
+
+enum IntroStateType {
+ STATE_PS2DEV,
+ STATE_TYRA,
+ STATE_PRESS_KEY,
+ STATE_INTRO_END
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/intro/states/intro_press_key_state.hpp b/demo/inc/states/intro/states/intro_press_key_state.hpp
new file mode 100644
index 0000000..cbc6c44
--- /dev/null
+++ b/demo/inc/states/intro/states/intro_press_key_state.hpp
@@ -0,0 +1,74 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include "state/state.hpp"
+#include "../intro_state_type.hpp"
+#include
+
+using Tyra::Sprite;
+using Tyra::Texture;
+using Tyra::Timer;
+using Tyra::Vec2;
+
+namespace Demo {
+
+class IntroPressKeyState : public State {
+ public:
+ IntroPressKeyState(Engine* t_engine);
+ ~IntroPressKeyState();
+
+ const IntroStateType& getState() const { return state; }
+
+ const bool& wantFinish() const { return _wantFinish; };
+
+ void onStart();
+
+ /** @return Next game state */
+ IntroStateType onFinish();
+
+ void update();
+
+ private:
+ void renderFiller();
+ void updateMap();
+
+ IntroStateType state;
+ bool _wantFinish;
+ bool initialized;
+ bool mapFadeIn;
+ bool fillerFadeIn;
+ bool showPressKey;
+ bool pressKeyAlphaToggle;
+ u8 pressKeyAlphaDelayer;
+
+ float fillersOffset;
+ constexpr static float textureWidthHeight = 512.0F;
+ const static u8 mapRows = 3;
+ const static u8 mapCols = 3;
+
+ Vec2 mapPosition;
+ u8 mapDirection;
+
+ Texture* fillerTexture;
+ Sprite* fillerSprite;
+
+ Texture* logoTexture;
+ Sprite* logoSprite;
+
+ Texture* pressKeyTexture;
+ Sprite* pressKeySprite;
+
+ Texture* mapTextures[mapRows][mapCols];
+ Sprite* mapSprites[mapRows][mapCols];
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/intro/states/intro_ps2dev_state.hpp b/demo/inc/states/intro/states/intro_ps2dev_state.hpp
new file mode 100644
index 0000000..4607f0e
--- /dev/null
+++ b/demo/inc/states/intro/states/intro_ps2dev_state.hpp
@@ -0,0 +1,54 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include "state/state.hpp"
+#include "../intro_state_type.hpp"
+#include
+
+using Tyra::Sprite;
+using Tyra::Texture;
+using Tyra::Timer;
+
+namespace Demo {
+
+class IntroPs2DevState : public State {
+ public:
+ IntroPs2DevState(Engine* t_engine);
+ ~IntroPs2DevState();
+
+ const IntroStateType& getState() const { return state; }
+
+ const bool& wantFinish() const { return _wantFinish; };
+
+ void onStart();
+
+ void update();
+
+ /** @return Next game state */
+ IntroStateType onFinish();
+
+ private:
+ IntroStateType state;
+ bool _wantFinish;
+
+ Texture* texture;
+ Timer initialDelayTimer;
+ bool initialized;
+ bool initialDelayElapsed;
+ bool fadeinActivated;
+ bool fadeoutActivated;
+ u8 frameSkipper;
+
+ Sprite* sprite;
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/intro/states/intro_tyra_state.hpp b/demo/inc/states/intro/states/intro_tyra_state.hpp
new file mode 100644
index 0000000..f0058f6
--- /dev/null
+++ b/demo/inc/states/intro/states/intro_tyra_state.hpp
@@ -0,0 +1,63 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include "state/state.hpp"
+#include "../intro_state_type.hpp"
+#include
+
+using Tyra::Sprite;
+using Tyra::Texture;
+using Tyra::Timer;
+
+namespace Demo {
+
+class IntroTyraState : public State {
+ public:
+ IntroTyraState(Engine* t_engine);
+ ~IntroTyraState();
+
+ const IntroStateType& getState() const { return state; }
+
+ const bool& wantFinish() const { return _wantFinish; };
+
+ void onStart();
+
+ void update();
+
+ /** @return Next game state */
+ IntroStateType onFinish();
+
+ private:
+ IntroStateType state;
+ bool _wantFinish;
+
+ Texture* tyraTexture;
+ Texture* bgTexture;
+ Texture* bg2Texture;
+ Texture* fillerTexture;
+
+ Timer initialDelayTimer;
+ bool initialized;
+ bool initialDelayElapsed;
+ bool animActivated;
+ bool fadeoutActivated;
+ u8 frameSkipper;
+
+ Sprite* tyraSprite;
+ Sprite* bgSprite;
+ Sprite* bg2Sprite;
+ Sprite* fillerSprite;
+
+ void renderFillers();
+};
+
+} // namespace Demo
diff --git a/demo/inc/states/loading/loading_state.hpp b/demo/inc/states/loading/loading_state.hpp
new file mode 100644
index 0000000..9d010ca
--- /dev/null
+++ b/demo/inc/states/loading/loading_state.hpp
@@ -0,0 +1,41 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include "state/state.hpp"
+#include "state/global_state_type.hpp"
+#include "state/state_manager.hpp"
+
+namespace Demo {
+
+class LoadingState : public State {
+ public:
+ LoadingState(Engine* t_engine);
+ ~LoadingState();
+
+ const GlobalStateType& getState() const { return state; }
+
+ const bool& wantFinish() const { return _wantFinish; };
+
+ void onStart();
+
+ void update();
+
+ /** @return Next game state */
+ GlobalStateType onFinish();
+
+ private:
+ GlobalStateType state;
+ bool _wantFinish;
+ bool initialized;
+};
+
+} // namespace Demo
diff --git a/src/samples/cube/bin/.gitignore b/demo/res/.gitignore
similarity index 95%
rename from src/samples/cube/bin/.gitignore
rename to demo/res/.gitignore
index 86d0cb2..44c5ea8 100644
--- a/src/samples/cube/bin/.gitignore
+++ b/demo/res/.gitignore
@@ -1,4 +1,4 @@
-# Ignore everything in this directory
-*
-# Except this file
+# Ignore everything in this directory
+*
+# Except this file
!.gitignore
\ No newline at end of file
diff --git a/demo/run.ps1 b/demo/run.ps1
new file mode 100644
index 0000000..58035c6
--- /dev/null
+++ b/demo/run.ps1
@@ -0,0 +1,4 @@
+$ConfigFile = Join-Path $PSScriptRoot '../windows-pcsx2.ps1'
+. $ConfigFile
+
+RunPCSX2
diff --git a/demo/src/demo_game.cpp b/demo/src/demo_game.cpp
new file mode 100644
index 0000000..9767d5c
--- /dev/null
+++ b/demo/src/demo_game.cpp
@@ -0,0 +1,35 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "demo_game.hpp"
+#include "states/intro/intro_state.hpp"
+#include "states/loading/loading_state.hpp"
+#include "states/game/game_state.hpp"
+
+namespace Demo {
+
+DemoGame::DemoGame(Engine* t_engine)
+ : engine(t_engine), stateManager(STATE_INTRO, STATE_EXIT) {}
+DemoGame::~DemoGame() {}
+
+void DemoGame::init() {
+ stateManager.add(new IntroState(engine));
+ stateManager.add(new LoadingState(engine));
+ stateManager.add(new GameState(engine));
+}
+void DemoGame::loop() {
+ stateManager.update();
+
+ if (stateManager.finished()) {
+ exit(0);
+ }
+}
+
+} // namespace Demo
diff --git a/demo/src/main.cpp b/demo/src/main.cpp
new file mode 100644
index 0000000..08cc626
--- /dev/null
+++ b/demo/src/main.cpp
@@ -0,0 +1,29 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include
+#include "demo_game.hpp"
+#include "game_settings.hpp"
+
+int main() {
+ Tyra::EngineOptions options;
+
+ if (Demo::IS_REAL_PS2) {
+ options.writeLogsToFile = true;
+ options.loadUsbDriver = true;
+ }
+
+ Tyra::Engine engine(options);
+
+ Demo::DemoGame game(&engine);
+ engine.run(&game);
+ SleepThread();
+ return 0;
+}
diff --git a/demo/src/states/game/enemy/enemy.cpp b/demo/src/states/game/enemy/enemy.cpp
new file mode 100644
index 0000000..86070af
--- /dev/null
+++ b/demo/src/states/game/enemy/enemy.cpp
@@ -0,0 +1,159 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/game/enemy/enemy.hpp"
+#include
+
+using Tyra::Color;
+using Tyra::Math;
+
+namespace Demo {
+
+Enemy::Enemy(Engine* engine, const EnemyInfo& t_info) {
+ info = t_info;
+
+ mesh = new DynamicMesh(*info.motherMesh);
+
+ auto* bodyMaterial = mesh->getMaterialByName("BodyMaterial");
+ auto* clothMaterial = mesh->getMaterialByName("ClothMaterial");
+
+ TYRA_ASSERT(bodyMaterial != nullptr && clothMaterial != nullptr,
+ "Body and cloth materials not found!");
+
+ info.bodyTexture->addLink(bodyMaterial->id);
+ info.clothTexture->addLink(clothMaterial->id);
+
+ float r = Math::randomf(75.0F, 128.0F);
+ float g = Math::randomf(75.0F, 128.0F);
+ float b = Math::randomf(75.0F, 128.0F);
+
+ bodyMaterial->ambient.set(r, g, b);
+ clothMaterial->ambient.set(r, g, b);
+
+ walkSequence = {0, 1, 2};
+ fightSequence = {3, 4, 5};
+
+ terrainLeftUp = info.terrainLeftUp;
+ terrainRightDown = info.terrainRightDown;
+
+ setMeshToSpawn();
+
+ mesh->animation.setSequence(walkSequence);
+ mesh->animation.loop = true;
+ mesh->animation.setCallback(
+ std::bind(&Enemy::animationCallback, this, std::placeholders::_1));
+ mesh->translation.translateY(-5.0F);
+
+ audio = &engine->audio;
+ audio->adpcm.setVolume(30, info.adpcmChannel);
+
+ allocateOptions();
+
+ pair = new RendererDynamicPair{mesh, options};
+}
+
+Enemy::~Enemy() {
+ delete mesh;
+ delete options;
+ delete pair;
+}
+
+void Enemy::setMeshToSpawn() {
+ auto spawnPoint =
+ Vec4(Math::randomf(terrainLeftUp.x, terrainRightDown.x), 0.0F,
+ Math::randomf(terrainLeftUp.z, terrainRightDown.z), 1.0F);
+
+ mesh->setPosition(spawnPoint);
+}
+
+void Enemy::update(const Heightmap& heightmap, const Vec4& playerPosition,
+ const PlayerShootAction& shootAction) {
+ auto* enemyPosition = mesh->getPosition();
+
+ handlePlayerShoot(shootAction);
+
+ auto diff = *enemyPosition - playerPosition;
+ auto ang = Math::atan2(diff.x, diff.z);
+
+ if (diff.length() > 150.0F) {
+ walk(heightmap, diff);
+ } else {
+ fight();
+ }
+
+ const float naturalRotation = 3.1F;
+ mesh->rotation.identity();
+ ang += naturalRotation;
+ mesh->rotation.rotateByAngle(ang, Vec4(0.0F, 1.0F, 0.0F, 0.0F));
+
+ mesh->update();
+}
+
+void Enemy::handlePlayerShoot(const PlayerShootAction& shootAction) {
+ if (!shootAction.isShooting) {
+ return;
+ }
+
+ const auto& ray = shootAction.ray.value();
+
+ auto bbox =
+ mesh->getCurrentBoundingBox().getTransformed(mesh->getModelMatrix());
+
+ auto isOnEnemy = ray.intersectBox(bbox.min(), bbox.max());
+
+ if (isOnEnemy) {
+ setMeshToSpawn();
+ audio->adpcm.tryPlay(info.adpcmDeath, info.adpcmChannel);
+ }
+}
+
+void Enemy::walk(const Heightmap& heightmap, const Vec4& positionDiff) {
+ if (isFighting) {
+ mesh->animation.setSequence(walkSequence);
+ }
+
+ isFighting = false;
+ isWalking = true;
+
+ auto* enemyPosition = mesh->getPosition();
+ auto normalized = positionDiff;
+ normalized.normalize();
+ const float speed = 2.5F;
+ auto nextPos = *enemyPosition - normalized * speed;
+ nextPos.y = heightmap.getHeightOffset(nextPos) - 60.0F;
+
+ mesh->translation.identity();
+ mesh->translation.translate(nextPos);
+}
+
+void Enemy::fight() {
+ if (isWalking) {
+ mesh->animation.setSequence(fightSequence);
+ }
+
+ isFighting = true;
+ isWalking = false;
+}
+
+void Enemy::animationCallback(const AnimationSequenceCallback& callback) {
+ if (callback == AnimationSequenceCallback::AnimationSequenceCallback_Loop) {
+ if (isFighting) {
+ audio->adpcm.tryPlay(info.adpcmPunch, info.adpcmChannel);
+ }
+ }
+}
+
+void Enemy::allocateOptions() {
+ options = new DynPipOptions();
+ options->frustumCulling =
+ Tyra::PipelineFrustumCulling::PipelineFrustumCulling_None;
+}
+
+} // namespace Demo
diff --git a/demo/src/states/game/enemy/enemy_manager.cpp b/demo/src/states/game/enemy/enemy_manager.cpp
new file mode 100644
index 0000000..62f30a7
--- /dev/null
+++ b/demo/src/states/game/enemy/enemy_manager.cpp
@@ -0,0 +1,92 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/game/enemy/enemy_manager.hpp"
+#include
+#include "game_settings.hpp"
+
+using Tyra::FileUtils;
+using Tyra::Math;
+using Tyra::ObjLoader;
+using Tyra::ObjLoaderOptions;
+
+namespace Demo {
+
+EnemyManager::EnemyManager(Engine* engine, const Heightmap& heightmap) {
+ textureRepo = &engine->renderer.core.texture.repository;
+
+ ObjLoader loader;
+ ObjLoaderOptions objOptions;
+ objOptions.animation.count = 6;
+ objOptions.flipUVs = true;
+ objOptions.scale = 250.0F;
+
+ auto data = loader.load(FileUtils::fromCwd("game/models/zombie/zombie.obj"),
+ objOptions);
+ data->normalsEnabled = false;
+ motherMesh = new DynamicMesh(data.get());
+
+ bodyTexture = textureRepo->add(
+ FileUtils::fromCwd("game/models/zombie/BodyMaterial.png"));
+
+ clothTexture = textureRepo->add(
+ FileUtils::fromCwd("game/models/zombie/ClothMaterial.png"));
+
+ auto* punch = engine->audio.adpcm.load(
+ FileUtils::fromCwd("game/models/zombie/punch.adpcm"));
+
+ auto* death = engine->audio.adpcm.load(
+ FileUtils::fromCwd("game/models/zombie/death.adpcm"));
+
+ const int enemyCount = IS_REAL_PS2 ? 8 : 12;
+ for (int i = 0; i < enemyCount; i++) {
+ EnemyInfo info;
+ info.adpcmChannel = 9 + i;
+ info.adpcmPunch = punch;
+ info.adpcmDeath = death;
+ info.motherMesh = motherMesh;
+ info.clothTexture = clothTexture;
+ info.bodyTexture = bodyTexture;
+ info.terrainLeftUp = heightmap.leftUp;
+ info.terrainRightDown = heightmap.rightDown;
+ enemies.push_back(new Enemy(engine, info));
+ }
+}
+
+EnemyManager::~EnemyManager() {
+ for (auto* enemy : enemies) {
+ delete enemy;
+ }
+
+ delete motherMesh;
+
+ textureRepo->free(bodyTexture->id);
+ textureRepo->free(clothTexture->id);
+}
+
+std::vector EnemyManager::getPairs() const {
+ std::vector result;
+
+ for (auto* enemy : enemies) {
+ result.push_back(enemy->pair);
+ }
+
+ return result;
+}
+
+void EnemyManager::update(const Heightmap& heightmap,
+ const Vec4& playerPosition,
+ const PlayerShootAction& shootAction) {
+ for (auto* enemy : enemies) {
+ enemy->update(heightmap, playerPosition, shootAction);
+ }
+}
+
+} // namespace Demo
diff --git a/demo/src/states/game/game_state.cpp b/demo/src/states/game/game_state.cpp
new file mode 100644
index 0000000..75121e5
--- /dev/null
+++ b/demo/src/states/game/game_state.cpp
@@ -0,0 +1,97 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/game/game_state.hpp"
+#include "game_settings.hpp"
+
+using std::make_unique;
+using Tyra::FileUtils;
+
+namespace Demo {
+
+GameState::GameState(Engine* t_engine)
+ : State(t_engine), renderer(&t_engine->renderer) {
+ state = STATE_GAME;
+ _wantFinish = false;
+ initialized = false;
+ fpsChecker = 0;
+}
+
+GameState::~GameState() {}
+
+void GameState::onStart() {
+ TYRA_LOG("Game. RAM: ", engine->info.getAvailableRAM(), "MB");
+
+ engine->audio.song.stop();
+ engine->audio.song.inLoop = true;
+ engine->audio.song.load(FileUtils::fromCwd("game/game-audio.wav"));
+ engine->audio.song.setVolume(0);
+ engine->audio.song.play();
+
+ auto* repository = &engine->renderer.core.texture.repository;
+ player = make_unique(engine);
+ terrain = make_unique(repository);
+ enemyManager = make_unique(engine, terrain->heightmap);
+ ship = make_unique(repository);
+ skybox = make_unique(repository);
+ hud = make_unique(repository);
+
+ engine->audio.song.setVolume(85);
+
+ if (IS_REAL_PS2) engine->renderer.setFrameLimit(false);
+
+ initialized = true;
+}
+
+GlobalStateType GameState::onFinish() {
+ if (!initialized) return STATE_EXIT;
+
+ initialized = false;
+
+ return STATE_EXIT;
+}
+
+void GameState::update() {
+ // Check fps
+ if (fpsChecker++ > 50) {
+ TYRA_LOG("FPS: ", engine->info.getFps(),
+ " RAM: ", engine->info.getAvailableRAM());
+ fpsChecker = 0;
+ }
+
+ // Game logic
+ player->update(terrain->heightmap);
+ skybox->update(player->getPosition());
+ auto shootAction = player->getShootAction();
+ enemyManager->update(terrain->heightmap, player->getPosition(), shootAction);
+ auto* firstEnemyMesh = enemyManager->getPairs().front()->mesh;
+
+ // Render
+ engine->renderer.beginFrame(player->getCameraInfo());
+ {
+ renderer.clear();
+ {
+ renderer.add(skybox->pair); // First, because of ALLPASS
+ renderer.add(ship->pair);
+ renderer.add(player->pair);
+ renderer.add(enemyManager->getPairs());
+ renderer.add(terrain->pair);
+ renderer.add(firstEnemyMesh->getCurrentBoundingBox().getTransformed(
+ firstEnemyMesh->getModelMatrix()));
+ renderer.add(hud->crosshairSprite.get());
+ renderer.add(hud->hpSprite.get());
+ renderer.add(hud->soldierSprite.get());
+ }
+ renderer.render();
+ }
+ engine->renderer.endFrame();
+}
+
+} // namespace Demo
diff --git a/demo/src/states/game/hud/hud.cpp b/demo/src/states/game/hud/hud.cpp
new file mode 100644
index 0000000..7563b5d
--- /dev/null
+++ b/demo/src/states/game/hud/hud.cpp
@@ -0,0 +1,51 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/game/hud/hud.hpp"
+
+using Tyra::FileUtils;
+
+namespace Demo {
+
+Hud::Hud(TextureRepository* t_repo) {
+ repo = t_repo;
+
+ soldierSprite = std::make_unique();
+ soldierSprite->mode = Tyra::MODE_STRETCH;
+ soldierSprite->size.set(64.0F, 64.0F);
+ soldierSprite->position.set(5.0F, 448.0F - 64.0F - 5.0F);
+
+ soldierTexture = repo->add(FileUtils::fromCwd("game/soldier.png"));
+ soldierTexture->addLink(soldierSprite->id);
+
+ hpSprite = std::make_unique();
+ hpSprite->mode = Tyra::MODE_STRETCH;
+ hpSprite->size.set(128.0F, 64.0F);
+ hpSprite->position.set(70.0F, 448.0F - 64.0F - 5.0F);
+
+ hpTexture = repo->add(FileUtils::fromCwd("game/health_bar.png"));
+ hpTexture->addLink(hpSprite->id);
+
+ crosshairSprite = std::make_unique();
+ crosshairSprite->mode = Tyra::MODE_STRETCH;
+ crosshairSprite->size.set(16.0F, 16.0F);
+ crosshairSprite->position.set(512.0F / 2.0F - 16.0F / 2,
+ 448.0F / 2.0F - 16.0F / 2);
+
+ crosshairTexture = repo->add(FileUtils::fromCwd("game/crosshair.png"));
+ crosshairTexture->addLink(crosshairSprite->id);
+}
+
+Hud::~Hud() {
+ repo->free(crosshairTexture->id);
+ repo->free(hpTexture->id);
+}
+
+} // namespace Demo
diff --git a/demo/src/states/game/player/camera.cpp b/demo/src/states/game/player/camera.cpp
new file mode 100644
index 0000000..f4caa66
--- /dev/null
+++ b/demo/src/states/game/player/camera.cpp
@@ -0,0 +1,49 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/game/player/camera.hpp"
+
+namespace Demo {
+
+Camera::Camera(Pad* t_pad) : lookAt(0.0F), position(0.0F), pad(t_pad) {
+ lengthFromOrigin = 30.0F;
+ circleRotation = -3.5F;
+ height = 1.5F;
+}
+
+Camera::~Camera() {}
+
+void Camera::update(const Vec4& playerPosition, const float& terrainHeight) {
+ const float rotationOffset = 0.045F;
+ const float heightOffset = 0.5F;
+
+ const auto& rightJoy = pad->getRightJoyPad();
+
+ if (rightJoy.h <= 100) {
+ circleRotation -= rotationOffset;
+ } else if (rightJoy.h >= 200) {
+ circleRotation += rotationOffset;
+ }
+
+ if (rightJoy.v <= 100) {
+ height -= heightOffset;
+ } else if (rightJoy.v >= 200) {
+ height += heightOffset;
+ }
+
+ unitCircle.x = (sin(circleRotation) * lengthFromOrigin);
+ unitCircle.y = height;
+ unitCircle.z = (cos(circleRotation) * lengthFromOrigin);
+
+ position = playerPosition;
+ lookAt = unitCircle + playerPosition;
+}
+
+} // namespace Demo
diff --git a/demo/src/states/game/player/player.cpp b/demo/src/states/game/player/player.cpp
new file mode 100644
index 0000000..0940d52
--- /dev/null
+++ b/demo/src/states/game/player/player.cpp
@@ -0,0 +1,77 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/game/player/player.hpp"
+
+namespace Demo {
+
+Player::Player(Engine* engine)
+ : position(0.0F), weapon(engine), camera(&engine->pad) {
+ pair = new RendererStaticPair{weapon.mesh, weapon.options};
+ pad = &engine->pad;
+ position = Vec4(3.0F, 50.0F, 0.0F);
+ camera.lookAt = Vec4(0.0F, 0.0F, -30.0F);
+ speed = 6.0F;
+}
+
+Player::~Player() { delete pair; }
+
+void Player::update(const Heightmap& heightmap) {
+ float terrainHeight = heightmap.getHeightOffset(getPosition());
+
+ handlePlayerPosition(heightmap, terrainHeight);
+ camera.update(position, terrainHeight);
+ weapon.update();
+}
+
+PlayerShootAction Player::getShootAction() const {
+ PlayerShootAction action;
+ action.isShooting = weapon.isShooting;
+
+ if (action.isShooting) {
+ action.ray =
+ Ray(camera.position, (camera.lookAt - camera.position).getNormalized());
+ }
+
+ return action;
+}
+
+void Player::handlePlayerPosition(const Heightmap& heightmap,
+ const float& terrainHeight) {
+ const auto& leftJoy = pad->getLeftJoyPad();
+
+ auto normalizedCamera = Vec4(camera.unitCircle);
+ normalizedCamera.normalize();
+ normalizedCamera *= speed;
+
+ Vec4 nextPosition(position);
+ nextPosition.y = terrainHeight + 50.0F;
+
+ if (leftJoy.v <= 100) {
+ nextPosition.x += normalizedCamera.x;
+ nextPosition.z += normalizedCamera.z;
+ } else if (leftJoy.v >= 200) {
+ nextPosition.x += -normalizedCamera.x;
+ nextPosition.z += -normalizedCamera.z;
+ }
+ if (leftJoy.h <= 100) {
+ nextPosition.x += normalizedCamera.z;
+ nextPosition.z += -normalizedCamera.x;
+ } else if (leftJoy.h >= 200) {
+ nextPosition.x += -normalizedCamera.z;
+ nextPosition.z += normalizedCamera.x;
+ }
+
+ if (!heightmap.isOutside(nextPosition)) position = nextPosition;
+
+ camera.position = position;
+}
+
+} // namespace Demo
diff --git a/demo/src/states/game/player/weapon.cpp b/demo/src/states/game/player/weapon.cpp
new file mode 100644
index 0000000..c3144e6
--- /dev/null
+++ b/demo/src/states/game/player/weapon.cpp
@@ -0,0 +1,131 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/game/player/weapon.hpp"
+
+using Tyra::FileUtils;
+using Tyra::ObjLoader;
+using Tyra::ObjLoaderOptions;
+
+namespace Demo {
+
+Weapon::Weapon(Engine* engine) {
+ ObjLoader loader;
+
+ ObjLoaderOptions objOptions;
+ objOptions.flipUVs = true;
+ objOptions.scale = .5F;
+
+ pad = &engine->pad;
+ audio = &engine->audio;
+ auto* repo = &engine->renderer.core.texture.repository;
+ auto data =
+ loader.load(FileUtils::fromCwd("game/models/ak47/ak47.obj"), objOptions);
+ data->normalsEnabled = false;
+ mesh = new StaticMesh(data.get());
+
+ mesh->translation.translateX(2.85F);
+ mesh->translation.translateY(-3.40F);
+ mesh->translation.translateZ(-10.50F);
+
+ mesh->rotation.rotateY(1.60F);
+ mesh->rotation.rotateZ(-6.30F);
+
+ mesh->scale.scale(0.5F);
+
+ repo->addByMesh(mesh, FileUtils::fromCwd("game/models/ak47/"), "png");
+
+ initialPosition = *mesh->getPosition();
+
+ allocateOptions();
+
+ shootAdpcm =
+ audio->adpcm.load(FileUtils::fromCwd("game/models/ak47/ak47.adpcm"));
+
+ isShootAnimation1 = isShootAnimation2 = false;
+
+ adpcmChannelsCount = 8;
+ adpcmCurrentChannel = 0;
+
+ const u8 shootVolume = 25;
+ for (u8 i = 0; i < adpcmChannelsCount; i++) {
+ audio->adpcm.setVolume(shootVolume, i);
+ }
+}
+
+Weapon::~Weapon() {
+ delete mesh;
+ delete options;
+ delete shootAdpcm;
+}
+
+u8 Weapon::getShootChannel() {
+ auto result = adpcmCurrentChannel++;
+
+ if (adpcmCurrentChannel >= adpcmChannelsCount) {
+ adpcmCurrentChannel = 0;
+ }
+ return result;
+}
+
+void Weapon::update() {
+ isShooting = false;
+
+ if (!isShootAnimation1 && !isShootAnimation2 && pad->getPressed().Cross) {
+ shoot();
+ }
+
+ auto* position = mesh->getPosition();
+
+ const float recoil = 0.5F;
+ const float speed = 500;
+
+ if (isShootAnimation1) {
+ position->z += recoil;
+ position->y -= recoil / 4;
+ if (shootTimer.getTimeDelta() > speed) {
+ isShootAnimation1 = false;
+ shootTimer.prime();
+ isShootAnimation2 = true;
+ }
+ }
+
+ if (isShootAnimation2) {
+ position->z -= recoil / 2;
+ position->y += recoil / 8;
+ if (shootTimer.getTimeDelta() > speed * 2) {
+ isShootAnimation2 = false;
+ *position = initialPosition;
+ if (pad->getPressed().Cross) {
+ shoot();
+ }
+ }
+ }
+}
+
+void Weapon::shoot() {
+ isShooting = true;
+ shootTimer.prime();
+ audio->adpcm.tryPlay(shootAdpcm, getShootChannel());
+ isShootAnimation1 = true;
+}
+
+void Weapon::allocateOptions() {
+ options = new StaPipOptions();
+ options->textureMappingType = Tyra::TyraNearest;
+ options->shadingType = Tyra::TyraShadingFlat;
+ options->blendingEnabled = false;
+ options->fullClipChecks = false;
+ options->frustumCulling = Tyra::PipelineFrustumCulling_None;
+ options->transformationType = Tyra::TyraMP;
+ options->antiAliasingEnabled = false;
+}
+
+} // namespace Demo
diff --git a/demo/src/states/game/renderer/game_renderer.cpp b/demo/src/states/game/renderer/game_renderer.cpp
new file mode 100644
index 0000000..9be33c0
--- /dev/null
+++ b/demo/src/states/game/renderer/game_renderer.cpp
@@ -0,0 +1,89 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/game/renderer/game_renderer.hpp"
+
+using Tyra::Threading;
+
+namespace Demo {
+
+GameRenderer::GameRenderer(Renderer* t_renderer) {
+ renderer = t_renderer;
+
+ stpip.setRenderer(&renderer->core);
+ dypip.setRenderer(&renderer->core);
+}
+
+GameRenderer::~GameRenderer() {}
+
+void GameRenderer::add(std::vector t_staticPairs) {
+ staticPairs.insert(staticPairs.end(), t_staticPairs.begin(),
+ t_staticPairs.end());
+}
+
+void GameRenderer::add(std::vector t_dynamicPairs) {
+ dynamicPairs.insert(dynamicPairs.end(), t_dynamicPairs.begin(),
+ t_dynamicPairs.end());
+}
+
+void GameRenderer::add(Sprite* sprite) { sprites.push_back(sprite); }
+
+void GameRenderer::add(const CoreBBox& bbox) { bboxes.push_back(bbox); }
+
+void GameRenderer::add(RendererStaticPair* staticPair) {
+ staticPairs.push_back(staticPair);
+}
+
+void GameRenderer::add(RendererDynamicPair* dynamicPair) {
+ dynamicPairs.push_back(dynamicPair);
+}
+
+void GameRenderer::clear() {
+ staticPairs.clear();
+ dynamicPairs.clear();
+ sprites.clear();
+ bboxes.clear();
+}
+
+void GameRenderer::render() {
+ // Render static stuff
+ if (staticPairs.size()) {
+ renderer->renderer3D.usePipeline(&stpip);
+
+ for (auto& pair : staticPairs) {
+ stpip.render(pair->mesh, pair->options);
+ }
+ }
+
+ Threading::switchThread(); // give some time for audio thread
+
+ // Render animated stuff
+ if (dynamicPairs.size()) {
+ renderer->renderer3D.usePipeline(&dypip);
+
+ for (auto& pair : dynamicPairs) {
+ dypip.render(pair->mesh, pair->options);
+ }
+ }
+
+ Threading::switchThread();
+
+ // Render debug stuff after stapip/dynpip, otherwise it will not be visible
+ for (auto& bbox : bboxes) {
+ renderer->renderer3D.utility.drawBBox(bbox);
+ }
+
+ // Render 2D
+ for (auto& sprite : sprites) {
+ renderer->renderer2D.render(sprite);
+ }
+}
+
+} // namespace Demo
diff --git a/demo/src/states/game/ship/ship.cpp b/demo/src/states/game/ship/ship.cpp
new file mode 100644
index 0000000..37fee41
--- /dev/null
+++ b/demo/src/states/game/ship/ship.cpp
@@ -0,0 +1,50 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/game/ship/ship.hpp"
+
+using Tyra::FileUtils;
+using Tyra::ObjLoader;
+using Tyra::ObjLoaderOptions;
+
+namespace Demo {
+
+Ship::Ship(TextureRepository* repo) {
+ ObjLoader loader;
+
+ ObjLoaderOptions objOptions;
+ objOptions.flipUVs = true;
+ objOptions.scale = 200.0F;
+
+ auto data =
+ loader.load(FileUtils::fromCwd("game/models/ship/ship.obj"), objOptions);
+ mesh = new StaticMesh(data.get());
+
+ for (std::size_t i = 0; i < mesh->materials.size(); i++) {
+ mesh->materials[i]->ambient /= 2.0F;
+ mesh->materials[i]->ambient.a = 128.0F;
+ }
+
+ mesh->setPosition(Vec4(-800.0F, 10.0F, -2500.0F));
+
+ allocateOptions();
+
+ pair = new RendererStaticPair{mesh, options};
+}
+
+Ship::~Ship() {
+ delete mesh;
+ delete options;
+ delete pair;
+}
+
+void Ship::allocateOptions() { options = new StaPipOptions(); }
+
+} // namespace Demo
diff --git a/demo/src/states/game/skybox/skybox.cpp b/demo/src/states/game/skybox/skybox.cpp
new file mode 100644
index 0000000..a59acc4
--- /dev/null
+++ b/demo/src/states/game/skybox/skybox.cpp
@@ -0,0 +1,59 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/game/skybox/skybox.hpp"
+
+using Tyra::FileUtils;
+using Tyra::ObjLoader;
+using Tyra::ObjLoaderOptions;
+
+namespace Demo {
+
+Skybox::Skybox(TextureRepository* repo) {
+ ObjLoader loader;
+
+ ObjLoaderOptions objOptions;
+ objOptions.flipUVs = true;
+ objOptions.scale = 100.0F;
+
+ auto data = loader.load(FileUtils::fromCwd("game/models/skybox/skybox.obj"),
+ objOptions);
+ data->normalsEnabled = false;
+ mesh = new StaticMesh(data.get());
+
+ repo->addByMesh(mesh, FileUtils::fromCwd("game/models/skybox/"), "png");
+
+ allocateOptions();
+
+ pair = new RendererStaticPair{mesh, options};
+}
+
+Skybox::~Skybox() {
+ delete mesh;
+ delete options;
+ delete pair;
+}
+
+void Skybox::update(const Vec4& playerPosition) {
+ mesh->translation.identity();
+ mesh->translation.translate(playerPosition);
+}
+
+void Skybox::allocateOptions() {
+ options = new StaPipOptions();
+ options->frustumCulling = Tyra::PipelineFrustumCulling_None;
+ options->shadingType = Tyra::TyraShadingGouraud;
+ options->zTestType = Tyra::PipelineZTest_AllPass;
+ options->textureMappingType = Tyra::TyraNearest;
+ options->blendingEnabled = true;
+ options->antiAliasingEnabled = false;
+}
+
+} // namespace Demo
diff --git a/demo/src/states/game/terrain/heightmap.cpp b/demo/src/states/game/terrain/heightmap.cpp
new file mode 100644
index 0000000..95d7506
--- /dev/null
+++ b/demo/src/states/game/terrain/heightmap.cpp
@@ -0,0 +1,113 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/game/terrain/heightmap.hpp"
+
+using Tyra::FileUtils;
+using Tyra::PngLoader;
+using Tyra::PngPixel3;
+using Tyra::Vec4;
+
+namespace Demo {
+
+Heightmap::Heightmap(const float& t_minHeight, const float& t_maxHeight,
+ const Vec4& t_leftUp, const Vec4& t_rightDown)
+ : minHeight(t_minHeight),
+ maxHeight(t_maxHeight),
+ leftUp(t_leftUp),
+ rightDown(t_rightDown) {
+ PngLoader loader;
+ auto* data =
+ loader.load(FileUtils::fromCwd("game/models/terrain/heightmap.png"));
+
+ allocateMap(data->data, data->width, data->height);
+
+ delete data;
+}
+
+bool Heightmap::isOutside(const Vec4& position) const {
+ return position.x <= leftUp.x || position.x >= rightDown.x ||
+ position.z <= leftUp.z || position.z >= rightDown.z;
+}
+
+Heightmap::~Heightmap() {
+ if (map) {
+ for (int i = 0; i < mapHeight; i++) {
+ delete[] map[i];
+ }
+ delete[] map;
+ }
+}
+
+const float& Heightmap::getHeightOffset(const Vec4& playerPosition) const {
+ float widthPercentage = getWidthPercentage(playerPosition);
+ float heightPercentage = getHeightPercentage(playerPosition);
+
+ auto x = static_cast(widthPercentage * mapWidth);
+ auto y = static_cast(heightPercentage * mapHeight);
+
+ TYRA_ASSERT(x >= 0 && x < mapWidth, "x is out of range");
+ TYRA_ASSERT(y >= 0 && y < mapHeight, "y is out of range");
+
+ return map[x][y];
+}
+
+float Heightmap::getWidthPercentage(const Vec4& playerPosition) const {
+ return (playerPosition.x - leftUp.x) / (rightDown.x - leftUp.x);
+}
+
+float Heightmap::getHeightPercentage(const Vec4& playerPosition) const {
+ return (playerPosition.z - leftUp.z) / (rightDown.z - leftUp.z);
+}
+
+void Heightmap::allocateMap(const unsigned char* data, const int& width,
+ const int& height) {
+ auto* pixels = (struct PngPixel3*)data;
+
+ u8 min = pixels[0].r; // in grayscale every r,g,b is the same
+ u8 max = pixels[0].r;
+
+ // Get min and max
+ for (int i = 0; i < width * height; i++) {
+ const auto& value = pixels[i].r;
+ if (value < min) {
+ min = value;
+ }
+ if (value > max) {
+ max = value;
+ }
+ }
+
+ // Allocate
+ mapWidth = width;
+ mapHeight = height;
+ map = new float*[width];
+ for (int i = 0; i < width; i++) {
+ map[i] = new float[height];
+ }
+
+ // Fill
+ int k = 0;
+ for (int j = 0; j < height; j++) {
+ for (int i = 0; i < width; i++) {
+ map[i][j] = getGameHeight(pixels[k++].r, min, max);
+ }
+ }
+}
+
+float Heightmap::getGameHeight(const u8& inputColor, const u8& minColor,
+ const u8& maxColor) const {
+ const float mapRange = maxColor - minColor;
+ const float gameRange = maxHeight - minHeight;
+
+ return (inputColor - minColor) / mapRange * gameRange + minHeight;
+}
+
+} // namespace Demo
diff --git a/demo/src/states/game/terrain/terrain.cpp b/demo/src/states/game/terrain/terrain.cpp
new file mode 100644
index 0000000..6848bfc
--- /dev/null
+++ b/demo/src/states/game/terrain/terrain.cpp
@@ -0,0 +1,60 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/game/terrain/terrain.hpp"
+
+using Tyra::FileUtils;
+using Tyra::ObjLoader;
+using Tyra::ObjLoaderOptions;
+
+namespace Demo {
+Terrain::Terrain(TextureRepository* repo)
+ : heightmap(-70.0F, // Min height
+ 240.0F, // Max height
+ Vec4(-750.0F, 0.0F, -1400.0F, 1.0F), // Left up
+ Vec4(920.0F, 0.0F, 1520.0F, 1.0F) // Right down
+ ) {
+ ObjLoader loader;
+
+ ObjLoaderOptions objOptions;
+ objOptions.flipUVs = true;
+ objOptions.scale = 100.0F;
+
+ auto data = loader.load(FileUtils::fromCwd("game/models/terrain/terrain.obj"),
+ objOptions);
+ data->normalsEnabled = false;
+ mesh = new StaticMesh(data.get());
+
+ repo->addByMesh(mesh, FileUtils::fromCwd("game/models/terrain/"), "png");
+
+ allocateOptions();
+
+ pair = new RendererStaticPair{mesh, options};
+}
+
+float Terrain::getHeightOffset(const Vec4& playerPosition) {
+ return heightmap.getHeightOffset(playerPosition);
+}
+
+Terrain::~Terrain() {
+ delete mesh;
+ delete options;
+ delete pair;
+}
+
+void Terrain::allocateOptions() {
+ options = new StaPipOptions();
+ options->frustumCulling = Tyra::PipelineFrustumCulling_None;
+ options->shadingType = Tyra::TyraShadingGouraud;
+ options->blendingEnabled = true;
+ options->antiAliasingEnabled = false;
+}
+
+} // namespace Demo
diff --git a/demo/src/states/intro/intro_state.cpp b/demo/src/states/intro/intro_state.cpp
new file mode 100644
index 0000000..c4560b1
--- /dev/null
+++ b/demo/src/states/intro/intro_state.cpp
@@ -0,0 +1,62 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/intro/intro_state.hpp"
+
+#include "states/intro/states/intro_ps2dev_state.hpp"
+#include "states/intro/states/intro_tyra_state.hpp"
+#include "states/intro/states/intro_press_key_state.hpp"
+
+using Tyra::FileUtils;
+
+namespace Demo {
+
+IntroState::IntroState(Engine* t_engine)
+ : State(t_engine), stateManager(STATE_PS2DEV, STATE_INTRO_END) {
+ state = STATE_INTRO;
+ _wantFinish = false;
+ initialized = false;
+}
+
+IntroState::~IntroState() {}
+
+void IntroState::onStart() {
+ TYRA_LOG("Intro. RAM: ", engine->info.getAvailableRAM(), "MB");
+
+ engine->audio.song.load(FileUtils::fromCwd("intro/intro.wav"));
+ engine->audio.song.inLoop = false;
+ engine->audio.song.setVolume(80);
+ engine->audio.song.play();
+
+ stateManager.add(new IntroPs2DevState(engine));
+ stateManager.add(new IntroTyraState(engine));
+ stateManager.add(new IntroPressKeyState(engine));
+
+ initialized = true;
+}
+
+GlobalStateType IntroState::onFinish() {
+ if (!initialized) return STATE_LOADING;
+
+ stateManager.freeAll();
+
+ initialized = false;
+ return STATE_LOADING;
+}
+
+void IntroState::update() {
+ stateManager.update();
+
+ if (stateManager.finished()) {
+ _wantFinish = true;
+ }
+}
+
+} // namespace Demo
diff --git a/demo/src/states/intro/states/intro_press_key_state.cpp b/demo/src/states/intro/states/intro_press_key_state.cpp
new file mode 100644
index 0000000..8e7b691
--- /dev/null
+++ b/demo/src/states/intro/states/intro_press_key_state.cpp
@@ -0,0 +1,198 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/intro/states/intro_press_key_state.hpp"
+#include
+
+using Tyra::FileUtils;
+using Tyra::Math;
+using Tyra::PngLoader;
+using Tyra::SpriteMode;
+using Tyra::Threading;
+
+namespace Demo {
+
+IntroPressKeyState::IntroPressKeyState(Engine* t_engine)
+ : State(t_engine), mapPosition(0.0F, 0.0F) {
+ state = STATE_PRESS_KEY;
+ _wantFinish = false;
+ mapDirection = 0;
+ mapFadeIn = true;
+ fillerFadeIn = false;
+ showPressKey = false;
+ pressKeyAlphaToggle = false;
+ pressKeyAlphaDelayer = 0;
+ fillersOffset = -512.0F;
+}
+
+IntroPressKeyState::~IntroPressKeyState() {}
+
+void IntroPressKeyState::onStart() {
+ TYRA_LOG("Intro - presskey. RAM: ", engine->info.getAvailableRAM(), "MB");
+
+ for (u8 i = 0; i < mapRows; i++) {
+ for (u8 j = 0; j < mapCols; j++) {
+ mapSprites[i][j] = new Sprite;
+ mapSprites[i][j]->color.a = 0.0F;
+ mapSprites[i][j]->mode = SpriteMode::MODE_STRETCH;
+ mapSprites[i][j]->size.set(textureWidthHeight, textureWidthHeight);
+ mapSprites[i][j]->position.set(i * textureWidthHeight,
+ j * textureWidthHeight);
+
+ std::string filename =
+ "map-" + std::to_string(i) + "-" + std::to_string(j);
+ std::string path = "intro/map/" + filename + ".png";
+
+ mapTextures[i][j] = engine->renderer.core.texture.repository.add(
+ FileUtils::fromCwd(path));
+ mapTextures[i][j]->addLink(mapSprites[i][j]->id);
+ }
+ }
+
+ fillerSprite = new Sprite;
+ fillerSprite->size.set(256.0F, 50.0F);
+ fillerSprite->position.set(0.0F, 224.0F - 25.0F);
+
+ fillerTexture = engine->renderer.core.texture.repository.add(
+ FileUtils::fromCwd("intro/filler.png"));
+ fillerTexture->addLink(fillerSprite->id);
+
+ pressKeySprite = new Sprite;
+ pressKeySprite->size.set(256.0F, 64.0F);
+ pressKeySprite->scale = 0.5F;
+ pressKeySprite->position.set(192.0F, 380.0F);
+
+ pressKeyTexture = engine->renderer.core.texture.repository.add(
+ FileUtils::fromCwd("intro/press-key.png"));
+ pressKeyTexture->addLink(pressKeySprite->id);
+
+ logoSprite = new Sprite;
+ logoSprite->size.set(256.0F, 128.0F);
+ logoSprite->position.set(256.0F - 128.0F, 224.0F - 64.0F);
+
+ logoTexture = engine->renderer.core.texture.repository.add(
+ FileUtils::fromCwd("intro/lastalive.png"));
+ logoTexture->addLink(logoSprite->id);
+
+ initialized = true;
+}
+
+IntroStateType IntroPressKeyState::onFinish() {
+ if (!initialized) return STATE_INTRO_END;
+
+ for (u8 i = 0; i < mapRows; i++)
+ for (u8 j = 0; j < mapCols; j++) {
+ engine->renderer.core.texture.repository.free(mapTextures[i][j]->id);
+ delete mapSprites[i][j];
+ }
+
+ engine->renderer.core.texture.repository.free(fillerTexture->id);
+ delete fillerSprite;
+
+ engine->renderer.core.texture.repository.free(logoTexture->id);
+ delete logoSprite;
+
+ engine->renderer.core.texture.repository.free(pressKeyTexture->id);
+ delete pressKeySprite;
+
+ initialized = false;
+ return STATE_INTRO_END;
+}
+
+void IntroPressKeyState::update() {
+ engine->renderer.beginFrame();
+
+ if (mapDirection == 0 &&
+ mapPosition.x >= textureWidthHeight * (mapRows - 1) &&
+ mapPosition.y >= textureWidthHeight * (mapCols - 1)) {
+ mapDirection = 1;
+ } else if (mapDirection == 1 && mapPosition.x <= 0.0F &&
+ mapPosition.y <= 0.0F) {
+ mapDirection = 0;
+ }
+
+ if (engine->pad.getClicked().Cross) {
+ _wantFinish = true;
+ }
+
+ const float offset = 0.1F;
+ if (mapDirection == 0) {
+ mapPosition += offset;
+ } else if (mapDirection == 1) {
+ mapPosition -= offset;
+ }
+
+ if (mapFadeIn) {
+ for (u8 i = 0; i < mapRows; i++) {
+ for (u8 j = 0; j < mapCols; j++) {
+ auto* sprite = mapSprites[i][j];
+ if (sprite->color.a >= 16.0F && sprite->color.a < 32.0F) {
+ fillerFadeIn = true;
+ }
+ if (sprite->color.a < 96.0F) {
+ sprite->color.a += 0.2F;
+ } else {
+ mapFadeIn = false;
+ }
+ }
+ }
+ }
+
+ if (fillerFadeIn) {
+ if (fillersOffset < -1.0F) {
+ fillersOffset += 4.0F;
+ } else {
+ fillerFadeIn = false;
+ showPressKey = true;
+ }
+ }
+
+ updateMap();
+
+ Threading::switchThread();
+
+ for (u8 i = 0; i < mapRows; i++)
+ for (u8 j = 0; j < mapCols; j++)
+ engine->renderer.renderer2D.render(mapSprites[i][j]);
+
+ renderFiller();
+ engine->renderer.renderer2D.render(logoSprite);
+
+ if (showPressKey) {
+ if (pressKeyAlphaDelayer++ > 50) {
+ pressKeyAlphaToggle = !pressKeyAlphaToggle;
+ pressKeyAlphaDelayer = 0;
+ }
+
+ if (pressKeyAlphaToggle) {
+ engine->renderer.renderer2D.render(pressKeySprite);
+ }
+ }
+
+ engine->renderer.endFrame();
+}
+
+void IntroPressKeyState::renderFiller() {
+ fillerSprite->position.x = -1.0F + fillersOffset;
+ engine->renderer.renderer2D.render(fillerSprite);
+ fillerSprite->position.x = 255.0F + fillersOffset;
+ engine->renderer.renderer2D.render(fillerSprite);
+}
+
+void IntroPressKeyState::updateMap() {
+ for (u8 i = 0; i < mapRows; i++)
+ for (u8 j = 0; j < mapCols; j++) {
+ mapSprites[i][j]->position.x =
+ i * (textureWidthHeight + 1.0F) - mapPosition.x;
+ mapSprites[i][j]->position.y =
+ j * (textureWidthHeight + 1.0F) - mapPosition.y;
+ }
+}
+} // namespace Demo
diff --git a/demo/src/states/intro/states/intro_ps2dev_state.cpp b/demo/src/states/intro/states/intro_ps2dev_state.cpp
new file mode 100644
index 0000000..e5cad85
--- /dev/null
+++ b/demo/src/states/intro/states/intro_ps2dev_state.cpp
@@ -0,0 +1,104 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/intro/states/intro_ps2dev_state.hpp"
+
+using Tyra::FileUtils;
+using Tyra::PngLoader;
+using Tyra::Threading;
+
+namespace Demo {
+
+IntroPs2DevState::IntroPs2DevState(Engine* t_engine) : State(t_engine) {
+ state = STATE_PS2DEV;
+ _wantFinish = false;
+ initialized = false;
+ initialDelayElapsed = false;
+ frameSkipper = 0;
+ fadeinActivated = true;
+ fadeoutActivated = false;
+}
+
+IntroPs2DevState::~IntroPs2DevState() {}
+
+void IntroPs2DevState::onStart() {
+ TYRA_LOG("Intro - ps2dev. RAM: ", engine->info.getAvailableRAM(), "MB");
+
+ const auto& settings = engine->renderer.core.getSettings();
+
+ initialDelayTimer.prime();
+
+ sprite = new Sprite;
+ sprite->size.set(256.0F, 64.0F);
+ sprite->position.set(settings.getWidth() / 2 - sprite->size.x / 2,
+ settings.getHeight() / 2 - sprite->size.y / 2);
+ sprite->color.a = 0;
+
+ Threading::switchThread();
+
+ texture = engine->renderer.core.texture.repository.add(
+ FileUtils::fromCwd("intro/ps2dev.png"));
+ texture->addLink(sprite->id);
+
+ initialized = true;
+}
+
+IntroStateType IntroPs2DevState::onFinish() {
+ if (!initialized) return STATE_TYRA;
+
+ delete sprite;
+ engine->renderer.core.texture.repository.free(texture->id);
+
+ initialized = false;
+ return STATE_TYRA;
+}
+
+void IntroPs2DevState::update() {
+ engine->renderer.beginFrame();
+
+ if (initialDelayTimer.getTimeDelta() >= 60000) initialDelayElapsed = true;
+
+ if (engine->pad.getClicked().Cross) {
+ _wantFinish = true;
+ }
+
+ frameSkipper++;
+ if (frameSkipper > 3) {
+ frameSkipper = 0;
+ }
+
+ if (fadeinActivated && initialDelayElapsed && frameSkipper == 0) {
+ if (sprite->color.a < 128.0F)
+ sprite->color.a += 2.0F;
+ else {
+ fadeoutActivated = true;
+ fadeinActivated = false;
+ }
+ if (sprite->color.a >= 96.0F && sprite->color.a < 128.0F) {
+ sprite->color.g -= 6.0F;
+ sprite->color.b -= 6.0F;
+ }
+ }
+
+ Threading::switchThread();
+
+ if (fadeoutActivated && frameSkipper == 0) {
+ if (sprite->color.a > 0)
+ sprite->color.a -= 4;
+ else
+ _wantFinish = true;
+ }
+
+ engine->renderer.renderer2D.render(sprite);
+
+ engine->renderer.endFrame();
+}
+
+} // namespace Demo
diff --git a/demo/src/states/intro/states/intro_tyra_state.cpp b/demo/src/states/intro/states/intro_tyra_state.cpp
new file mode 100644
index 0000000..5cbdde8
--- /dev/null
+++ b/demo/src/states/intro/states/intro_tyra_state.cpp
@@ -0,0 +1,180 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include "states/intro/states/intro_tyra_state.hpp"
+
+using Tyra::FileUtils;
+using Tyra::Math;
+using Tyra::PngLoader;
+using Tyra::Threading;
+
+namespace Demo {
+
+IntroTyraState::IntroTyraState(Engine* t_engine) : State(t_engine) {
+ state = STATE_TYRA;
+ _wantFinish = false;
+ initialized = false;
+ initialDelayElapsed = false;
+ animActivated = true;
+ fadeoutActivated = false;
+ frameSkipper = 0;
+}
+
+IntroTyraState::~IntroTyraState() {}
+
+void IntroTyraState::onStart() {
+ TYRA_LOG("Intro - tyra. RAM: ", engine->info.getAvailableRAM(), "MB");
+
+ const auto& settings = engine->renderer.core.getSettings();
+
+ initialDelayTimer.prime();
+
+ tyraSprite = new Sprite;
+ tyraSprite->size.set(256.0F, 64.0F);
+ tyraSprite->position.set(settings.getWidth() / 2 - tyraSprite->size.x / 2,
+ settings.getHeight() / 2 - tyraSprite->size.y / 2);
+
+ bgSprite = new Sprite;
+ bgSprite->size.set(256.0F, 256.0F);
+ bgSprite->position.set(settings.getWidth() / 2 - bgSprite->size.x / 2,
+ settings.getHeight() / 3 * 2.0F);
+
+ bg2Sprite = new Sprite;
+ bg2Sprite->size.set(256.0F, 256.0F);
+ bg2Sprite->position.set(
+ settings.getWidth() / 2 - bg2Sprite->size.x / 2,
+ (settings.getHeight() / 3 * 2.0F) + bg2Sprite->size.y + 1.0F);
+
+ fillerSprite = new Sprite;
+ fillerSprite->size.set(256.0F, 256.0F);
+ fillerSprite->position.set(0.0F, 0.0F);
+
+ tyraTexture = engine->renderer.core.texture.repository.add(
+ FileUtils::fromCwd("intro/tyra.png"));
+ tyraTexture->addLink(tyraSprite->id);
+
+ bgTexture = engine->renderer.core.texture.repository.add(
+ FileUtils::fromCwd("intro/tyra_bg.png"));
+ bgTexture->addLink(bgSprite->id);
+
+ Threading::switchThread();
+
+ bg2Texture = engine->renderer.core.texture.repository.add(
+ FileUtils::fromCwd("intro/tyra_bg2.png"));
+ bg2Texture->addLink(bg2Sprite->id);
+
+ fillerTexture = engine->renderer.core.texture.repository.add(
+ FileUtils::fromCwd("intro/filler.png"));
+ fillerTexture->addLink(fillerSprite->id);
+
+ initialized = true;
+}
+
+IntroStateType IntroTyraState::onFinish() {
+ if (!initialized) return STATE_PRESS_KEY;
+
+ engine->renderer.core.texture.repository.free(tyraTexture->id);
+ engine->renderer.core.texture.repository.free(bgTexture->id);
+ engine->renderer.core.texture.repository.free(bg2Texture->id);
+ engine->renderer.core.texture.repository.free(fillerTexture->id);
+
+ delete tyraSprite;
+ delete bgSprite;
+ delete bg2Sprite;
+ delete fillerSprite;
+
+ initialized = false;
+ return STATE_PRESS_KEY;
+}
+
+void IntroTyraState::update() {
+ engine->renderer.beginFrame();
+
+ if (initialDelayTimer.getTimeDelta() >= 15000) initialDelayElapsed = true;
+
+ frameSkipper++;
+ if (frameSkipper > 3) {
+ frameSkipper = 0;
+ }
+
+ if (engine->pad.getClicked().Cross) {
+ _wantFinish = true;
+ }
+
+ const float slideUpMaxPost = -128.0F;
+
+ if (initialDelayElapsed) {
+ if (bgSprite->position.y <= 32.0F) fadeoutActivated = true;
+
+ float offset = 1.0F;
+ if (bgSprite->position.y > slideUpMaxPost && bgSprite->position.y <= 64.0F)
+ offset /= 1.6F;
+ else if (bgSprite->position.y > 64.0F && bgSprite->position.y <= 128.0F)
+ offset /= 1.2F;
+
+ if (bgSprite->position.y > 32.0F && bgSprite->position.y <= 64.0F &&
+ bgSprite->color.g > 0.0F) {
+ const float colorOffset = 1.5F;
+ bgSprite->color.g -= colorOffset;
+ bgSprite->color.b -= colorOffset;
+ bg2Sprite->color.g -= colorOffset;
+ bg2Sprite->color.b -= colorOffset;
+ }
+
+ if (bgSprite->position.y > slideUpMaxPost) {
+ bgSprite->position.y -= offset;
+ bg2Sprite->position.y -= offset;
+ }
+ }
+
+ if (fadeoutActivated && frameSkipper == 0) {
+ if (bgSprite->color.a > 0) {
+ bgSprite->color.a -= 4.0F;
+ bg2Sprite->color.a -= 4.0F;
+ } else
+ _wantFinish = true;
+ }
+
+ Threading::switchThread();
+
+ engine->renderer.renderer2D.render(bgSprite);
+ engine->renderer.renderer2D.render(bg2Sprite);
+ engine->renderer.renderer2D.render(tyraSprite);
+
+ renderFillers();
+
+ engine->renderer.endFrame();
+}
+
+void IntroTyraState::renderFillers() {
+ // Up
+ fillerSprite->position.set(0.0F, -63.0F);
+ engine->renderer.renderer2D.render(fillerSprite);
+
+ fillerSprite->position.set(256.0F, -63.0F);
+ engine->renderer.renderer2D.render(fillerSprite);
+
+ // Left
+ fillerSprite->position.set(-124.0F, 128.0F);
+ engine->renderer.renderer2D.render(fillerSprite);
+
+ // Down
+ fillerSprite->position.set(0.0F, 256.0F);
+ engine->renderer.renderer2D.render(fillerSprite);
+
+ fillerSprite->position.set(256.0F, 256.0F);
+ engine->renderer.renderer2D.render(fillerSprite);
+
+ // Right
+ fillerSprite->position.set(386.0F, 128.0F);
+ engine->renderer.renderer2D.render(fillerSprite);
+}
+
+} // namespace Demo
diff --git a/demo/src/states/loading/loading_state.cpp b/demo/src/states/loading/loading_state.cpp
new file mode 100644
index 0000000..21b8c59
--- /dev/null
+++ b/demo/src/states/loading/loading_state.cpp
@@ -0,0 +1,72 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#include
+#include "states/loading/loading_state.hpp"
+#include
+
+using std::make_unique;
+using std::unique_ptr;
+using Tyra::FileUtils;
+using Tyra::Sprite;
+
+namespace Demo {
+
+LoadingState::LoadingState(Engine* t_engine) : State(t_engine) {
+ state = STATE_LOADING;
+ _wantFinish = false;
+ initialized = false;
+}
+
+LoadingState::~LoadingState() {}
+
+void LoadingState::onStart() {
+ TYRA_LOG("Loading. RAM: ", engine->info.getAvailableRAM(), "MB");
+
+ auto* repo = &engine->renderer.core.texture.repository;
+
+ auto fillerTexture = repo->add(FileUtils::fromCwd("loading/filler.png"));
+ auto loadingTexture = repo->add(FileUtils::fromCwd("loading/loading.png"));
+
+ auto fillerSprite = make_unique();
+ auto loadingSprite = make_unique();
+
+ fillerSprite->position.set(0.0F, 0.0F);
+ fillerSprite->size.set(600.0F, 600.0F);
+ fillerSprite->mode = Tyra::MODE_STRETCH;
+
+ loadingSprite->size.set(128.0F, 64.0F);
+ loadingSprite->mode = Tyra::MODE_STRETCH;
+ loadingSprite->position.set(5.0F, 448.0F - 64.0F - 5.0F);
+
+ fillerTexture->addLink(fillerSprite->id);
+ loadingTexture->addLink(loadingSprite->id);
+
+ engine->renderer.beginFrame();
+ engine->renderer.renderer2D.render(fillerSprite.get());
+ engine->renderer.renderer2D.render(loadingSprite.get());
+ engine->renderer.endFrame();
+
+ repo->free(fillerTexture->id);
+ repo->free(loadingTexture->id);
+
+ initialized = true;
+}
+
+GlobalStateType LoadingState::onFinish() {
+ if (!initialized) return STATE_GAME;
+
+ initialized = false;
+ return STATE_GAME;
+}
+
+void LoadingState::update() { _wantFinish = true; }
+
+} // namespace Demo
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..ff8ece3
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,14 @@
+version: "3.9"
+volumes:
+ tyra-volume:
+services:
+ compiler:
+ environment:
+ TERM: xterm-256color
+ network_mode: host
+ image: "h4570/tyra"
+ tty: true
+ container_name: tyra-compiler
+ volumes:
+ - tyra-volume:/src
+ - ./:/host
diff --git a/docs/install.md b/docs/install.md
deleted file mode 100644
index dc05168..0000000
--- a/docs/install.md
+++ /dev/null
@@ -1,254 +0,0 @@
-# Installation tutorial
-
-This tutorial will show you, how to:
-
-- Setup your WSL machine for PS2DEV
-- Install entire PS2DEV+Tyra environment
-- Setup VSCode with Intelisense
-- Setup PCSX2
-
-So let's get started!
-
----
-
-
-
-## VSCode and WSL
-
-
-- Install VSCode from
- https://code.visualstudio.com/
-- Install WSL https://docs.microsoft.com/en-us/windows/wsl/install-win10 `Info: Please choose Ubuntu-20.04`
-- Set root as default WSL user.
- Open ` cmd.exe` and type:
- ` ubuntu2004 config --default-user root`
-
-
----
-
-
-
-## Ubuntu configuration and PS2DEV+Tyra
-
-- Open WSL terminal (Windows search -> wsl)`Info: Be sure that are you logged as a root!`
-- Type these commands
-
-```
-apt-get update && apt-get upgrade -y
-apt-get install -y build-essential make texinfo cmake bison flex gettext libgmp3-dev libmpfr-dev libmpc-dev libz-dev wget patch
-```
-
-- Configure git (replace your name and email)
-
-```
-git config --global user.email "your@email.com"
-git config --global user.name "Your Name"
-```
-
-- Create directories
-
-```
-mkdir /ps2dev
-mkdir /ps2dev/built
-mkdir /ps2dev/source
-mkdir /repos
-mkdir /repos/tyra
-```
-
-- Clone repos
-
-```
-git clone https://github.com/ps2dev/ps2dev.git /ps2dev/source/
-git clone https://github.com/h4570/tyra.git /repos/tyra/
-```
-
-- Open bashrc via `nano ~/.bashrc` and add these lines at the end of file. Be sure that they are 1:1, so don't add additional spaces!
-
-```
-export PS2DEV=/ps2dev/built
-export PS2SDK=$PS2DEV/ps2sdk
-export GSKIT=$PS2DEV/gsKit
-export PATH=$PATH:$PS2DEV/bin:$PS2DEV/ee/bin:$PS2DEV/iop/bin:$PS2DEV/dvp/bin:$PS2SDK/bin
-export PS2HOSTNAME=192.168.0.200
-export TYRA=/repos/tyra
-export WSL_MAKE_WINDOWS=\\\\\\\\wsl\$\$\\\\Ubuntu-20.04
-export WSL_LINUX_PCSX2=/mnt/c/Program\\\ Files\\\ \\\(x86\\\)/PCSX2
-```
-
-- Refresh env variables via -> `. ~/.bashrc`
-- Close wsl terminal
-
----
-
-
-
-## Mapping WSL directories to your Windows machine
-
-- Open cmd and type `wsl.exe --list``Info: Remember your distribution name from this list. My is "Ubuntu-20.04"`
-- Click at the windows logo on your keyboard, type "This PC" and open
-- At the upper menu click "Map disk drive"`Info: Replace my name with your WSL name!`
- - Folder: \\\\wsl$\\Ubuntu-20.04
- - Click OK
-
-`Info: You will have now new disk on your PC, this is the WSL filesystem.`
-
----
-
-
-
-## Configuring VSCode
-
-- Open VSCode
-- Install extension "Remote - WSL"
-- Install extension "C/C++"
-- Restart VSCode if required
-- VSCode: Click on "File" -> "Open folder in WSL"
- - Open "repos/tyra"
-- VSCode: Click on "Terminal" -> "New terminal"
-
-`Info: New terminal should appear. This is the same WSL terminal :)`
-
----
-
-
-
-## PS2DEV installation
-
-- In VSCode's WSL terminal type:
-
-```
-cd /ps2dev/source
-unset ZLIB
-unset LIBTIFF
-unset LIBJPEG
-unset LIBPNG
-./build-all.sh
-```
-
-`Info: This can take some time...`
-
-- And open again .bashrc via `nano ~/.bashrc` and add these lines at the end of file.
-
-```
-export ZLIB=${PS2SDK}/ports
-export LIBTIFF=${PS2SDK}/ports
-export LIBJPEG=${PS2SDK}/ports
-export LIBPNG=${PS2SDK}/ports
-```
-
-- Refresh env variables via `. ~/.bashrc`
-
-### Please don't forgot to remove/refresh these 4 exports when you will want to update PS2DEV (./build-all.sh), because there is circular dependency between these libs and gsKit which is included in PS2DEV!!!
-
----
-
-
-
-## Build Tyra
-
-- Just hit make all on the root of tyra folder or you can go via `cd /src/engine` and hit `make all` there.
-- If you wanna build the debug build or rebuild the engine, just `make rebuild-engine ` on the root of tyra or `make rebuild-dbg-engine` to build debug version or go to one of the samples via `cd /repos/tyra/src/samples/floors `and do the same process above
-
-
-
-## Install & configure PCSX2
-
-- Download & install PCSX2 from (1.6+ version)https://pcsx2.net/
-- Extract bios files fromhttps://romsmania.cc/bios/pcsx2-playstation-2-bios-3to `C:\Users%USERNAME%\Documents\PCSX2\bios`
-- Close `PCSX2` if you have opened!
-- Open `C:\Users\hasto\Documents\PCSX2\inis\PCSX2_vm.ini` in notepad
-
-```
-HostFs=enabled
-```
-
-- Open PCSX2 and map pad to your keyboard, for example:
-
-```
-WASD = Left joy
-Arrows = Right joy
-Space = X
-Circle = C
-```
-
----
-
-
-
-## Run one of the samples - Method 1
-
-- Add assets for "floors" sample
-- Download assets from https://github.com/h4570/tyra/tree/master/src/samples/floors
-- Windows: Open your WSL folder (This PC -> WSL folder)
-- Extract assets to /repos/tyra/src/samples/floors/bin/ (create bin folder if not exists)
-- Compile sample via `make``Info: Elf file should be produced in /bin `
-- Run sample in PCSX2 via `make run-pcsx2`
-
-## Run one of the samples Method 2
-
-- Run `make floors` on the root of tyra project or change directory to the sampels via `cd src/samples/floors` and do the `make install` command
-
----
-
-
-
-## How to code?
-
-Do some changes in your sample and just type `make clean all run-pcsx2` in your sample folder.
-You can create your own sample, by just copying cube folder.
-
----
-
-
-
-## Setup Intelisense
-
-
-- Download
- http://apgcglz.cluster028.hosting.ovh.net/tyra/intellisense.zip
-- Unpack .vscode folder to WSL's `/repos/tyra/Info: This configuration is set for cube sample, but you can change it in tasks.json file`
-
-
----
-
-
-
-## Advanced: Compiling VU1 programs
-
-`Info: Based on https://github.com/microsoft/wsl/issues/2468#issuecomment-374904520`
-
-- Download VCL
- http://lukasz.dk/files/vcl.1.4.beta7.x86.tgz
-- Rename `vcl_14beta7_x86` binary to `vcl`
-- Put `vcl` to WSL's `/usr/bin`
-
-```
-apt-get install qemu qemu-user-static binfmt-support
-update-binfmts --install i386 /usr/bin/qemu-i386-static --magic '\x7fELF\x01\x01\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x03\x00\x01\x00\x00\x00' --mask '\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xf8\xff\xff\xff\xff\xff\xff\xff'
-dpkg --add-architecture i386
-apt-get update
-apt-get install libstdc++5:i386
-chmod 755 /usr/bin/vcl
-```
-
-`Info: In Ubuntu 18.04 I was obligated to install lib32ncurses5 also`
-
-- Add this line to `~/.bashrc`
-
-```
-service binfmt-support start &>/dev/null
-```
-
-- Uncomment `.vsm`/`.vcl` recipe from Tyra's engine `Makefile`
-- Download & install VCL preprocessor
-
-```
-mkdir temp && cd temp
-git clone https://github.com/glampert/vclpp .
-make
-cp vclpp /usr/bin/
-chmod 755 /usr/bin/vclpp
-cd .. && rm -rf ./temp
-```
-
-- Uncomment `.vclpp`/`.vcl` recipe from Tyra's engine `Makefile`
diff --git a/docs/install/README.MD b/docs/install/README.MD
new file mode 100644
index 0000000..ba2237e
--- /dev/null
+++ b/docs/install/README.MD
@@ -0,0 +1,25 @@
+# Installing Tyra
+
+[](#)
+
+## Prerequisites:
+1. Git
+2. VSCode
+3. Docker with PowerShell as a default terminal
+4. [PCSX2](https://github.com/h4570/tyra/blob/master/docs/install/pcsx2.md#)
+
+---
+
+## Steps:
+1. Download [intellisense package](http://apgcglz.cluster028.hosting.ovh.net/tyra/ps2dev-intellisense.rar) and unzip it somewhere in your computer.
+2. Open terminal in your favourite repositories directory and execute:
+```
+docker pull h4570/tyra
+git clone https://github.com/h4570/tyra.git
+```
+3. Open `VSCode` and open previously cloned directory.
+4. In `VSCode` open `.vscode/configurations/windows-docker` and copy configuration files into `.vscode/`
+5. In `c_cpp_properties.json` fix all `YOUR-INTELLISENSE-PATH` with path from point 2.
+6. Click `CTRL+SHIFT+P`, type `Tasks: Run task` and run `Start docker container` - Repeat this step every time you want to start working with Tyra (every reboot)
+7. Click `CTRL+SHIFT+P`, type `Tasks: Run build task`
+8. Tyra should automatically compile engine, `demo` game and run `PCSX2`. If you want to compile & run tutorials, please change `buildDirectory` in `settings.json` to example `tutorials/01-hello`
\ No newline at end of file
diff --git a/docs/install/pcsx2.md b/docs/install/pcsx2.md
new file mode 100644
index 0000000..f2d5d7b
--- /dev/null
+++ b/docs/install/pcsx2.md
@@ -0,0 +1,21 @@
+## Install & configure PCSX2
+
+- Download & install PCSX2 (1.6+ version) from https://pcsx2.net/
+- Extract bios files from https://romsmania.cc/bios/pcsx2-playstation-2-bios-3 to `%UserProfile%\Documents\PCSX2\bios`
+- Close `PCSX2` if you have opened!
+- Open `%UserProfile%\Documents\PCSX2\inis\PCSX2_vm.ini` in notepad
+
+```
+HostFs=enabled
+```
+
+- Open PCSX2 and map pad to your keyboard, for example:
+
+```
+WASD = Left joy
+Arrows = Right joy
+Space = X
+Circle = C
+```
+
+- Enable **"EE timing hack"** in PCSX2 hacks!
\ No newline at end of file
diff --git a/engine/Makefile b/engine/Makefile
new file mode 100644
index 0000000..a0bb104
--- /dev/null
+++ b/engine/Makefile
@@ -0,0 +1,26 @@
+TARGET := libtyra.a
+
+#The Directories, Source, Includes, Objects, Binary and Resources
+SRCDIR := src
+INCDIR := inc
+BUILDDIR := obj
+TARGETDIR := bin
+RESDIR := res
+SRCEXT := cpp
+VSMEXT := vsm
+VCLEXT := vcl
+VCLPPEXT := vclpp
+DEPEXT := d
+OBJEXT := o
+
+#Flags, Libraries and Includes
+CFLAGS :=
+LIB :=
+LIBDIRS :=
+INC := -I$(INCDIR)
+INCDEP := -I$(INCDIR)
+
+include ../Makefile.base
+
+$(TARGET): $(OBJECTS) $(VCL_OBJECTS) $(VU_OBJECTS_VCL) $(VU_OBJECTS_VSM) $(IRXEM_OBJECTS)
+ $(AR) rcs $(TARGETDIR)/$(TARGET) $(OBJECTS) $(VCL_OBJECTS) $(VU_OBJECTS_VCL) $(VU_OBJECTS_VSM) $(IRXEM_OBJECTS)
diff --git a/src/unit_tests/bin/.gitignore b/engine/bin/.gitignore
similarity index 95%
rename from src/unit_tests/bin/.gitignore
rename to engine/bin/.gitignore
index 86d0cb2..44c5ea8 100644
--- a/src/unit_tests/bin/.gitignore
+++ b/engine/bin/.gitignore
@@ -1,4 +1,4 @@
-# Ignore everything in this directory
-*
-# Except this file
+# Ignore everything in this directory
+*
+# Except this file
!.gitignore
\ No newline at end of file
diff --git a/engine/inc/audio/adpcm_result.hpp b/engine/inc/audio/adpcm_result.hpp
new file mode 100644
index 0000000..7505c69
--- /dev/null
+++ b/engine/inc/audio/adpcm_result.hpp
@@ -0,0 +1,28 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022-2022, Tyra - https://github.com/h4570/tyrav2
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+# Wellinator Carvalho
+*/
+
+#pragma once
+
+namespace Tyra {
+
+enum AdpcmResult {
+ ADPCM_OK,
+
+ /** When provided channel by user is currently in use */
+ ADPCM_CHANNEL_USED,
+
+ /** When user did not provided channel and all are in use */
+ ADPCM_NO_FREE_CHANNELS,
+
+ ADPCM_ERROR
+};
+
+} // namespace Tyra
diff --git a/engine/inc/audio/audio.hpp b/engine/inc/audio/audio.hpp
new file mode 100644
index 0000000..68c39b3
--- /dev/null
+++ b/engine/inc/audio/audio.hpp
@@ -0,0 +1,42 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022-2022, Tyra - https://github.com/h4570/tyrav2
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+# Wellinator Carvalho
+*/
+
+#pragma once
+
+#include "./audio_adpcm.hpp"
+#include "./audio_song.hpp"
+
+namespace Tyra {
+
+/** Class responsible for audio. */
+class Audio {
+ public:
+ Audio();
+ ~Audio();
+
+ void init();
+
+ AudioSong song;
+ AudioAdpcm adpcm;
+
+ void work();
+
+ private:
+ ee_thread_t thread;
+ int threadId;
+ static const u16 threadStackSize;
+ u8* threadStack;
+
+ void initAUDSRV();
+ void initThread();
+};
+
+} // namespace Tyra
diff --git a/engine/inc/audio/audio_adpcm.hpp b/engine/inc/audio/audio_adpcm.hpp
new file mode 100644
index 0000000..ea2091a
--- /dev/null
+++ b/engine/inc/audio/audio_adpcm.hpp
@@ -0,0 +1,77 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022-2022, Tyra - https://github.com/h4570/tyrav2
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+# Wellinator Carvalho
+*/
+
+#pragma once
+
+#include "./audio_listener_ref.hpp"
+#include "./adpcm_result.hpp"
+#include
+#include
+
+namespace Tyra {
+
+/**
+ * Class responsible for playing ADPCM samples.
+ * ADPCM sample can't be stopped.
+ */
+class AudioAdpcm {
+ public:
+ AudioAdpcm();
+ ~AudioAdpcm();
+
+ void init();
+
+ /**
+ * Load ADPCM sample.
+ * ADPCM sample is an output from "Convert WAV to ADPCM" task (adpenc tool).
+ * Adpenc expects 22kHz 16bit file.
+ * @param t_path Example: "host:hit.adpcm" or "host:folder/jump.adpcm"
+ */
+ audsrv_adpcm_t* load(const char* t_path);
+ audsrv_adpcm_t* load(const std::string& t_path);
+
+ /**
+ * Frees up all memory taken by samples, and stops all voices from
+ * being played.
+ */
+ void reset();
+
+ /**
+ * Try play ADPCM sample if channel(s) is not occupied.
+ * @param t_adpcm ADPCM data, created by load();
+ * @param t_ch Channel (0-23). Type -1 for use any free channel.
+ */
+ AdpcmResult tryPlay(audsrv_adpcm_t* t_adpcm);
+ AdpcmResult tryPlay(audsrv_adpcm_t* t_adpcm, const s8& t_ch);
+
+ /**
+ * Play ADPCM sample, if channel is occupied, wait for it.
+ * If not used properly, can hugely reduce performance.
+ * @param t_adpcm ADPCM data, created by load();
+ * @param t_ch Channel (0-23). Type -1 for use any free channel.
+ */
+ void playWait(audsrv_adpcm_t* t_adpcm);
+ void playWait(audsrv_adpcm_t* t_adpcm, const s8& t_ch);
+
+ /**
+ * Set ADPCM volume.
+ * @param t_vol Value 0-100
+ * @param t_ch Channel (0-23)
+ */
+ void setVolume(const u8& t_vol, const s8& t_ch) {
+ audsrv_adpcm_set_volume(t_ch, t_vol);
+ }
+
+ private:
+ void initAUDSRV();
+};
+
+} // namespace Tyra
diff --git a/engine/inc/audio/audio_listener.hpp b/engine/inc/audio/audio_listener.hpp
new file mode 100644
index 0000000..e286f51
--- /dev/null
+++ b/engine/inc/audio/audio_listener.hpp
@@ -0,0 +1,25 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022-2022, Tyra - https://github.com/h4570/tyrav2
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+# Wellinator Carvalho
+*/
+
+#pragma once
+
+namespace Tyra {
+
+class AudioListener {
+ public:
+ virtual ~AudioListener(){};
+ virtual void onAudioTick() = 0;
+ virtual void onAudioFinish() = 0;
+
+ private:
+};
+
+} // namespace Tyra
diff --git a/engine/inc/audio/audio_listener_ref.hpp b/engine/inc/audio/audio_listener_ref.hpp
new file mode 100644
index 0000000..0b25f05
--- /dev/null
+++ b/engine/inc/audio/audio_listener_ref.hpp
@@ -0,0 +1,24 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022-2022, Tyra - https://github.com/h4570/tyrav2
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+# Wellinator Carvalho
+*/
+
+#pragma once
+
+#include "./audio_listener.hpp"
+#include
+
+namespace Tyra {
+
+struct AudioListenerRef {
+ AudioListener* listener;
+ u32 id;
+};
+
+} // namespace Tyra
diff --git a/engine/inc/audio/audio_song.hpp b/engine/inc/audio/audio_song.hpp
new file mode 100644
index 0000000..e05e029
--- /dev/null
+++ b/engine/inc/audio/audio_song.hpp
@@ -0,0 +1,90 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022-2022, Tyra - https://github.com/h4570/tyrav2
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+# Wellinator Carvalho
+*/
+
+#pragma once
+
+#include "./audio_listener_ref.hpp"
+#include
+#include
+#include
+#include
+
+namespace Tyra {
+
+/** Class responsible for background audio playing. */
+class AudioSong {
+ public:
+ AudioSong();
+ ~AudioSong();
+
+ void init();
+
+ bool inLoop;
+
+ /**
+ * Load 16bit 22kHz Stereo WAV.
+ * Can be used multiple times for song switching.
+ * @param t_path Example: "host:song.wav" or "host:folder/song.wav"
+ */
+ void load(const char* t_path);
+ void load(const std::string& t_path);
+
+ void play();
+
+ void stop();
+
+ const bool& isPlaying() const;
+
+ const bool& isLoaded() const;
+
+ const u8& getVolume() const;
+
+ /**
+ * Set song volume.
+ * @param t_vol Value 0-100
+ */
+ void setVolume(const u8& t_vol);
+
+ /**
+ * If you want to synchronize some actions with
+ * background song, add audio listener.
+ * @returns Listener id.
+ */
+ u32 addListener(AudioListener* t_listener);
+
+ /** Remove song listener. */
+ void removeListener(const u32& t_id);
+
+ std::size_t getListenersCount() const;
+
+ void work();
+
+ private:
+ bool songLoaded, songPlaying, songFinished;
+ u8 tyraVolume, audsrvVolume;
+ FILE* wav;
+ audsrv_fmt_t format;
+ std::vector songListeners;
+
+ ee_sema_t sema;
+ s32 fillbufferSema;
+ static const u16 chunkSize;
+ char* chunk;
+ s32 chunkReadStatus;
+
+ void initSema();
+ void initAUDSRV();
+ void setSongFormat();
+ void unloadSong();
+ void rewindSongToStart();
+};
+
+} // namespace Tyra
diff --git a/engine/inc/debug/debug.hpp b/engine/inc/debug/debug.hpp
new file mode 100644
index 0000000..d919b0e
--- /dev/null
+++ b/engine/inc/debug/debug.hpp
@@ -0,0 +1,117 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#ifdef NDEBUG
+#define TYRA_LOG(...) ((void)0)
+#define TYRA_WARN(...) ((void)0)
+#define TYRA_ERROR(...) ((void)0)
+#define TYRA_TRAP(...) ((void)0)
+#define TYRA_ASSERT(condition, ...) ((void)0)
+
+#else // IF Debug
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "file/file_utils.hpp"
+#include "info/info.hpp"
+
+#define TYRA_LOG(...) TyraDebug::writeLines("LOG: ", ##__VA_ARGS__, "\n")
+#define TYRA_WARN(...) TyraDebug::writeLines("==WARN: ", ##__VA_ARGS__, "\n")
+#define TYRA_ERROR(...) TyraDebug::writeLines("====ERR: ", ##__VA_ARGS__, "\n")
+#define TYRA_TRAP(...) TyraDebug::trap(__FILE__, __LINE__, ##__VA_ARGS__)
+#define TYRA_BREAKPOINT() TyraDebug::trap(__FILE__, __LINE__, "Breakpoint")
+#define TYRA_ASSERT(condition, ...) \
+ if (!(condition)) TyraDebug::trap(__FILE__, __LINE__, ##__VA_ARGS__)
+
+class TyraDebug {
+ public:
+ template
+ static void writeLines(Arg&& arg, Args&&... args) {
+ std::stringstream ss;
+
+ ss << std::forward(arg);
+ using expander = int[];
+ (void)expander{0, (void(ss << std::forward(args)), 0)...};
+
+ if (Tyra::Info::writeLogsToFile) {
+ auto* logFile = getLogFile();
+ *logFile << ss.str();
+ logFile->flush();
+ } else {
+ printf("%s", ss.str().c_str());
+ }
+ }
+
+ template
+ static void trap(const char* file, int line, Args... args) {
+ std::stringstream ss1;
+ ss1 << "\n";
+ ss1 << "============== TYRA ==============\n";
+ ss1 << "| Assertion failed!\n";
+ ss1 << "|\n";
+
+ if (Tyra::Info::writeLogsToFile) {
+ auto* logFile = getLogFile();
+ *logFile << ss1.str();
+ logFile->flush();
+ } else {
+ printf("%s", ss1.str().c_str());
+ }
+
+ writeAssertLines(args...);
+
+ std::stringstream ss2;
+ ss2 << "|\n";
+ ss2 << "| File : " << file << ":" << line << "\n";
+ ss2 << "====================================\n\n";
+
+ if (Tyra::Info::writeLogsToFile) {
+ auto* logFile = getLogFile();
+ *logFile << ss2.str();
+ logFile->flush();
+ } else {
+ printf("%s", ss2.str().c_str());
+ }
+
+ for (;;) {
+ }
+ }
+
+ private:
+ static std::unique_ptr logFile;
+ static std::ofstream* getLogFile();
+
+ template
+ static void writeAssertLines(Arg&& arg, Args&&... args) {
+ std::stringstream ss;
+
+ ss << "| " << std::forward(arg) << "\n";
+ using expander = int[];
+ (void)expander{
+ 0, (void(ss << "| " << std::forward(args) << "\n"), 0)...};
+
+ if (Tyra::Info::writeLogsToFile) {
+ auto* logfile = getLogFile();
+ *logfile << ss.str();
+ logFile->flush();
+ } else {
+ printf("%s", ss.str().c_str());
+ }
+ }
+};
+
+#endif // NDEBUG
\ No newline at end of file
diff --git a/engine/inc/engine.hpp b/engine/inc/engine.hpp
new file mode 100644
index 0000000..0344edc
--- /dev/null
+++ b/engine/inc/engine.hpp
@@ -0,0 +1,56 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include "./renderer/renderer.hpp"
+#include "./pad/pad.hpp"
+#include "./audio/audio.hpp"
+#include "./irx/irx_loader.hpp"
+#include "./info/info.hpp"
+#include "./info/banner.hpp"
+#include "./game.hpp"
+
+namespace Tyra {
+
+struct EngineOptions {
+ /**
+ * True -> logs will be written to file.
+ * False -> logs will be displayed in console
+ */
+ bool writeLogsToFile = false;
+
+ bool loadUsbDriver = false;
+};
+
+class Engine {
+ public:
+ Engine();
+ Engine(const EngineOptions& options);
+ ~Engine();
+
+ Renderer renderer;
+ Pad pad;
+ Audio audio;
+ Info info;
+
+ void run(Game* t_game);
+
+ private:
+ IrxLoader irx;
+
+ Game* game;
+ Banner banner;
+
+ void realLoop();
+ void initAll(const bool& loadUsbDriver);
+};
+
+} // namespace Tyra
diff --git a/engine/inc/file/file_utils.hpp b/engine/inc/file/file_utils.hpp
new file mode 100644
index 0000000..7d66d75
--- /dev/null
+++ b/engine/inc/file/file_utils.hpp
@@ -0,0 +1,47 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Wellington Carvalho
+*/
+
+#pragma once
+
+#include
+
+namespace Tyra {
+
+class FileUtils {
+ public:
+ FileUtils();
+ ~FileUtils();
+
+ /**
+ * Get current working directory.
+ * Example: "host:" or "mass:"
+ */
+ static std::string getCwd();
+
+ /**
+ * Add current working directory prefix to path
+ * Example: "host:..." or "mass:..."
+ */
+ static std::string fromCwd(const std::string& relativePath);
+ static std::string fromCwd(const char* relativePath);
+
+ const char* getElfName() const { return elfName; };
+ const char* getElfPath() const { return elfPath; };
+
+ private:
+ // Argv name+path & just path
+ char cwd[255];
+ char elfName[255];
+ char elfPath[255 - 14];
+
+ void setPathInfo(const char* path);
+};
+
+} // namespace Tyra
diff --git a/engine/inc/game.hpp b/engine/inc/game.hpp
new file mode 100644
index 0000000..2e64268
--- /dev/null
+++ b/engine/inc/game.hpp
@@ -0,0 +1,21 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+namespace Tyra {
+
+class Game {
+ public:
+ virtual void init() = 0;
+ virtual void loop() = 0;
+};
+
+} // namespace Tyra
diff --git a/engine/inc/info/banner.hpp b/engine/inc/info/banner.hpp
new file mode 100644
index 0000000..2f01de4
--- /dev/null
+++ b/engine/inc/info/banner.hpp
@@ -0,0 +1,25 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Wellington Carvalho
+*/
+
+#pragma once
+
+#include "renderer/renderer.hpp"
+
+namespace Tyra {
+
+class Banner {
+ public:
+ Banner();
+ ~Banner();
+
+ void show(Renderer* renderer);
+};
+
+} // namespace Tyra
diff --git a/engine/inc/info/info.hpp b/engine/inc/info/info.hpp
new file mode 100644
index 0000000..d0c75ef
--- /dev/null
+++ b/engine/inc/info/info.hpp
@@ -0,0 +1,47 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Wellington Carvalho
+*/
+
+#pragma once
+
+#include
+#include
+#include "time/timer.hpp"
+#include "./version.hpp"
+
+namespace Tyra {
+
+class Info {
+ public:
+ Info();
+ ~Info();
+
+ Version version;
+
+ static bool writeLogsToFile;
+
+ /** Called by engine */
+ void update();
+
+ const u32& getFps() const { return fps; };
+
+ /** @return Available RAM in MB */
+ float getAvailableRAM();
+
+ private:
+ float calcFps();
+ void* allocateLargestFreeRAMBlock(size_t* size);
+ size_t getFreeRAMSize();
+
+ u8 fpsDelayer;
+ u32 fps;
+ Timer timer;
+};
+
+} // namespace Tyra
diff --git a/engine/inc/info/version.hpp b/engine/inc/info/version.hpp
new file mode 100644
index 0000000..e23546a
--- /dev/null
+++ b/engine/inc/info/version.hpp
@@ -0,0 +1,33 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# André Guilherme
+*/
+
+#pragma once
+
+#include
+#include
+
+namespace Tyra {
+
+class Version {
+ public:
+ Version() {}
+ ~Version() {}
+
+ static const u8 major = 2;
+ static const u8 minor = 0;
+ static const u8 patch = 0;
+
+ static std::string toString() {
+ return std::to_string(major) + "." + std::to_string(minor) + "." +
+ std::to_string(patch);
+ }
+};
+
+} // namespace Tyra
diff --git a/engine/inc/irx/irx_loader.hpp b/engine/inc/irx/irx_loader.hpp
new file mode 100644
index 0000000..a9aa19f
--- /dev/null
+++ b/engine/inc/irx/irx_loader.hpp
@@ -0,0 +1,38 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Wellington Carvalho
+*/
+
+#pragma once
+
+#include
+
+namespace Tyra {
+
+class IrxLoader {
+ public:
+ IrxLoader();
+ ~IrxLoader();
+
+ void loadAll(const bool& withUsb, const bool& isLoggingToFile);
+
+ private:
+ static bool isLoaded;
+
+ void loadSio2man(const bool& verbose);
+ void loadPadman(const bool& verbose);
+ void loadLibsd(const bool& verbose);
+ void loadUsbModules(const bool& verbose);
+ void loadAudsrv(const bool& verbose);
+
+ int applyRpcPatches();
+ void waitUntilUsbDeviceIsReady();
+ void delay(int count);
+};
+
+} // namespace Tyra
diff --git a/engine/inc/loaders/3d/builder/mesh_builder_data.hpp b/engine/inc/loaders/3d/builder/mesh_builder_data.hpp
new file mode 100644
index 0000000..0affd4a
--- /dev/null
+++ b/engine/inc/loaders/3d/builder/mesh_builder_data.hpp
@@ -0,0 +1,34 @@
+
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include "./mesh_builder_material_data.hpp"
+
+namespace Tyra {
+
+/**
+ * Data needed for constructing mesh class.
+ * All dynamic data ownership is moved to mesh class, so
+ * verts, coords.. are not deallocated!
+ */
+class MeshBuilderData {
+ public:
+ MeshBuilderData();
+ ~MeshBuilderData();
+
+ std::vector materials;
+
+ bool textureCoordsEnabled, normalsEnabled, lightMapEnabled;
+};
+
+} // namespace Tyra
diff --git a/engine/inc/loaders/3d/builder/mesh_builder_material_data.hpp b/engine/inc/loaders/3d/builder/mesh_builder_material_data.hpp
new file mode 100644
index 0000000..ecde268
--- /dev/null
+++ b/engine/inc/loaders/3d/builder/mesh_builder_material_data.hpp
@@ -0,0 +1,31 @@
+
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include
+#include "./mesh_builder_material_frame_data.hpp"
+#include "renderer/models/color.hpp"
+
+namespace Tyra {
+
+class MeshBuilderMaterialData {
+ public:
+ MeshBuilderMaterialData();
+ ~MeshBuilderMaterialData();
+
+ std::vector frames;
+ std::string name;
+ Color ambient;
+};
+
+} // namespace Tyra
diff --git a/engine/inc/loaders/3d/builder/mesh_builder_material_frame_data.hpp b/engine/inc/loaders/3d/builder/mesh_builder_material_frame_data.hpp
new file mode 100644
index 0000000..23303d9
--- /dev/null
+++ b/engine/inc/loaders/3d/builder/mesh_builder_material_frame_data.hpp
@@ -0,0 +1,30 @@
+
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include
+#include "math/vec4.hpp"
+#include "renderer/models/color.hpp"
+
+namespace Tyra {
+
+class MeshBuilderMaterialFrameData {
+ public:
+ MeshBuilderMaterialFrameData();
+ ~MeshBuilderMaterialFrameData();
+
+ u32 count;
+ Vec4 *vertices, *textureCoords, *normals;
+ Color* colors;
+};
+
+} // namespace Tyra
diff --git a/engine/inc/loaders/3d/md2_loader/anorms.hpp b/engine/inc/loaders/3d/md2_loader/anorms.hpp
new file mode 100644
index 0000000..220c4fa
--- /dev/null
+++ b/engine/inc/loaders/3d/md2_loader/anorms.hpp
@@ -0,0 +1,103 @@
+/*
+Copyright (C) 1997-2001 Id Software, Inc.
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#pragma once
+
+namespace Tyra {
+
+const float ANORMS[162][3] = {
+ {-0.525731f, 0.000000f, 0.850651f}, {-0.442863f, 0.238856f, 0.864188f},
+ {-0.295242f, 0.000000f, 0.955423f}, {-0.309017f, 0.500000f, 0.809017f},
+ {-0.162460f, 0.262866f, 0.951056f}, {0.000000f, 0.000000f, 1.000000f},
+ {0.000000f, 0.850651f, 0.525731f}, {-0.147621f, 0.716567f, 0.681718f},
+ {0.147621f, 0.716567f, 0.681718f}, {0.000000f, 0.525731f, 0.850651f},
+ {0.309017f, 0.500000f, 0.809017f}, {0.525731f, 0.000000f, 0.850651f},
+ {0.295242f, 0.000000f, 0.955423f}, {0.442863f, 0.238856f, 0.864188f},
+ {0.162460f, 0.262866f, 0.951056f}, {-0.681718f, 0.147621f, 0.716567f},
+ {-0.809017f, 0.309017f, 0.500000f}, {-0.587785f, 0.425325f, 0.688191f},
+ {-0.850651f, 0.525731f, 0.000000f}, {-0.864188f, 0.442863f, 0.238856f},
+ {-0.716567f, 0.681718f, 0.147621f}, {-0.688191f, 0.587785f, 0.425325f},
+ {-0.500000f, 0.809017f, 0.309017f}, {-0.238856f, 0.864188f, 0.442863f},
+ {-0.425325f, 0.688191f, 0.587785f}, {-0.716567f, 0.681718f, -0.147621f},
+ {-0.500000f, 0.809017f, -0.309017f}, {-0.525731f, 0.850651f, 0.000000f},
+ {0.000000f, 0.850651f, -0.525731f}, {-0.238856f, 0.864188f, -0.442863f},
+ {0.000000f, 0.955423f, -0.295242f}, {-0.262866f, 0.951056f, -0.162460f},
+ {0.000000f, 1.000000f, 0.000000f}, {0.000000f, 0.955423f, 0.295242f},
+ {-0.262866f, 0.951056f, 0.162460f}, {0.238856f, 0.864188f, 0.442863f},
+ {0.262866f, 0.951056f, 0.162460f}, {0.500000f, 0.809017f, 0.309017f},
+ {0.238856f, 0.864188f, -0.442863f}, {0.262866f, 0.951056f, -0.162460f},
+ {0.500000f, 0.809017f, -0.309017f}, {0.850651f, 0.525731f, 0.000000f},
+ {0.716567f, 0.681718f, 0.147621f}, {0.716567f, 0.681718f, -0.147621f},
+ {0.525731f, 0.850651f, 0.000000f}, {0.425325f, 0.688191f, 0.587785f},
+ {0.864188f, 0.442863f, 0.238856f}, {0.688191f, 0.587785f, 0.425325f},
+ {0.809017f, 0.309017f, 0.500000f}, {0.681718f, 0.147621f, 0.716567f},
+ {0.587785f, 0.425325f, 0.688191f}, {0.955423f, 0.295242f, 0.000000f},
+ {1.000000f, 0.000000f, 0.000000f}, {0.951056f, 0.162460f, 0.262866f},
+ {0.850651f, -0.525731f, 0.000000f}, {0.955423f, -0.295242f, 0.000000f},
+ {0.864188f, -0.442863f, 0.238856f}, {0.951056f, -0.162460f, 0.262866f},
+ {0.809017f, -0.309017f, 0.500000f}, {0.681718f, -0.147621f, 0.716567f},
+ {0.850651f, 0.000000f, 0.525731f}, {0.864188f, 0.442863f, -0.238856f},
+ {0.809017f, 0.309017f, -0.500000f}, {0.951056f, 0.162460f, -0.262866f},
+ {0.525731f, 0.000000f, -0.850651f}, {0.681718f, 0.147621f, -0.716567f},
+ {0.681718f, -0.147621f, -0.716567f}, {0.850651f, 0.000000f, -0.525731f},
+ {0.809017f, -0.309017f, -0.500000f}, {0.864188f, -0.442863f, -0.238856f},
+ {0.951056f, -0.162460f, -0.262866f}, {0.147621f, 0.716567f, -0.681718f},
+ {0.309017f, 0.500000f, -0.809017f}, {0.425325f, 0.688191f, -0.587785f},
+ {0.442863f, 0.238856f, -0.864188f}, {0.587785f, 0.425325f, -0.688191f},
+ {0.688191f, 0.587785f, -0.425325f}, {-0.147621f, 0.716567f, -0.681718f},
+ {-0.309017f, 0.500000f, -0.809017f}, {0.000000f, 0.525731f, -0.850651f},
+ {-0.525731f, 0.000000f, -0.850651f}, {-0.442863f, 0.238856f, -0.864188f},
+ {-0.295242f, 0.000000f, -0.955423f}, {-0.162460f, 0.262866f, -0.951056f},
+ {0.000000f, 0.000000f, -1.000000f}, {0.295242f, 0.000000f, -0.955423f},
+ {0.162460f, 0.262866f, -0.951056f}, {-0.442863f, -0.238856f, -0.864188f},
+ {-0.309017f, -0.500000f, -0.809017f}, {-0.162460f, -0.262866f, -0.951056f},
+ {0.000000f, -0.850651f, -0.525731f}, {-0.147621f, -0.716567f, -0.681718f},
+ {0.147621f, -0.716567f, -0.681718f}, {0.000000f, -0.525731f, -0.850651f},
+ {0.309017f, -0.500000f, -0.809017f}, {0.442863f, -0.238856f, -0.864188f},
+ {0.162460f, -0.262866f, -0.951056f}, {0.238856f, -0.864188f, -0.442863f},
+ {0.500000f, -0.809017f, -0.309017f}, {0.425325f, -0.688191f, -0.587785f},
+ {0.716567f, -0.681718f, -0.147621f}, {0.688191f, -0.587785f, -0.425325f},
+ {0.587785f, -0.425325f, -0.688191f}, {0.000000f, -0.955423f, -0.295242f},
+ {0.000000f, -1.000000f, 0.000000f}, {0.262866f, -0.951056f, -0.162460f},
+ {0.000000f, -0.850651f, 0.525731f}, {0.000000f, -0.955423f, 0.295242f},
+ {0.238856f, -0.864188f, 0.442863f}, {0.262866f, -0.951056f, 0.162460f},
+ {0.500000f, -0.809017f, 0.309017f}, {0.716567f, -0.681718f, 0.147621f},
+ {0.525731f, -0.850651f, 0.000000f}, {-0.238856f, -0.864188f, -0.442863f},
+ {-0.500000f, -0.809017f, -0.309017f}, {-0.262866f, -0.951056f, -0.162460f},
+ {-0.850651f, -0.525731f, 0.000000f}, {-0.716567f, -0.681718f, -0.147621f},
+ {-0.716567f, -0.681718f, 0.147621f}, {-0.525731f, -0.850651f, 0.000000f},
+ {-0.500000f, -0.809017f, 0.309017f}, {-0.238856f, -0.864188f, 0.442863f},
+ {-0.262866f, -0.951056f, 0.162460f}, {-0.864188f, -0.442863f, 0.238856f},
+ {-0.809017f, -0.309017f, 0.500000f}, {-0.688191f, -0.587785f, 0.425325f},
+ {-0.681718f, -0.147621f, 0.716567f}, {-0.442863f, -0.238856f, 0.864188f},
+ {-0.587785f, -0.425325f, 0.688191f}, {-0.309017f, -0.500000f, 0.809017f},
+ {-0.147621f, -0.716567f, 0.681718f}, {-0.425325f, -0.688191f, 0.587785f},
+ {-0.162460f, -0.262866f, 0.951056f}, {0.442863f, -0.238856f, 0.864188f},
+ {0.162460f, -0.262866f, 0.951056f}, {0.309017f, -0.500000f, 0.809017f},
+ {0.147621f, -0.716567f, 0.681718f}, {0.000000f, -0.525731f, 0.850651f},
+ {0.425325f, -0.688191f, 0.587785f}, {0.587785f, -0.425325f, 0.688191f},
+ {0.688191f, -0.587785f, 0.425325f}, {-0.955423f, 0.295242f, 0.000000f},
+ {-0.951056f, 0.162460f, 0.262866f}, {-1.000000f, 0.000000f, 0.000000f},
+ {-0.850651f, 0.000000f, 0.525731f}, {-0.955423f, -0.295242f, 0.000000f},
+ {-0.951056f, -0.162460f, 0.262866f}, {-0.864188f, 0.442863f, -0.238856f},
+ {-0.951056f, 0.162460f, -0.262866f}, {-0.809017f, 0.309017f, -0.500000f},
+ {-0.864188f, -0.442863f, -0.238856f}, {-0.951056f, -0.162460f, -0.262866f},
+ {-0.809017f, -0.309017f, -0.500000f}, {-0.681718f, 0.147621f, -0.716567f},
+ {-0.681718f, -0.147621f, -0.716567f}, {-0.850651f, 0.000000f, -0.525731f},
+ {-0.688191f, 0.587785f, -0.425325f}, {-0.587785f, 0.425325f, -0.688191f},
+ {-0.425325f, 0.688191f, -0.587785f}, {-0.425325f, -0.688191f, -0.587785f},
+ {-0.587785f, -0.425325f, -0.688191f}, {-0.688191f, -0.587785f, -0.425325f}};
+
+}
diff --git a/engine/inc/loaders/3d/md2_loader/md2_loader.hpp b/engine/inc/loaders/3d/md2_loader/md2_loader.hpp
new file mode 100644
index 0000000..da6d9a4
--- /dev/null
+++ b/engine/inc/loaders/3d/md2_loader/md2_loader.hpp
@@ -0,0 +1,36 @@
+/*
+# _____ ____ ___
+# | \/ ____| |___|
+# | | | \ | |
+#-----------------------------------------------------------------------
+# Copyright 2022, tyra - https://github.com/h4570/tyra
+# Licensed under Apache License 2.0
+# Sandro Sobczyński
+*/
+
+#pragma once
+
+#include "../../loader.hpp"
+#include "../builder/mesh_builder_data.hpp"
+#include
+#include
+
+namespace Tyra {
+
+struct MD2LoaderOptions {
+ bool flipUVs = false;
+ float scale = 1.0F;
+};
+
+/** Class responsible for loading & parsing Quake's II ".md2" 3D files */
+class MD2Loader : public Loader {
+ public:
+ static std::unique_ptr load(const char* fullpath);
+ static std::unique_ptr load(const char* fullpath,
+ MD2LoaderOptions options);
+ static std::unique_ptr load(const std::string& fullpath);
+ static std::unique_ptr load(const std::string& fullpath,
+ MD2LoaderOptions options);
+};
+
+} // namespace Tyra
diff --git a/engine/inc/loaders/3d/obj_loader/mapbox/earcut.hpp b/engine/inc/loaders/3d/obj_loader/mapbox/earcut.hpp
new file mode 100644
index 0000000..ef95ee2
--- /dev/null
+++ b/engine/inc/loaders/3d/obj_loader/mapbox/earcut.hpp
@@ -0,0 +1,853 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+namespace mapbox {
+
+namespace util {
+
+template
+struct nth {
+ inline static typename std::tuple_element::type get(const T& t) {
+ return std::get(t);
+ };
+};
+
+} // namespace util
+
+namespace detail {
+
+template
+class Earcut {
+ public:
+ std::vector indices;
+ std::size_t vertices = 0;
+
+ template
+ void operator()(const Polygon& points);
+
+ private:
+ struct Node {
+ Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {}
+ Node(const Node&) = delete;
+ Node& operator=(const Node&) = delete;
+ Node(Node&&) = delete;
+ Node& operator=(Node&&) = delete;
+
+ const N i;
+ const double x;
+ const double y;
+
+ // previous and next vertice nodes in a polygon ring
+ Node* prev = nullptr;
+ Node* next = nullptr;
+
+ // z-order curve value
+ int32_t z = 0;
+
+ // previous and next nodes in z-order
+ Node* prevZ = nullptr;
+ Node* nextZ = nullptr;
+
+ // indicates whether this is a steiner point
+ bool steiner = false;
+ };
+
+ template
+ Node* linkedList(const Ring& points, const bool clockwise);
+ Node* filterPoints(Node* start, Node* end = nullptr);
+ void earcutLinked(Node* ear, int pass = 0);
+ bool isEar(Node* ear);
+ bool isEarHashed(Node* ear);
+ Node* cureLocalIntersections(Node* start);
+ void splitEarcut(Node* start);
+ template
+ Node* eliminateHoles(const Polygon& points, Node* outerNode);
+ Node* eliminateHole(Node* hole, Node* outerNode);
+ Node* findHoleBridge(Node* hole, Node* outerNode);
+ bool sectorContainsSector(const Node* m, const Node* p);
+ void indexCurve(Node* start);
+ Node* sortLinked(Node* list);
+ int32_t zOrder(const double x_, const double y_);
+ Node* getLeftmost(Node* start);
+ bool pointInTriangle(double ax, double ay, double bx, double by, double cx,
+ double cy, double px, double py) const;
+ bool isValidDiagonal(Node* a, Node* b);
+ double area(const Node* p, const Node* q, const Node* r) const;
+ bool equals(const Node* p1, const Node* p2);
+ bool intersects(const Node* p1, const Node* q1, const Node* p2,
+ const Node* q2);
+ bool onSegment(const Node* p, const Node* q, const Node* r);
+ int sign(double val);
+ bool intersectsPolygon(const Node* a, const Node* b);
+ bool locallyInside(const Node* a, const Node* b);
+ bool middleInside(const Node* a, const Node* b);
+ Node* splitPolygon(Node* a, Node* b);
+ template
+ Node* insertNode(std::size_t i, const Point& p, Node* last);
+ void removeNode(Node* p);
+
+ bool hashing;
+ double minX, maxX;
+ double minY, maxY;
+ double inv_size = 0;
+
+ template >
+ class ObjectPool {
+ public:
+ ObjectPool() {}
+ ObjectPool(std::size_t blockSize_) { reset(blockSize_); }
+ ~ObjectPool() { clear(); }
+ template
+ T* construct(Args&&... args) {
+ if (currentIndex >= blockSize) {
+ currentBlock = alloc_traits::allocate(alloc, blockSize);
+ allocations.emplace_back(currentBlock);
+ currentIndex = 0;
+ }
+ T* object = ¤tBlock[currentIndex++];
+ alloc_traits::construct(alloc, object, std::forward(args)...);
+ return object;
+ }
+ void reset(std::size_t newBlockSize) {
+ for (auto allocation : allocations) {
+ alloc_traits::deallocate(alloc, allocation, blockSize);
+ }
+ allocations.clear();
+ blockSize = std::max(1, newBlockSize);
+ currentBlock = nullptr;
+ currentIndex = blockSize;
+ }
+ void clear() { reset(blockSize); }
+
+ private:
+ T* currentBlock = nullptr;
+ std::size_t currentIndex = 1;
+ std::size_t blockSize = 1;
+ std::vector allocations;
+ Alloc alloc;
+ typedef typename std::allocator_traits alloc_traits;
+ };
+ ObjectPool nodes;
+};
+
+template
+template
+void Earcut::operator()(const Polygon& points) {
+ // reset
+ indices.clear();
+ vertices = 0;
+
+ if (points.empty()) return;
+
+ double x;
+ double y;
+ int threshold = 80;
+ std::size_t len = 0;
+
+ for (size_t i = 0; threshold >= 0 && i < points.size(); i++) {
+ threshold -= static_cast(points[i].size());
+ len += points[i].size();
+ }
+
+ // estimate size of nodes and indices
+ nodes.reset(len * 3 / 2);
+ indices.reserve(len + points[0].size());
+
+ Node* outerNode = linkedList(points[0], true);
+ if (!outerNode || outerNode->prev == outerNode->next) return;
+
+ if (points.size() > 1) outerNode = eliminateHoles(points, outerNode);
+
+ // if the shape is not too simple, we'll use z-order curve hash later;
+ // calculate polygon bbox
+ hashing = threshold < 0;
+ if (hashing) {
+ Node* p = outerNode->next;
+ minX = maxX = outerNode->x;
+ minY = maxY = outerNode->y;
+ do {
+ x = p->x;
+ y = p->y;
+ minX = std::min(minX, x);
+ minY = std::min(minY, y);
+ maxX = std::max(maxX, x);
+ maxY = std::max(maxY, y);
+ p = p->next;
+ } while (p != outerNode);
+
+ // minX, minY and size are later used to transform coords into integers for
+ // z-order calculation
+ inv_size = std::max(maxX - minX, maxY - minY);
+ inv_size = inv_size != .0 ? (1. / inv_size) : .0;
+ }
+
+ earcutLinked(outerNode);
+
+ nodes.clear();
+}
+
+// create a circular doubly linked list from polygon points in the specified
+// winding order
+template
+template
+typename Earcut::Node* Earcut::linkedList(const Ring& points,
+ const bool clockwise) {
+ using Point = typename Ring::value_type;
+ double sum = 0;
+ const std::size_t len = points.size();
+ std::size_t i, j;
+ Node* last = nullptr;
+
+ // calculate original winding order of a polygon ring
+ for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) {
+ const auto& p1 = points[i];
+ const auto& p2 = points[j];
+ const double p20 = util::nth<0, Point>::get(p2);
+ const double p10 = util::nth<0, Point>::get(p1);
+ const double p11 = util::nth<1, Point>::get(p1);
+ const double p21 = util::nth<1, Point>::get(p2);
+ sum += (p20 - p10) * (p11 + p21);
+ }
+
+ // link points into circular doubly-linked list in the specified winding order
+ if (clockwise == (sum > 0)) {
+ for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last);
+ } else {
+ for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last);
+ }
+
+ if (last && equals(last, last->next)) {
+ removeNode(last);
+ last = last->next;
+ }
+
+ vertices += len;
+
+ return last;
+}
+
+// eliminate colinear or duplicate points
+template
+typename Earcut::Node* Earcut::filterPoints(Node* start, Node* end) {
+ if (!end) end = start;
+
+ Node* p = start;
+ bool again;
+ do {
+ again = false;
+
+ if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) {
+ removeNode(p);
+ p = end = p->prev;
+
+ if (p == p->next) break;
+ again = true;
+
+ } else {
+ p = p->next;
+ }
+ } while (again || p != end);
+
+ return end;
+}
+
+// main ear slicing loop which triangulates a polygon (given as a linked list)
+template
+void Earcut::earcutLinked(Node* ear, int pass) {
+ if (!ear) return;
+
+ // interlink polygon nodes in z-order
+ if (!pass && hashing) indexCurve(ear);
+
+ Node* stop = ear;
+ Node* prev;
+ Node* next;
+
+ int iterations = 0;
+
+ // iterate through ears, slicing them one by one
+ while (ear->prev != ear->next) {
+ iterations++;
+ prev = ear->prev;
+ next = ear->next;
+
+ if (hashing ? isEarHashed(ear) : isEar(ear)) {
+ // cut off the triangle
+ indices.emplace_back(prev->i);
+ indices.emplace_back(ear->i);
+ indices.emplace_back(next->i);
+
+ removeNode(ear);
+
+ // skipping the next vertice leads to less sliver triangles
+ ear = next->next;
+ stop = next->next;
+
+ continue;
+ }
+
+ ear = next;
+
+ // if we looped through the whole remaining polygon and can't find any more
+ // ears
+ if (ear == stop) {
+ // try filtering points and slicing again
+ if (!pass) earcutLinked(filterPoints(ear), 1);
+
+ // if this didn't work, try curing all small self-intersections locally
+ else if (pass == 1) {
+ ear = cureLocalIntersections(filterPoints(ear));
+ earcutLinked(ear, 2);
+
+ // as a last resort, try splitting the remaining polygon into two
+ } else if (pass == 2)
+ splitEarcut(ear);
+
+ break;
+ }
+ }
+}
+
+// check whether a polygon node forms a valid ear with adjacent nodes
+template
+bool Earcut::isEar(Node* ear) {
+ const Node* a = ear->prev;
+ const Node* b = ear;
+ const Node* c = ear->next;
+
+ if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
+
+ // now make sure we don't have other points inside the potential ear
+ Node* p = ear->next->next;
+
+ while (p != ear->prev) {
+ if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
+ area(p->prev, p, p->next) >= 0)
+ return false;
+ p = p->next;
+ }
+
+ return true;
+}
+
+template
+bool Earcut::isEarHashed(Node* ear) {
+ const Node* a = ear->prev;
+ const Node* b = ear;
+ const Node* c = ear->next;
+
+ if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
+
+ // triangle bbox; min & max are calculated like this for speed
+ const double minTX = std::min(a->x, std::min(b->x, c->x));
+ const double minTY = std::min(a->y, std::min(b->y, c->y));
+ const double maxTX = std::max(a->x, std::max(b->x, c->x));
+ const double maxTY = std::max(a->y, std::max(b->y, c->y));
+
+ // z-order range for the current triangle bbox;
+ const int32_t minZ = zOrder(minTX, minTY);
+ const int32_t maxZ = zOrder(maxTX, maxTY);
+
+ // first look for points inside the triangle in increasing z-order
+ Node* p = ear->nextZ;
+
+ while (p && p->z <= maxZ) {
+ if (p != ear->prev && p != ear->next &&
+ pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
+ area(p->prev, p, p->next) >= 0)
+ return false;
+ p = p->nextZ;
+ }
+
+ // then look for points in decreasing z-order
+ p = ear->prevZ;
+
+ while (p && p->z >= minZ) {
+ if (p != ear->prev && p != ear->next &&
+ pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
+ area(p->prev, p, p->next) >= 0)
+ return false;
+ p = p->prevZ;
+ }
+
+ return true;
+}
+
+// go through all polygon nodes and cure small local self-intersections
+template
+typename Earcut::Node* Earcut::cureLocalIntersections(Node* start) {
+ Node* p = start;
+ do {
+ Node* a = p->prev;
+ Node* b = p->next->next;
+
+ // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2])
+ if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) &&
+ locallyInside(b, a)) {
+ indices.emplace_back(a->i);
+ indices.emplace_back(p->i);
+ indices.emplace_back(b->i);
+
+ // remove two nodes involved
+ removeNode(p);
+ removeNode(p->next);
+
+ p = start = b;
+ }
+ p = p->next;
+ } while (p != start);
+
+ return filterPoints(p);
+}
+
+// try splitting polygon into two and triangulate them independently
+template
+void Earcut::splitEarcut(Node* start) {
+ // look for a valid diagonal that divides the polygon into two
+ Node* a = start;
+ do {
+ Node* b = a->next->next;
+ while (b != a->prev) {
+ if (a->i != b->i && isValidDiagonal(a, b)) {
+ // split the polygon in two by the diagonal
+ Node* c = splitPolygon(a, b);
+
+ // filter colinear points around the cuts
+ a = filterPoints(a, a->next);
+ c = filterPoints(c, c->next);
+
+ // run earcut on each half
+ earcutLinked(a);
+ earcutLinked(c);
+ return;
+ }
+ b = b->next;
+ }
+ a = a->next;
+ } while (a != start);
+}
+
+// link every hole into the outer loop, producing a single-ring polygon without
+// holes
+template
+template
+typename Earcut::Node* Earcut