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/.gitignore b/.gitignore new file mode 100644 index 0000000..0152f52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# --- + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.vsm + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Binary files +bin/ +obj/ +res/ + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +*.erl +*.elf 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..891eba0 --- /dev/null +++ b/.vscode/configurations/windows-docker/c_cpp_properties.json @@ -0,0 +1,40 @@ +{ + "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", + "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..e47f737 --- /dev/null +++ b/.vscode/configurations/windows-docker/settings.json @@ -0,0 +1,14 @@ +{ + "buildDirectory": "samples/cube", + "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..0546db7 --- /dev/null +++ b/.vscode/configurations/windows-docker/tasks.json @@ -0,0 +1,94 @@ +{ + "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": "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" + } + ], + "inputs": [ + { + "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..b5b5f82 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "ms-vscode.cpptools-extension-pack", + "ryzngard.vscode-header-source", + "maziac.asm-code-lens" + ] +} \ No newline at end of file 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/Makefile.base b/Makefile.base new file mode 100644 index 0000000..34b7c01 --- /dev/null +++ b/Makefile.base @@ -0,0 +1,116 @@ +#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 irxcopy $(TARGET) + +release: + $(MAKE) CFLAGS="-DNDEBUG $(CFLAGS)" + +#Remake +remake: cleaner all + +#Copy Resources from Resources Directory to Target Directory +resources: directories + @cp $(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 new file mode 100644 index 0000000..acfff8c --- /dev/null +++ b/README.MD @@ -0,0 +1 @@ +# Work in progress \ No newline at end of file diff --git a/ROADMAP.txt b/ROADMAP.txt new file mode 100644 index 0000000..114b9a2 --- /dev/null +++ b/ROADMAP.txt @@ -0,0 +1,57 @@ +------------ Tyra's v2.0 roadmap to publish on GitHub ------------ + +--- H4570 +- [Renderer] Move clipping algorithm into VU1 +- [Renderer] Move animation calcs into VU1 +- [Renderer] Dbufferring during mesh unroll +- [Loaders] Think about ".tyrobj" format - implement it (add multicolor support) +- [Loaders] DFF loader +- [Texture] Test cache hits with large amount of textures (dolphin sample?) +- [Demo] Create cool demo which will show all features of Tyra (I will take this one) + +--- Wellinator +- [General] Physics and stuff from threeJS + +--- Unassigned + +- [General] Control generated id to avoid duplication. Maybe Just increment from 0? Add interface IIdentificable? + +- [DMA] Check if all chain transfers have end tag! + +- [General] Change libs of sample? (remove -lpng, -lz...), probably they are not required + +- [General] Check if there are any TODOs + +- [General] Check all TYRA_ASSERT() if there are no asserts like (TYRA_ASSERT(!audsrv_load_adpcm(result, data, adpcmFileSize))) +because this lines will be removed on make-release. Only debug stuff should be checked in assert + +- [General] All Copyrights to 2020-2022 + +- [General] CI in Github via docker image + +- [General] Add cpp lint checker in GitHub + +- [Tutorials] 1. Hello world! - explain init(), loop() and what should be inside them (every tutorial should be well commented!) + +- [Tutorials] 2. 2D - explain beginFrame(), endFrame() + +- [Tutorials] 3. Minecraft cube - explain this pipeline, and explain that there are many 3D pipelines + +- [Tutorials] 4. Mesh rendering - explain stdPipeline, render .tyrobj file via Mesh class, explain that this is high-level +abstract 3D object class, and what it have (animation etc) + +- [Tutorials] 5. Manual rendering via stdPipeline - via Render3DBag (core.render()), render simple poly, +explain that this is lower-level abstract rendering, it have the all the features as Mesh rendering, +because Mesh rendering uses only core.render() + +- [Tutorials] 6. Animation in Mesh rendering rendering, explain the API + +- [Tutorials] 7. Directional lights in Mesh rendering + +- [Tutorials] 8. Color-per-vertex light map in Mesh rendering (color array) + +- [Tutorials] 9. Render skybox, and show that stdPipOptions.noClipChecks must be set to false, to turn on clip algorithms (prevent glitches) + +------------ Github issues for Tyra v2 ------------ +- [3D] Add drawLine(x, y , color, size) +- [3D] Add drawBBox(x, y , color, size) \ No newline at end of file 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/README.MD b/docs/install/README.MD new file mode 100644 index 0000000..3f559ed --- /dev/null +++ b/docs/install/README.MD @@ -0,0 +1,25 @@ +# Installing Tyra + +[![](https://img.shields.io/badge/Install%20time-5%20minutes-brightgreen)](#) + +## 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, `wellinator` sample and run `PCSX2`. If you want to compile & run other sample, please change `buildDirectory` in `settings.json` \ No newline at end of file diff --git a/docs/install/pcsx2.md b/docs/install/pcsx2.md new file mode 100644 index 0000000..1c1a1d2 --- /dev/null +++ b/docs/install/pcsx2.md @@ -0,0 +1,19 @@ +## 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 +``` \ 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/engine/inc/audio/audio.hpp b/engine/inc/audio/audio.hpp new file mode 100644 index 0000000..d6b97ed --- /dev/null +++ b/engine/inc/audio/audio.hpp @@ -0,0 +1,161 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020-2022, Tyra - https://github.com/h4570/tyrav2 +# Licenced under Apache License 2.0 +# Sandro Sobczyński +# Wellinator Carvalho +*/ + +#pragma once + +#include "./audio_listener.hpp" +#include +#include + +namespace Tyra { + +struct AudioListenerRef { + AudioListener* listener; + u32 id; +}; + +/** + * Class responsible for audio playing. + * There are two main features: + * - WAV song (background music) + * - ADPCM samples + */ +class Audio { + public: + /** Constructor is called by the engine. */ + Audio(); + ~Audio(); + + void init(); + + /** + * Load 24bit 22050Hz Stereo WAV. + * Can be used multiple times for song switching. + * @param t_path Example: "host:song.wav" or "host:folder/song.wav" + */ + void loadSong(const char* t_path); + + /** + * Load 24bit 22050Hz Stereo WAV. + * Can be used multiple times for song switching. + * @param t_path Example: "host:song.wav" or "host:folder/song.wav" + */ + inline void loadSong(const std::string& t_path) { loadSong(t_path.c_str()); } + + void playSong(); + + void stopSong(); + + const u8& isSongPlaying() const { return songPlaying; } + + const u8& isSongLoaded() const { return songLoaded; } + + /** + * Set song loop/noloop. + * @returns Looping state is 0 - noloop / 1 - loop + */ + const u8& isSongInLoop() const { return songInLoop; } + + /** + * Set song loop/noloop. + * @param t_set false - noloop, true - loop + */ + void setSongLoop(const u8& t_set) { songInLoop = t_set; } + + const u8& getVolume() const { return tyraVolume; } + + /** + * Set song volume. + * @param t_vol Value 0-100 + */ + void setSongVolume(const u8& t_vol); + + /** + * If you want to synchronize some actions with + * background song, add audio listener. + * @returns Listener id. + */ + u32 addSongListener(AudioListener* t_listener); + + /** Remove song listener. */ + void removeSongListener(const u32& t_id); + + /** + * Load ADPCM sample. ADPCM sample is an output from + * "adpenc" tool, shipped with PS2SDK. + * @param t_path Example: "host:hit.adpcm" or "host:folder/jump.adpcm" + */ + audsrv_adpcm_t* loadADPCM(const char* t_path); + + /** + * Load ADPCM sample. ADPCM sample is an output from + * "adpenc" tool, shipped with PS2SDK. + * @param t_path Example: "host:hit.adpcm" or "host:folder/jump.adpcm" + */ + inline audsrv_adpcm_t* loadADPCM(const std::string& t_path) { + return loadADPCM(t_path.c_str()); + } + + /** + * Play ADPCM sample. + * ADPCM sample can't be stopped. + * @param t_adpcm ADPCM data, created by loadADPCM(); + */ + void playADPCM(audsrv_adpcm_t* t_adpcm); + + /** + * Play ADPCM sample. + * ADPCM sample can't be stopped. + * @param t_adpcm ADPCM data, created by loadADPCM(); + * @param t_ch Channel (0-23). Type -1 for use any free channel. + */ + void playADPCM(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 setADPCMVolume(const u8& t_vol, const s8& t_ch) { + audsrv_adpcm_set_volume(t_ch, t_vol); + } + + void work(); + + private: + u8 songLoaded, tyraVolume, audsrvVolume, songPlaying, songInLoop, + songFinished; + FILE* wav; + audsrv_fmt_t format; + std::vector songListeners; + + ee_thread_t thread; + int threadId; + static const u16 threadStackSize; + u8* threadStack; + + ee_sema_t sema; + s32 fillbufferSema; + static const u16 chunkSize; + char* chunk; + s32 chunkReadStatus; + + void setSongFormat(); + void unloadSong(); + void rewindSongToStart(); + u32 getSongListenersCount() const { return songListeners.size(); } + + void initSema(); + void initAUDSRV(); + void initThread(); +}; + +} // namespace Tyra diff --git a/engine/inc/audio/audio_listener.hpp b/engine/inc/audio/audio_listener.hpp new file mode 100644 index 0000000..8a28111 --- /dev/null +++ b/engine/inc/audio/audio_listener.hpp @@ -0,0 +1,25 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020-2022, Tyra - https://github.com/h4570/tyrav2 +# Licenced 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/debug/debug.hpp b/engine/inc/debug/debug.hpp new file mode 100644 index 0000000..07722c9 --- /dev/null +++ b/engine/inc/debug/debug.hpp @@ -0,0 +1,76 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced 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 + +#define TYRA_LOG(...) Debug::writeLines("LOG: ", ##__VA_ARGS__, "\n") +#define TYRA_WARN(...) Debug::writeLines("==WARN: ", ##__VA_ARGS__, "\n") +#define TYRA_ERROR(...) Debug::writeLines("====ERR: ", ##__VA_ARGS__, "\n") +#define TYRA_TRAP(...) Debug::trap(__FILE__, __LINE__, ##__VA_ARGS__) +#define TYRA_BREAKPOINT() Debug::trap(__FILE__, __LINE__, "Breakpoint") +#define TYRA_ASSERT(condition, ...) \ + if (!(condition)) Debug::trap(__FILE__, __LINE__, ##__VA_ARGS__) + +class Debug { + 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)...}; + + printf("%s", ss.str().c_str()); + } + + template + static void trap(const char* file, int line, Args... args) { + printf("\n"); + printf("============== TYRA ==============\n"); + printf("| Assertion failed!\n"); + printf("|\n"); + writeAssertLines(args...); + printf("|\n"); + printf("| File : %s:%d\n", file, line); + printf("====================================\n\n"); + for (;;) { + } + } + + private: + 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)...}; + + 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..5903909 --- /dev/null +++ b/engine/inc/engine.hpp @@ -0,0 +1,41 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced 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 "./game.hpp" + +namespace Tyra { + +class Engine { + public: + Engine(); + ~Engine(); + + Renderer renderer; + Pad pad; + Audio audio; + IrxLoader irx; + Info info; + + void run(Game* t_game); + + private: + Game* game; + + void realLoop(); +}; + +} // namespace Tyra diff --git a/engine/inc/file/file_utils.hpp b/engine/inc/file/file_utils.hpp new file mode 100644 index 0000000..eb8b617 --- /dev/null +++ b/engine/inc/file/file_utils.hpp @@ -0,0 +1,35 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020 - 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Wellington Carvalho +*/ + +#pragma once + +namespace Tyra { + +class FileUtils { + public: + FileUtils(); + ~FileUtils(); + + static std::string getCwd(); + 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[NAME_MAX]; + char elfName[NAME_MAX]; + char elfPath[NAME_MAX - 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..c3ffa21 --- /dev/null +++ b/engine/inc/game.hpp @@ -0,0 +1,21 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced 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/info.hpp b/engine/inc/info/info.hpp new file mode 100644 index 0000000..bb8cb1e --- /dev/null +++ b/engine/inc/info/info.hpp @@ -0,0 +1,36 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020 - 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Wellington Carvalho +*/ + +#pragma once + +#include +#include "time/timer.hpp" + +namespace Tyra { + +class Info { + public: + Info(); + ~Info(); + + /** Called by engine */ + void update(); + + const u32& getFps() const { return fps; }; + + private: + float calcFps(); + + u8 fpsDelayer; + u32 fps; + Timer timer; +}; + +} // namespace Tyra diff --git a/engine/inc/irx/irx_loader.hpp b/engine/inc/irx/irx_loader.hpp new file mode 100644 index 0000000..3e4b9e1 --- /dev/null +++ b/engine/inc/irx/irx_loader.hpp @@ -0,0 +1,36 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020 - 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Wellington Carvalho +*/ + +#pragma once + +#include + +namespace Tyra { + +class IrxLoader { + public: + IrxLoader(); + ~IrxLoader(); + + /** Load's audio and pad driver */ + void loadDefaultDrivers(); + + void loadUSBDriver(); + + private: + int applyRpcPatches(); + int loadAudio(); + int loadPad(); + int loadUsb(); + 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..214bd6d --- /dev/null +++ b/engine/inc/loaders/3d/builder/mesh_builder_data.hpp @@ -0,0 +1,44 @@ + +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "./mesh_builder_material_data.hpp" +#include "./mesh_builder_frame_data.hpp" + +namespace Tyra { + +class MeshBuilderData { + public: + MeshBuilderData(); + + /** + * Frames array and materials array will be deallocated, but + * data (vert,st, ...) inside it not. + * Data ownership is transferred to + * the Mesh class. + */ + ~MeshBuilderData(); + + MeshBuilderFrameData** frames; + u32 framesCount; + + MeshBuilderMaterialData** materials; + u32 materialsCount; + + bool textureCoordsEnabled, normalsEnabled, manyColorsEnabled; + + void allocateFrames(const u32& count); + void allocateMaterials(const u32& count); + void allocate(const u32& framesCount, const u32& materialsCount); +}; + +} // namespace Tyra diff --git a/engine/inc/loaders/3d/builder/mesh_builder_frame_data.hpp b/engine/inc/loaders/3d/builder/mesh_builder_frame_data.hpp new file mode 100644 index 0000000..7f186a1 --- /dev/null +++ b/engine/inc/loaders/3d/builder/mesh_builder_frame_data.hpp @@ -0,0 +1,39 @@ + +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "math/vec4.hpp" +#include "renderer/models/color.hpp" + +namespace Tyra { + +/** + * Data is not deallocated here. + * Ownership is moved to Mesh class. + */ +class MeshBuilderFrameData { + public: + MeshBuilderFrameData(); + ~MeshBuilderFrameData(); + + Vec4 *vertices, *normals, *textureCoords; + Color* colors; + u32 verticesCount, textureCoordsCount, normalsCount, colorsCount; + + void allocateTextureCoords(const u32& count); + void allocateVertices(const u32& count); + void allocateNormals(const u32& count); + void allocateColors(const u32& count); +}; + +} // 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..51f4dd4 --- /dev/null +++ b/engine/inc/loaders/3d/builder/mesh_builder_material_data.hpp @@ -0,0 +1,35 @@ + +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include + +namespace Tyra { + +/** + * Data is not deallocated here. + * Ownership is moved to Mesh class. + */ +class MeshBuilderMaterialData { + public: + MeshBuilderMaterialData(); + ~MeshBuilderMaterialData(); + + std::string name; + u32 *vertexFaces, *textureCoordFaces, *normalFaces, *colorFaces; + u32 count; + + void allocateFaces(const u32& count); +}; + +} // namespace Tyra diff --git a/engine/inc/loaders/3d/md2/anorms.hpp b/engine/inc/loaders/3d/md2/anorms.hpp new file mode 100644 index 0000000..220c4fa --- /dev/null +++ b/engine/inc/loaders/3d/md2/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/md2_loader.hpp b/engine/inc/loaders/3d/md2/md2_loader.hpp new file mode 100644 index 0000000..cf3052e --- /dev/null +++ b/engine/inc/loaders/3d/md2/md2_loader.hpp @@ -0,0 +1,32 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include + +namespace Tyra { + +/** Class responsible for loading&parsing Quake's II ".md2" 3D files */ +class MD2Loader : public Loader { + public: + MD2Loader(); + ~MD2Loader(); + + MeshBuilderData* load(const char* fullpath, const float& t_scale, + const u8& t_invertT); + + inline MeshBuilderData* load(const std::string& fullpath, + const float& t_scale, const u8& t_invertT) { + return load(fullpath.c_str(), t_scale, t_invertT); + } +}; + +} // namespace Tyra diff --git a/engine/inc/loaders/loader.hpp b/engine/inc/loaders/loader.hpp new file mode 100644 index 0000000..eb093c9 --- /dev/null +++ b/engine/inc/loaders/loader.hpp @@ -0,0 +1,23 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include + +namespace Tyra { + +class Loader { + protected: + std::string getFilenameFromPath(const std::string& path); + std::string getFilenameWithoutExtension(const std::string& filename); +}; + +} // namespace Tyra diff --git a/engine/inc/loaders/texture/base/texture_loader.hpp b/engine/inc/loaders/texture/base/texture_loader.hpp new file mode 100644 index 0000000..019b880 --- /dev/null +++ b/engine/inc/loaders/texture/base/texture_loader.hpp @@ -0,0 +1,35 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "loaders/loader.hpp" +#include "loaders/texture/builder/texture_builder_data.hpp" +#include "renderer/core/texture/models/texture_bpp.hpp" +#include + +namespace Tyra { +class TextureLoader : public Loader { + public: + /** + * @brief Loads texture data from file. + * Sets size, name and data. + * @param fullpath Full path to texture file. Example: "host:texture.png" + */ + virtual TextureBuilderData* load(const char* fullpath) = 0; + + inline TextureBuilderData* load(const std::string& fullpath) { + return load(fullpath.c_str()); + } + + u32 getTextureSize(const u32& width, const u32& height, + const TextureBpp& bpp); +}; +} // namespace Tyra diff --git a/engine/inc/loaders/texture/base/texture_loader_selector.hpp b/engine/inc/loaders/texture/base/texture_loader_selector.hpp new file mode 100644 index 0000000..f695ff7 --- /dev/null +++ b/engine/inc/loaders/texture/base/texture_loader_selector.hpp @@ -0,0 +1,34 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "loaders/texture/base/texture_loader.hpp" +#include "loaders/texture/png_loader.hpp" +#include + +namespace Tyra { +class TextureLoaderSelector { + public: + TextureLoaderSelector(); + ~TextureLoaderSelector(); + + TextureLoader& getLoaderByFileName(const char* fullpath); + + inline TextureLoader& getLoaderByFileName(const std::string& fullpath) { + return getLoaderByFileName(fullpath.c_str()); + } + + TextureLoader& getLoaderByExtension(const std::string& extension); + + private: + PngLoader pngLoader; +}; +} // namespace Tyra diff --git a/engine/inc/loaders/texture/builder/texture_builder_data.hpp b/engine/inc/loaders/texture/builder/texture_builder_data.hpp new file mode 100644 index 0000000..4935c27 --- /dev/null +++ b/engine/inc/loaders/texture/builder/texture_builder_data.hpp @@ -0,0 +1,36 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "renderer/core/texture/models/texture_bpp.hpp" + +namespace Tyra { + +class TextureBuilderData { + public: + TextureBuilderData(); + ~TextureBuilderData(); + + std::string name; + + int width, height; + unsigned char* data; + TextureBpp bpp; + unsigned char gsComponents; + + int clutWidth, clutHeight; + unsigned char* clut; + TextureBpp clutBpp; + unsigned char clutGsComponents; +}; +} // namespace Tyra diff --git a/engine/inc/loaders/texture/png_loader.hpp b/engine/inc/loaders/texture/png_loader.hpp new file mode 100644 index 0000000..f4dfa02 --- /dev/null +++ b/engine/inc/loaders/texture/png_loader.hpp @@ -0,0 +1,56 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "loaders/texture/base/texture_loader.hpp" +#include "renderer/core/texture/models/texture.hpp" + +#ifdef INTELLISENSE +typedef void* png_structp; +typedef void* png_bytep; +typedef void* png_infop; +typedef void* png_colorp; +#else +#include +#endif + +namespace Tyra { + +class PngLoader : public TextureLoader { + public: + PngLoader(); + ~PngLoader(); + + TextureBuilderData* load(const char* fullpath); + + private: + void handle32bpp(TextureBuilderData* result, png_structp pngPtr, + png_infop infoPtr, png_bytep* rowPointers); + + void handle24bpp(TextureBuilderData* result, png_structp pngPtr, + png_infop infoPtr, png_bytep* rowPointers); + + void handlePalletized(TextureBuilderData* result, png_structp pngPtr, + png_infop infoPtr, png_bytep* rowPointers, + const int& bitDepth); + + void handle8bppPalletized(TextureBuilderData* result, png_structp pngPtr, + png_infop infoPtr, png_bytep* rowPointers, + png_colorp palette, png_bytep trans, + const int& numPallete, const int& numTrans); + + void handle4bppPalletized(TextureBuilderData* result, png_structp pngPtr, + png_infop infoPtr, png_bytep* rowPointers, + png_colorp palette, png_bytep trans, + const int& numPallete, const int& numTrans); +}; + +} // namespace Tyra diff --git a/engine/inc/math/m4x4.hpp b/engine/inc/math/m4x4.hpp new file mode 100644 index 0000000..efb9a74 --- /dev/null +++ b/engine/inc/math/m4x4.hpp @@ -0,0 +1,194 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "math/vec4.hpp" +#include "math/math.hpp" + +namespace Tyra { + +class M4x4 { + public: + MATRIX data alignas(sizeof(float) * 16); + + /** + * @param identity true = identity matrix, false = random values + */ + explicit M4x4(const bool& identity = true); + M4x4(const M4x4& v) { copy(this, v); } + explicit M4x4(const float* v) { copy(this, v); } + inline M4x4(const float& m11, const float& m12, const float& m13, + const float& m14, const float& m21, const float& m22, + const float& m23, const float& m24, const float& m31, + const float& m32, const float& m33, const float& m34, + const float& m41, const float& m42, const float& m43, + const float& m44) { + set(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, + m43, m44); + } + + static void copy(M4x4* out, const float* in); + static void copy(M4x4* out, const M4x4& in) { copy(out, in.data); } + + void operator=(const M4x4& v); + Vec4 operator*(const Vec4& v) const; + + M4x4 operator*(const M4x4& v) const { + M4x4 result; + cross(result.data, this->data, v.data); + return result; + } + + void operator*=(const M4x4& v) { cross(this->data, this->data, v.data); } + + float& operator[](const u8& index) { return data[index]; } + + /** + * 1 0 0 0 + * 0 1 0 0 + * 0 0 1 0 + * 0 0 0 1 + */ + void identity(); + + /** + * 1 0 0 0 + * 0 1 0 0 + * 0 0 1 0 + * 0 0 0 1 + */ + void unit() { identity(); } + + void set(const float& m11, const float& m12, const float& m13, + const float& m14, const float& m21, const float& m22, + const float& m23, const float& m24, const float& m31, + const float& m32, const float& m33, const float& m34, + const float& m41, const float& m42, const float& m43, + const float& m44); + + void translate(const Vec4& v) { + M4x4 temp = M4x4(true); + temp.translationX(v.x); + temp.translationY(v.y); + temp.translationZ(v.z); + cross(this->data, temp.data, this->data); + } + + void translateX(const float& v) { + M4x4 temp = M4x4(true); + temp.translationX(v); + cross(this->data, temp.data, this->data); + } + + void translateY(const float& v) { + M4x4 temp = M4x4(true); + temp.translationY(v); + cross(this->data, temp.data, this->data); + } + + void translateZ(const float& v) { + M4x4 temp = M4x4(true); + temp.translationZ(v); + cross(this->data, temp.data, this->data); + } + + /** Rotate M4x4. */ + void rotate(const Vec4& v) { + M4x4 temp = M4x4(true); + + temp.rotationZ(v.z); + cross(this->data, temp.data, this->data); + + temp.identity(); + temp.rotationY(v.y); + cross(this->data, temp.data, this->data); + + temp.identity(); + temp.rotationX(v.x); + cross(this->data, temp.data, this->data); + } + + /** Rotate M4x4 by X. */ + void rotateX(const float& radians) { + M4x4 temp = M4x4(true); + temp.rotationX(radians); + cross(this->data, temp.data, this->data); + } + + /** Rotate M4x4 by Y. */ + void rotateY(const float& radians) { + M4x4 temp = M4x4(true); + temp.rotationY(radians); + cross(this->data, temp.data, this->data); + } + + /** Rotate M4x4 by Z. */ + void rotateZ(const float& radians) { + M4x4 temp = M4x4(true); + temp.rotationZ(radians); + cross(this->data, temp.data, this->data); + } + + /** Rotate M4x4 by angle. */ + void rotateByAngle(const float& angle, const Vec4& axis) { + M4x4 temp; + temp.rotationByAngle(angle, axis); + cross(this->data, temp.data, this->data); + } + + void scale(const float& v) { scale(Vec4(v, v, v, 1.0F)); } + void scaleX(const float& v) { scale(Vec4(v, 1.0F, 1.0F, 1.0F)); } + void scaleY(const float& v) { scale(Vec4(1.0F, v, 1.0F, 1.0F)); } + void scaleZ(const float& v) { scale(Vec4(1.0F, 1.0F, v, 1.0F)); } + + void scale(const Vec4& v) { + M4x4 temp = M4x4(true); + temp.setScale(v); + cross(this->data, temp.data, this->data); + } + + /** Create perspective projection M4x4 (gluPerspective) */ + static M4x4 perspective(const float& fov, const float& width, + const float& height, const float& projectionScale, + const float& aspectRatio, const float& near, + const float& far); + + /** + * Create a view M4x4 that transforms coordinates in + * such a way that the user looks at a target vector + * direction from a position vector (glLookAt) + */ + static M4x4 lookAt(const Vec4& position, const Vec4& target); + static void lookAt(M4x4* res, const Vec4& position, const Vec4& target); + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; + + private: + static float upVec[4] alignas(sizeof(float) * 4); + static float viewVec[4] alignas(sizeof(float) * 4); + + static void cross(float res[16], const float a[16], const float b[16]); + void rotationX(const float& v); + void rotationY(const float& v); + void rotationZ(const float& v); + void rotationByAngle(const float& angle, const Vec4& axis); + void translationX(const float& v); + void translationY(const float& v); + void translationZ(const float& v); + void setScale(const Vec4& val); + static M4x4 setCamera(const float pos[4], const float vz[4], + const float vy[4]); +}; + +} // namespace Tyra diff --git a/engine/inc/math/math.hpp b/engine/inc/math/math.hpp new file mode 100644 index 0000000..2b82cb5 --- /dev/null +++ b/engine/inc/math/math.hpp @@ -0,0 +1,38 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +extern "C" { +#include +} + +#include + +namespace Tyra { + +class Math { + public: + constexpr static float HALF_ANG2RAD = 3.14159265358979323846F / 360.0F; + constexpr static float ANG2RAD = 3.14159265358979323846F / 180.0F; + constexpr static float PI = 3.1415926535897932384626433832795F; + constexpr static float HALF_PI = 1.5707963267948966192313216916398F; + static float cos(float x); + static float asin(float x); + static float mod(float x, float y); + static inline float sin(float x) { return cos(x - HALF_PI); } + static inline float tan(float x) { return sin(x) / cos(x); } + static float invSqrt(float x); + + private: + Math(); +}; + +} // namespace Tyra diff --git a/engine/inc/math/plane.hpp b/engine/inc/math/plane.hpp new file mode 100644 index 0000000..a9c802d --- /dev/null +++ b/engine/inc/math/plane.hpp @@ -0,0 +1,36 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + + +namespace Tyra { + +class Plane { + public: + Vec4 normal; + float distance; + + Plane(); + Plane(const Vec4& a, const Vec4& b, const Vec4& c); + ~Plane(); + + void update(const Vec4& a, const Vec4& b, const Vec4& c); + inline float distanceTo(const Vec4& t_vec) const { + return this->distance + this->normal.innerProduct(t_vec); + } + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; +}; + +} // namespace Tyra diff --git a/engine/inc/math/vec2.hpp b/engine/inc/math/vec2.hpp new file mode 100644 index 0000000..9f479ae --- /dev/null +++ b/engine/inc/math/vec2.hpp @@ -0,0 +1,49 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +extern "C" { +#include +#include +} + +#include +#include +#include "math/math.hpp" + +namespace Tyra { + +class Vec2 { + public: + union { + struct { + float x; + float y; + }; + float xy[2] alignas(sizeof(float) * 2); + }; + + Vec2(const float& t_x, const float& t_y); + Vec2(const Vec2& v); + Vec2(); + ~Vec2(); + + void set(const float& t_x, const float& t_y); + void set(const Vec2& v); + void rotate(const float& t_angle, const float& t_x, const float& t_y); + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; +}; + +} // namespace Tyra diff --git a/engine/inc/math/vec4.hpp b/engine/inc/math/vec4.hpp new file mode 100644 index 0000000..35c1df6 --- /dev/null +++ b/engine/inc/math/vec4.hpp @@ -0,0 +1,151 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +extern "C" { +#include +#include +} + +#include +#include +#include +#include +#include +#include "math/math.hpp" + +namespace Tyra { + +class Vec4 { + public: + union { + struct { + float x; + float y; + float z; + float w; + }; + VECTOR xyzw alignas(sizeof(float) * 4); + }; + + static void copy(Vec4* out, const float* in); + static inline void copy(Vec4* out, const Vec4& in) { copy(out, in.xyzw); } + + /** Initialize Vec4 without setting default values */ + Vec4() {} + + /** Initialize Vec4 with provided values */ + Vec4(const float& x, const float& y = 0.0F, const float& z = 0.0F, + const float& w = 1.0F) + : x(x), y(y), z(z), w(w) {} + + /** Initialize Vec4 with other Vec4 values */ + Vec4(const Vec4& v) { copy(this, v); } + + /** Initialize Vec4 with float[4] values */ + explicit Vec4(const float* v) { copy(this, v); } + + Vec4 operator+(const Vec4& v) const; + Vec4 operator-(const Vec4& v) const; + /** Cross product */ + Vec4 operator*(const Vec4& v) const; + Vec4 operator*(const float& v) const; + Vec4 operator/(const float& v) const; + Vec4 operator-(void) const; + void operator=(const Vec4& v); + void operator+=(const Vec4& v); + void operator*=(const float& v); + void operator*=(const Vec4& v); + void operator/=(const float& v); + + inline void set(const Vec4& v) { copy(this, v.xyzw); } + inline void set(const float* v) { copy(this, v); } + void set(const float& x, const float& y = 0.0F, const float& z = 0.0F, + const float& w = 1.0F); + + Vec4 cross(const Vec4& v) const; + + void rotateZ(const int& angle); + + int getRelativeCosBetween(const Vec4& v) const; + int getRelativeAngleBetween(const Vec4& v) const; + + float innerProduct(const Vec4& v) const; + inline float dot3(const Vec4& v) const { return innerProduct(v); } + + /** Get vector length */ + float length() const; + + /** Normalize vector */ + void normalize(); + + /** Returns distance between two vectors */ + float distanceTo(const Vec4& v) const; + + /** + * Checks intersection with given box + * @param min Opposite min vertex (ex. near, left, down vertex of bounding + * box) + * @param max Opposite max vertex (ex. far, right, up vertex of bounding + * box) + */ + u8 collidesBox(const Vec4& min, const Vec4& max) const { + return ((this->x <= max.x && this->x >= min.x) && + (this->y < max.y && this->y >= min.y) && + (this->z <= max.z && this->z >= min.z)) + ? 1 + : 0; + } + + /** + * Checks if this vector is on given box (this->y >= box.y) + * @param min Opposite min vertex (ex. near, left, down vertex of bounding + * box) + * @param max Opposite max vertex (ex. far, right, up vertex of bounding + * box) + */ + u8 isOnBox(const Vec4& min, const Vec4& max) const { + return ((this->x <= max.x && this->x >= min.x) && (this->y >= max.y) && + (this->z <= max.z && this->z >= min.z)) + ? 1 + : 0; + } + + /** Check if given triangle should be backface culled */ + static u8 shouldBeBackfaceCulled(const Vec4* cameraPos, const Vec4* v0, + const Vec4* v1, const Vec4* v2); + + /** + * Update this vector by linear interpolation between two vertices + * @param v1 Before vertex + * @param v2 After vertex + * @param interp State of interpolation + */ + void lerp(const Vec4& v1, const Vec4& v2, const float& interp); + + /** + * Get vector by linear interpolation between two vertices + * @param v1 Before vertex + * @param v2 After vertex + * @param interp State of interpolation + */ + static Vec4 getByLerp(const Vec4& v1, const Vec4& v2, const float& interp); + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; + + static void setLerp(Vec4* output, const Vec4& v1, const Vec4& v2, + const float& interp); +}; + +} // namespace Tyra diff --git a/engine/inc/packet2/packet2_tyra_utils.hpp b/engine/inc/packet2/packet2_tyra_utils.hpp new file mode 100644 index 0000000..50b319d --- /dev/null +++ b/engine/inc/packet2/packet2_tyra_utils.hpp @@ -0,0 +1,37 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "math/m4x4.hpp" +#include "renderer/models/color.hpp" + +namespace Tyra { + +class Packet2TyraUtils { + public: + inline static void addM4x4(packet2_t* packet2, const M4x4& val) { + *(reinterpret_cast(packet2->next)) = val; + packet2_advance_next(packet2, sizeof(MATRIX)); + } + + inline static void addVec4(packet2_t* packet2, const Vec4& val) { + *(reinterpret_cast(packet2->next)) = val; + packet2_advance_next(packet2, sizeof(VECTOR)); + } + + inline static void addColor(packet2_t* packet2, const Color& val) { + *(reinterpret_cast(packet2->next)) = val; + packet2_advance_next(packet2, sizeof(VECTOR)); + } +}; + +} // namespace Tyra diff --git a/engine/inc/pad/pad.hpp b/engine/inc/pad/pad.hpp new file mode 100644 index 0000000..1bb8547 --- /dev/null +++ b/engine/inc/pad/pad.hpp @@ -0,0 +1,58 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020-2022, tyra - https://github.com/h4570/tyrav2 +# Licenced under Apache License 2.0 +# Sandro Sobczyński +# Sandro Wellinator +*/ + +#include +#include + +#pragma once + +namespace Tyra { + +struct PadButtons { + u8 Cross, Square, Triangle, Circle, DpadUp, DpadDown, DpadLeft, DpadRight, L1, + L2, L3, R1, R2, R3, Start, Select; +}; + +struct PadJoy { + u8 h, v, isCentered, isMoved; +}; + +/** Class responsible for player pad */ +class Pad { + public: + Pad(); + ~Pad(); + + void init(); + void update(); + + inline const PadButtons& getClicked() { return clicked; } + inline const PadButtons& getPressed() { return pressed; } + inline const PadJoy& getLeftJoyPad() { return leftJoyPad; } + inline const PadJoy& getRightJoyPad() { return rightJoyPad; } + + private: + char padBuf[256] alignas(sizeof(char) * 256); + char actAlign[6]; + int actuators, ret, port, slot; + padButtonStatus buttons; + u32 padData, oldPad, newPad; + PadButtons pressed, clicked; + PadJoy leftJoyPad, rightJoyPad; + + void reset(); + void handleClickedButtons(); + void handlePressedButtons(); + int waitPadReady(); + int initPad(); +}; + +} // namespace Tyra \ No newline at end of file diff --git a/engine/inc/renderer/2d/renderer_2d.hpp b/engine/inc/renderer/2d/renderer_2d.hpp new file mode 100644 index 0000000..0356b47 --- /dev/null +++ b/engine/inc/renderer/2d/renderer_2d.hpp @@ -0,0 +1,30 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "renderer/core/2d/sprite/sprite.hpp" +#include "renderer/core/renderer_core.hpp" + +namespace Tyra { + +class Renderer2D { + public: + Renderer2D(); + ~Renderer2D(); + + void init(RendererCore* rendererCore); + void render(Sprite* sprite); + + private: + RendererCore* core; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/bbox/bbox.hpp b/engine/inc/renderer/3d/bbox/bbox.hpp new file mode 100644 index 0000000..35fdd0c --- /dev/null +++ b/engine/inc/renderer/3d/bbox/bbox.hpp @@ -0,0 +1,64 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "./bbox_face.hpp" +#include "renderer/core/3d/bbox/core_bbox.hpp" +#include "math/vec4.hpp" + +namespace Tyra { + +/** Bounding box with more features */ +class BBox : public CoreBBox { + public: + explicit BBox(CoreBBox** t_bboxes, const u32& count); + explicit BBox(Vec4* t_vertices, u32* faces, u32 t_count); + explicit BBox(Vec4* t_vertices, u32 t_count); + explicit BBox(Vec4* t_vertices); + const float& getHeight() { return _height; } + const float& getDepth() { return _depth; } + const float& getWidth() { return _width; } + /** @returns the vector directly in middle of the bounding box. */ + const Vec4& getCenter() { return _centerVector; } + /** @returns the front face (further on z-axis) */ + const BBoxFace& getFrontFace() { return _frontFace; } + /** @returns the back face (nearer on z-axis) */ + const BBoxFace& getBackFace() { return _backFace; } + /** @returns the left face (further on x-axis) */ + const BBoxFace& getLeftFace() { return _leftFace; } + /** @returns the right face (nearer on x-axis) */ + const BBoxFace& getRightFace() { return _rightFace; } + /** @returns the top face (further on y-axis) */ + const BBoxFace& getTopFace() { return _topFace; } + /** @returns the bottom face (nearer on y-axis) */ + const BBoxFace& getBottomFace() { return _bottomFace; } + + protected: + float _height, _depth, _width; + Vec4 _centerVector; + /** Front face (further on z-axis) */ + BBoxFace _frontFace; + /** Back face (nearer on z-axis) */ + BBoxFace _backFace; + /** Left face (nearer on x-axis) */ + BBoxFace _leftFace; + /** Right face (further on x-axis) */ + BBoxFace _rightFace; + /** Top face (further on y-axis) */ + BBoxFace _topFace; + /** Bottom face (nearer on y-axis) */ + BBoxFace _bottomFace; + + private: + void setData(); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/bbox/bbox_face.hpp b/engine/inc/renderer/3d/bbox/bbox_face.hpp new file mode 100644 index 0000000..a266095 --- /dev/null +++ b/engine/inc/renderer/3d/bbox/bbox_face.hpp @@ -0,0 +1,35 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "math/vec4.hpp" + +namespace Tyra { + +/** BBox face */ +class BBoxFace { + public: + BBoxFace() {} + BBoxFace(const Vec4& t_minCorner, const Vec4& t_maxCorner, float t_axisPos) { + minCorner = t_minCorner; + maxCorner = t_maxCorner; + axisPosition = t_axisPos; + } + + /** Position of the face on it's respective axis. */ + float axisPosition; + /** Corner of the face with lower coordinates */ + Vec4 minCorner; + /** Corner of the face with higher coordinates */ + Vec4 maxCorner; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/mesh/mesh.hpp b/engine/inc/renderer/3d/mesh/mesh.hpp new file mode 100644 index 0000000..9b48ee0 --- /dev/null +++ b/engine/inc/renderer/3d/mesh/mesh.hpp @@ -0,0 +1,121 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "loaders/3d/builder/mesh_builder_data.hpp" +#include "./mesh_frame.hpp" +#include "./mesh_material_frame.hpp" +#include "./mesh_anim_state.hpp" + +namespace Tyra { + +class Mesh { + public: + explicit Mesh(const MeshBuilderData& data); + explicit Mesh(const Mesh& mesh); + ~Mesh(); + + /** Translation matrix */ + M4x4 translation; + + /** Rotation matrix */ + M4x4 rotation; + + /** Scale matrix */ + M4x4 scale; + + const u32& getId() const { return id; } + + const u8& isMother() const { return _isMother; } + + /** Get position from translation matrix */ + Vec4* getPosition() { + return reinterpret_cast(&translation.data[3 * 4]); + } + + void setPosition(const Vec4& v) { + TYRA_ASSERT(v.w == 1.0F, "Vec4 must be homogeneous"); + reinterpret_cast(&translation.data[3 * 4])->set(v); + } + + /** Returns material, which is a mesh "subgroup". */ + MeshMaterial* getMaterial(const u32& i) const { return materials[i]; } + + const u32& getMaterialsCount() const { return materialsCount; } + + const MeshAnimState& getAnimState() const { return animState; } + + /** Count of all vertices. */ + u32 getVertexCount() { return frames[0]->getVertexCount(); } + + /** Returns single frame. */ + MeshFrame* getFrame(const u32& i) const { return frames[i]; } + + /** Count of frames. Static object (not animated) will have only 1 frame. */ + const u32& getFramesCount() const { return framesCount; } + + const u32& getCurrentAnimationFrame() const { return animState.currentFrame; } + + const u32& getNextAnimationFrame() const { return animState.nextFrame; } + + const u32& getStartAnimationFrame() const { return animState.startFrame; } + + const u32& getEndAnimationFrame() const { return animState.endFrame; } + + const u32& getStayAnimationFrame() const { return animState.stayFrame; } + + M4x4 getModelMatrix() const; + + // /** + // * Returns material, which is a mesh "subgroup". + // * NULL if not found. + // */ + // MeshMaterial* getMaterialById(const u32& t_id) const; + + /** @returns bounding box object of current frame. */ + const BBox& getCurrentBoundingBox() const { + return frames[animState.currentFrame]->getBBox(); + } + + /** Check if are there any frames */ + u8 isDataLoaded() const { return framesCount > 0; } + + /** Loop in one frame */ + void playAnimation(const u32& t_frame) { playAnimation(t_frame, t_frame); } + + /** Play animation in loop from startFrame to endFrame */ + void playAnimation(const u32& t_startFrame, const u32& t_endFrame); + + /** Play animation from startFrame to endFrame and after loop in stayFrame + */ + void playAnimation(const u32& t_startFrame, const u32& t_endFrame, + const u32& t_stayFrame); + + void setAnimSpeed(const float& t_value) { animState.speed = t_value; } + + void animate(); + + /** + * Check if this class loaded mesh data first. + * Meshes which use loadFrom() method have false there. + */ + const u8& isStayAnimationSet() const { return animState.isStayFrameSet; } + + private: + void initMesh(); + MeshAnimState animState; + u8 _isMother; + u32 id, framesCount, materialsCount; + MeshFrame** frames; + MeshMaterial** materials; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/mesh/mesh_anim_state.hpp b/engine/inc/renderer/3d/mesh/mesh_anim_state.hpp new file mode 100644 index 0000000..4a02964 --- /dev/null +++ b/engine/inc/renderer/3d/mesh/mesh_anim_state.hpp @@ -0,0 +1,30 @@ + +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include + +namespace Tyra { + +typedef struct { + u32 startFrame; + u32 endFrame; + u32 stayFrame; + u8 isStayFrameSet; + float speed; + float interpolation; + u32 animType; + u32 currentFrame; + u32 nextFrame; +} MeshAnimState; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/mesh/mesh_frame.hpp b/engine/inc/renderer/3d/mesh/mesh_frame.hpp new file mode 100644 index 0000000..9aa09a4 --- /dev/null +++ b/engine/inc/renderer/3d/mesh/mesh_frame.hpp @@ -0,0 +1,51 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "renderer/3d/mesh/mesh_material.hpp" +#include "loaders/3d/builder/mesh_builder_data.hpp" +#include "renderer/3d/bbox/bbox.hpp" + +namespace Tyra { + +class MeshFrame { + public: + explicit MeshFrame(const MeshBuilderData& data, const u32& index); + explicit MeshFrame(const MeshFrame& frame); + ~MeshFrame(); + + const u32& getId() const { return id; } + const u32& getVertexCount() const { return vertexCount; } + const u32& getTextureCoordsCount() const { return textureCoordsCount; } + const u32& getNormalsCount() const { return normalsCount; } + const u32& getColorsCount() const { return colorsCount; } + const u8& isMother() const { return _isMother; } + const BBox& getBBox() const { return *bbox; } + + Vec4* getVertices() const { return vertices; } + Vec4* getNormals() const { return normals; } + Vec4* getTextureCoords() const { return textureCoords; } + Color* getColors() const { return colors; } + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; + + private: + u8 _isMother; + BBox* bbox; + u32 id, vertexCount, textureCoordsCount, normalsCount, colorsCount; + Vec4 *vertices, *textureCoords, *normals; + Color* colors; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/mesh/mesh_material.hpp b/engine/inc/renderer/3d/mesh/mesh_material.hpp new file mode 100644 index 0000000..af5d83a --- /dev/null +++ b/engine/inc/renderer/3d/mesh/mesh_material.hpp @@ -0,0 +1,57 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "debug/debug.hpp" +#include "loaders/3d/builder/mesh_builder_data.hpp" +#include "renderer/3d/mesh/mesh_material_frame.hpp" + +namespace Tyra { + +class MeshMaterial { + public: + explicit MeshMaterial(const MeshBuilderData& data, const u32& materialIndex); + explicit MeshMaterial(const MeshMaterial& material); + ~MeshMaterial(); + + Color singleColor; + + const u32& getId() const { return id; } + const std::string& getName() const { return _name; } + const u32& getFacesCount() const { return facesCount; } + const u8& isMother() const { return _isMother; } + const u8& isSingleColorActivated() const { return singleColorFlag; } + + u32* getVertexFaces() const { return vertexFaces; } + u32* getTextureCoordFaces() const { return textureCoordFaces; } + u32* getNormalFaces() const { return normalFaces; } + u32* getColorFaces() const { return normalFaces; } + MeshMaterialFrame* getFrames() const { return *frames; } + + const BBox& getBBox(const u32& frame) const; + + void setSingleColorFlag(const u8& flag); + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; + + private: + MeshMaterialFrame** frames; + + std::string _name; + u8 _isMother, singleColorFlag; + u32 id, facesCount, framesCount; + u32 *vertexFaces, *textureCoordFaces, *normalFaces, *colorFaces; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/mesh/mesh_material_frame.hpp b/engine/inc/renderer/3d/mesh/mesh_material_frame.hpp new file mode 100644 index 0000000..5cc3922 --- /dev/null +++ b/engine/inc/renderer/3d/mesh/mesh_material_frame.hpp @@ -0,0 +1,35 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "./renderer/3d/bbox/bbox.hpp" + +namespace Tyra { + +class MeshMaterialFrame { + public: + explicit MeshMaterialFrame(const MeshBuilderData& data, const u32& frameIndex, + const u32& materialIndex); + explicit MeshMaterialFrame(const MeshMaterialFrame& frame); + ~MeshMaterialFrame(); + + const u32& getId() const { return id; } + const u8& isMother() const { return _isMother; } + const BBox& getBBox() const { return *bbox; } + + private: + BBox* bbox; + + u8 _isMother; + u32 id; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/minecraft/data/mcpip_block_data.hpp b/engine/inc/renderer/3d/pipeline/minecraft/data/mcpip_block_data.hpp new file mode 100644 index 0000000..c40db9a --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/data/mcpip_block_data.hpp @@ -0,0 +1,31 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "math/vec4.hpp" + +namespace Tyra { + +class McpipBlockData { + public: + McpipBlockData(); + ~McpipBlockData(); + + u32 count; + float offset; + u32 getComboCount() const { return count * 2; } + + Tyra::Vec4* comboData; + Tyra::Vec4* vertices; + Tyra::Vec4* textureCoords; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/minecraft/data/mcpip_multi_tex_block_data.hpp b/engine/inc/renderer/3d/pipeline/minecraft/data/mcpip_multi_tex_block_data.hpp new file mode 100644 index 0000000..66ed8c6 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/data/mcpip_multi_tex_block_data.hpp @@ -0,0 +1,35 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "debug/debug.hpp" +#include "./mcpip_block_data.hpp" +#include "string" + +namespace Tyra { + +class McpipMultiTexBlockData : public McpipBlockData { + public: + McpipMultiTexBlockData(); + ~McpipMultiTexBlockData(); + + private: + Vec4* tempVerts; + Vec4* tempTexCoords; + u32* tempVertFaces; + u32* tempTexCoordsFaces; + + void allocateTempData(); + void unroll(); + void dellocateTempData(); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/minecraft/data/mcpip_single_tex_block_data.hpp b/engine/inc/renderer/3d/pipeline/minecraft/data/mcpip_single_tex_block_data.hpp new file mode 100644 index 0000000..2dad027 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/data/mcpip_single_tex_block_data.hpp @@ -0,0 +1,35 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "debug/debug.hpp" +#include "./mcpip_block_data.hpp" +#include "string" + +namespace Tyra { + +class McpipSingleTexBlockData : public McpipBlockData { + public: + McpipSingleTexBlockData(); + ~McpipSingleTexBlockData(); + + private: + Vec4* tempVerts; + Vec4* tempTexCoords; + u32* tempVertFaces; + u32* tempTexCoordsFaces; + + void allocateTempData(); + void unroll(); + void dellocateTempData(); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/minecraft/mcpip_block.hpp b/engine/inc/renderer/3d/pipeline/minecraft/mcpip_block.hpp new file mode 100644 index 0000000..7e12b86 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/mcpip_block.hpp @@ -0,0 +1,30 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "math/m4x4.hpp" +#include "math/vec4.hpp" +#include "renderer/models/color.hpp" + +namespace Tyra { + +class McpipBlock { + public: + McpipBlock() {} + ~McpipBlock() {} + + M4x4 model; + Color color; + Vec4 textureOffset; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/minecraft/minecraft_pipeline.hpp b/engine/inc/renderer/3d/pipeline/minecraft/minecraft_pipeline.hpp new file mode 100644 index 0000000..0d195c7 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/minecraft_pipeline.hpp @@ -0,0 +1,58 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "./mcpip_block.hpp" +#include "./programs/mcpip_programs_manager.hpp" +#include "renderer/3d/pipeline/renderer_3d_pipeline.hpp" +#include "renderer/core/3d/bbox/core_bbox_frustum.hpp" +#include "renderer/core/3d/bbox/render_bbox.hpp" + +namespace Tyra { + +class MinecraftPipeline : public Renderer3DPipeline { + public: + MinecraftPipeline(); + ~MinecraftPipeline(); + + void init(RendererCore* core); + + void onUse(); + + void render(McpipBlock* blocks, const u32& count, Texture* t_tex, + const bool& isMulti = false, const bool& noClipChecks = true); + + inline const float& getTextureOffset() const { + return manager.getTextureOffset(); + } + + private: + BlockizerProgramsManager manager; + RendererCore* rendererCore; + RenderBBox* bbox; + McpipProgramName latestMode; + + void initBBox(); + + void changeMode(const McpipProgramName& requestedMode, const u8& force); + + void cull(McpipBlock* blocks, const std::vector& indexes, + RendererCoreTextureBuffers* texBuffers, + const bool& isMulti = false); + + void clip(McpipBlock* blocks, const std::vector& indexes, + RendererCoreTextureBuffers* texBuffers, + const bool& isMulti = false); + + Tyra::CoreBBoxFrustum isInFrustum(const McpipBlock& block) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_as_is_vu1_program.hpp b/engine/inc/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_as_is_vu1_program.hpp new file mode 100644 index 0000000..c63a37e --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_as_is_vu1_program.hpp @@ -0,0 +1,28 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "debug/debug.hpp" +#include "renderer/3d/pipeline/minecraft/programs/mcpip_program.hpp" + +namespace Tyra { + +class McpipAsIsVU1Program : public McpipProgram { + public: + McpipAsIsVU1Program(); + ~McpipAsIsVU1Program(); + + std::string getStringName() const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_clip.hpp b/engine/inc/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_clip.hpp new file mode 100644 index 0000000..17ef516 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_clip.hpp @@ -0,0 +1,70 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "debug/debug.hpp" +#include "renderer/renderer.hpp" +#include "packet2/packet2_tyra_utils.hpp" +#include "../mcpip_programs_repository.hpp" +#include "../../mcpip_block.hpp" +#include "../../data/mcpip_block_data.hpp" +#include "./mcpip_vu1_as_is_shared_defines.h" +#include "renderer/core/paths/path1/clipper/path1_ee_clip_algorithm.hpp" + +namespace Tyra { + +class McpipClip { + public: + McpipClip(); + ~McpipClip(); + + Path1EEClipAlgorithm algorithm; + Path1EEClipAlgorithmSettings algoSettings; + + void init(RendererCore* core, McpipBlockData* t_singleBlockData, + McpipBlockData* t_multiBlockData); + + u32 uploadVU1Program(McpipProgramsRepository* repo, const u32& addr); + + void configureVU1AndSendStaticData(); + + void addData(McpipBlock* block, const bool& isMulti, + RendererCoreTextureBuffers* texBuffers, packet2_t* packet, + const u8& context); + + private: + RendererCore* rendererCore; + McpipBlockData* singleBlockData; + McpipBlockData* multiBlockData; + packet2_t* staticPacket; + u16 vu1DBufferSize; + + std::vector clippedTriangle; + std::vector inputTriangle; + + Vec4 vertexBuffers[2][108]; + Vec4 texCoordBuffers[2][108]; + + void addCorrections(std::vector* vertices, + McpipBlock* block); + void moveDataToBuffer(std::vector* vertices, + const u8& context); + void addDataToPacket(packet2_t* packet, const u8& context, McpipBlock* block, + const int& count, + RendererCoreTextureBuffers* texBuffers); + + void initStaticPacket(); + void sendVU1StaticData(); + void setDBufferSize(); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_vu1_as_is_shared_defines.h b/engine/inc/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_vu1_as_is_shared_defines.h new file mode 100644 index 0000000..95b9915 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_vu1_as_is_shared_defines.h @@ -0,0 +1,22 @@ +// +// ______ ____ ___ +// | \/ ____| |___| +// | | | \ | | +//----------------------------------------------------------------------- +// Copyright 2022, tyra - https://github.com/h4570/tyra +// Licenced under Apache License 2.0 +// Sandro Sobczyński +// + +// Static data for blockizer program + +#define VU1_MCPIP_AS_IS_STATIC_LOD 0 +#define VU1_MCPIP_AS_IS_STATIC_SET_TAG 1 +#define VU1_MCPIP_AS_IS_STATIC_LAST_DATA_ADDR 1 + +// Dynamic data (unpack per VU1 call) +#define VU1_MCPIP_AS_IS_DYNAMIC_SCALE 0 +#define VU1_MCPIP_AS_IS_DYNAMIC_PRIM 1 +#define VU1_MCPIP_AS_IS_DYNAMIC_CLUT 2 +#define VU1_MCPIP_AS_IS_DYNAMIC_COLOR 3 +#define VU1_MCPIP_AS_IS_DYNAMIC_VERTEX_DATA_ADDR 4 diff --git a/engine/inc/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull.hpp b/engine/inc/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull.hpp new file mode 100644 index 0000000..8166bf3 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull.hpp @@ -0,0 +1,52 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "debug/debug.hpp" +#include "renderer/renderer.hpp" +#include "packet2/packet2_tyra_utils.hpp" +#include "../mcpip_programs_repository.hpp" +#include "../../mcpip_block.hpp" +#include "../../data/mcpip_block_data.hpp" +#include "./mcpip_vu1_cull_shared_defines.h" + +namespace Tyra { + +class McpipCull { + public: + McpipCull(); + ~McpipCull(); + + void init(RendererCore* core, McpipBlockData* t_blockData); + + u32 uploadVU1Program(McpipProgramsRepository* repo, const u32& addr); + + void configureVU1AndSendStaticData(); + + u32 getMaxBlocksCountPerQBuffer() const; + + void addData(packet2_t* packet, McpipBlock** blockPointerArray, + u32 blockPointerArrayCount, + RendererCoreTextureBuffers* texBuffers, bool isMulti); + + private: + RendererCore* rendererCore; + u16 vu1DBufferSize; + McpipBlockData* blockData; + packet2_t* staticPacket; + + void initStaticPacket(); + void sendVU1StaticData(); + void setDBufferSize(); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull_vu1_program.hpp b/engine/inc/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull_vu1_program.hpp new file mode 100644 index 0000000..166e57f --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull_vu1_program.hpp @@ -0,0 +1,28 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "debug/debug.hpp" +#include "renderer/3d/pipeline/minecraft/programs/mcpip_program.hpp" + +namespace Tyra { + +class McpipCullVU1Program : public McpipProgram { + public: + McpipCullVU1Program(); + ~McpipCullVU1Program(); + + std::string getStringName() const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/minecraft/programs/cull/mcpip_vu1_cull_shared_defines.h b/engine/inc/renderer/3d/pipeline/minecraft/programs/cull/mcpip_vu1_cull_shared_defines.h new file mode 100644 index 0000000..1653e90 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/programs/cull/mcpip_vu1_cull_shared_defines.h @@ -0,0 +1,33 @@ +// +// ______ ____ ___ +// | \/ ____| |___| +// | | | \ | | +//----------------------------------------------------------------------- +// Copyright 2022, tyra - https://github.com/h4570/tyra +// Licenced under Apache License 2.0 +// Sandro Sobczyński +// + +// Static data for blockizer program + +#define VU1_MCPIP_CULL_VERTEX_COUNT 36 +#define VU1_MCPIP_CULL_QWORDS_PER_BLOCK 6 + +#define VU1_MCPIP_CULL_STATIC_LOD 0 +#define VU1_MCPIP_CULL_STATIC_PRIM 1 +#define VU1_MCPIP_CULL_STATIC_SET_TAG 2 +#define VU1_MCPIP_CULL_STATIC_VU1_OPTIONS 3 +#define VU1_MCPIP_CULL_STATIC_VERTEX_DATA 4 +#define VU1_MCPIP_CULL_STATIC_TEX_COORD_DATA 4 + 36 +#define VU1_MCPIP_CULL_STATIC_LAST_DATA_ADDR \ + VU1_MCPIP_CULL_STATIC_TEX_COORD_DATA + 36 + +// Dynamic data (unpack per VU1 call) +#define VU1_MCPIP_CULL_DYNAMIC_SCALE_AND_BLOCKS_COUNT_ADDR 0 +#define VU1_MCPIP_CULL_DYNAMIC_CLUT_TEX 1 +#define VU1_MCPIP_CULL_DYNAMIC_VIEW_PROJ_MATRIX_ADDR 2 +#define VU1_MCPIP_CULL_DYNAMIC_BLOCKS_DATA 6 + +// Output mini double buffer inside VU1 mem +#define VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF1_ADDR 774 +#define VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF2_ADDR 887 diff --git a/engine/inc/renderer/3d/pipeline/minecraft/programs/mcpip_program.hpp b/engine/inc/renderer/3d/pipeline/minecraft/programs/mcpip_program.hpp new file mode 100644 index 0000000..1bc95b1 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/programs/mcpip_program.hpp @@ -0,0 +1,29 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "./mcpip_program_name.hpp" +#include "renderer/core/paths/path1/vu1_program.hpp" + +namespace Tyra { + +class McpipProgram : public VU1Program { + public: + McpipProgram(const McpipProgramName& name, u32* start, u32* end); + ~McpipProgram(); + + const McpipProgramName& getName() const; + + protected: + McpipProgramName name; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/minecraft/programs/mcpip_program_name.hpp b/engine/inc/renderer/3d/pipeline/minecraft/programs/mcpip_program_name.hpp new file mode 100644 index 0000000..d52d7ac --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/programs/mcpip_program_name.hpp @@ -0,0 +1,21 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +namespace Tyra { + +enum McpipProgramName { + UndefinedMcpipProgram, + McPipCull, + McPipAsIs, +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/minecraft/programs/mcpip_programs_manager.hpp b/engine/inc/renderer/3d/pipeline/minecraft/programs/mcpip_programs_manager.hpp new file mode 100644 index 0000000..aecff93 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/programs/mcpip_programs_manager.hpp @@ -0,0 +1,75 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "debug/debug.hpp" +#include "renderer/renderer.hpp" +#include "./cull/mcpip_cull.hpp" +#include "./as_is/mcpip_clip.hpp" +#include "./mcpip_programs_repository.hpp" +#include "../data/mcpip_single_tex_block_data.hpp" +#include "../data/mcpip_multi_tex_block_data.hpp" +#include "../mcpip_block.hpp" + +namespace Tyra { + +enum McpipStaticBlockData { + BlockNotUploaded, + BlockMultiUploaded, + BlockSingleUploaded +}; + +class BlockizerProgramsManager { + public: + BlockizerProgramsManager(); + ~BlockizerProgramsManager(); + + McpipCull culler; + McpipClip clipper; + + void init(RendererCore* core); + + void uploadVU1Programs(); + + void clearLastProgram() { lastProgramName = UndefinedMcpipProgram; } + + void cull(McpipBlock** blockPointerArray, u32 blockPointerArrayCount, + RendererCoreTextureBuffers* texBuffers, const bool& isMulti); + + void clip(McpipBlock* block, RendererCoreTextureBuffers* texBuffers, + const bool& isMulti); + + inline const float& getTextureOffset() const { + return singleTexBlockData.offset; + } + + const McpipBlockData& getBlockData() const { return singleTexBlockData; } + + private: + McpipProgramsRepository repo; + Renderer* renderer; + McpipSingleTexBlockData singleTexBlockData; + McpipMultiTexBlockData multiTexBlockData; + McpipProgramName lastProgramName; + + packet2_t* programsPacket; + packet2_t* staticPacket; + packet2_t* dynamicPackets[2]; + u8 context; + McpipStaticBlockData vu1BlockData; + + void setProgramsCache(); + void uploadBlock(bool isMulti); + void sendPacket(McpipProgram* program); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/minecraft/programs/mcpip_programs_repository.hpp b/engine/inc/renderer/3d/pipeline/minecraft/programs/mcpip_programs_repository.hpp new file mode 100644 index 0000000..45c52fe --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/minecraft/programs/mcpip_programs_repository.hpp @@ -0,0 +1,32 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "debug/debug.hpp" +#include "./mcpip_program.hpp" +#include "cull/mcpip_cull_vu1_program.hpp" +#include "as_is/mcpip_as_is_vu1_program.hpp" + +namespace Tyra { + +class McpipProgramsRepository { + public: + McpipProgramsRepository(); + ~McpipProgramsRepository(); + + McpipProgram* getProgram(const McpipProgramName& name); + + private: + McpipCullVU1Program cull; + McpipAsIsVU1Program asIs; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/renderer_3d_pipeline.hpp b/engine/inc/renderer/3d/pipeline/renderer_3d_pipeline.hpp new file mode 100644 index 0000000..7b9f3a6 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/renderer_3d_pipeline.hpp @@ -0,0 +1,26 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "renderer/core/renderer_core.hpp" + +namespace Tyra { + +class Renderer3DPipeline { + public: + Renderer3DPipeline() {} + ~Renderer3DPipeline() {} + + virtual void init(RendererCore* core) = 0; + virtual void onUse() = 0; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_package.hpp b/engine/inc/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_package.hpp new file mode 100644 index 0000000..194b28d --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_package.hpp @@ -0,0 +1,50 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "../stdpip_bag.hpp" +#include "renderer/core/3d/bbox/core_bbox_frustum.hpp" + +namespace Tyra { + +class StdpipBagPackage { + public: + StdpipBagPackage(); + ~StdpipBagPackage(); + + StdpipBag* bag; + + Vec4* vertices; + Vec4* sts; + Vec4* normals; + Vec4* colors; + /** maxVertCount is max value, because we decided to put max maxVertCount + * verts to single quad buffer in VU1 */ + u16 size; + CoreBBoxFrustum isInFrustum; + + /** + * We are creating StdpipBagPackagesBBox which checks CoreBBoxBBox for every + * maxVertCount / 3. So this variable is index of starting + * StdpipBagPackagesBBox's CoreBBoxBBox. If package have <= maxVertCount / 3 + * verts, we will need only single (starting) bbox. if package have + * maxVertCount verts, we will calculate CoreBBoxBBox from 3 + * StdpipBagPackagesBBox's bboxes. + */ + u32 indexOf1By3BBox; + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packager.hpp b/engine/inc/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packager.hpp new file mode 100644 index 0000000..aa91507 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packager.hpp @@ -0,0 +1,52 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "renderer/core/3d/bbox/core_bbox.hpp" +#include "renderer/core/3d/renderer_3d_frustum_planes.hpp" +#include "renderer/core/paths/path1/clipper/path1_ee_clip_algorithm.hpp" +#include "./stdpip_bag_packages_bbox.hpp" +#include "./stdpip_bag_package.hpp" +#include "../stdpip_bag.hpp" + +namespace Tyra { + +class StdpipBagPackager { + public: + StdpipBagPackager(); + ~StdpipBagPackager(); + + void init(Renderer3DFrustumPlanes* frustumPlanes); + void setRenderBBox(StdpipBagPackagesBBox* bbox) { renderBBox = bbox; } + void setMaxVertCount(const u32& count); + + /** + * @brief Create render packages from provided render data + * + * @param size Max maxVertCount verts (VU1 buffer size) + */ + StdpipBagPackage* create(u16* o_size, StdpipBag* data, u16 size); + /** + * @brief Split render package to smaller packages + * + * @param size Max maxVertCount verts (VU1 buffer size) + */ + StdpipBagPackage* create(u16* o_size, const StdpipBagPackage& pkg, u16 size); + + CoreBBoxFrustum checkFrustum(const StdpipBagPackage& pkg); + + private: + u32 maxVertCount; + Renderer3DFrustumPlanes* frustumPlanes; + StdpipBagPackagesBBox* renderBBox; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packages_bbox.hpp b/engine/inc/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packages_bbox.hpp new file mode 100644 index 0000000..ed7a460 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packages_bbox.hpp @@ -0,0 +1,69 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "renderer/core/3d/bbox/render_bbox.hpp" + +namespace Tyra { + +/** + * Splits all 3D input vertices into 16vert parts and creates child bboxes for + * it + */ +class StdpipBagPackagesBBox { + public: + StdpipBagPackagesBBox(Vec4* t_vertices, u32* t_faces, const u32& t_facesCount, + const u32& t_maxVertCount); + StdpipBagPackagesBBox(Vec4* t_vertices, const u32& t_counts, + const u32& t_maxVertCount); + ~StdpipBagPackagesBBox(); + + void setMaxVertCount(const u32& count); + + /** + * @param index Index of part + */ + const RenderBBox& getChildBBox1By3(const u32& index) const; + + /** @brief CoreBBox created from all of vertices */ + RenderBBox* getMainBBox(); + + /** @brief Get amount of child Bbox1/3 parts */ + const u32& getPartsCount() const; + + /** @brief Get amount of vertices */ + const u32& getVertexCount() const; + + /** + * @brief For example if we want to build a bbox from 96-112 vertices, we + * should input index = 3, partsSize = 1 + * @param index Index of part + * @param partsSize How many 1/3 parts + */ + RenderBBox createChildBBox(const u32& index, const u16& partsSize) const; + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; + + private: + u32 maxVertCount; + u32 vertexCount, partsCount; + + /** @brief CoreBBox of all vertices */ + RenderBBox* mainBBox; + + /** @brief Bounding boxes of 1/3 vertices */ + std::vector* bboxParts; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_bag.hpp b/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_bag.hpp new file mode 100644 index 0000000..30b4ffa --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_bag.hpp @@ -0,0 +1,62 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "math/vec4.hpp" +#include "./stdpip_lighting_bag.hpp" +#include "./stdpip_info_bag.hpp" +#include "./stdpip_color_bag.hpp" +#include "./stdpip_texture_bag.hpp" +#include "renderer/core/texture/models/texture.hpp" +#include "./packaging/stdpip_bag_packages_bbox.hpp" + +namespace Tyra { + +/** + * @brief 3D Render data bag. + * Supports frustum culling, clipping, lighting, + * texture and single color / many colors. + */ +class StdpipBag { + public: + StdpipBag(); + ~StdpipBag(); + + /** Mandatory. Object info. */ + StdpipInfoBag* info; + + /** Mandatory. Object color(s). */ + StdpipColorBag* color; + + /** Mandatory. Vertex count. */ + u32 count; + + /** Mandatory. Vertices. */ + Vec4* vertices; + + /** Optional. Texture coordinates and image. */ + StdpipTextureBag* texture; + + /** Optional. Object lighting. */ + StdpipLightingBag* lighting; + + /** + * @param maxVertCount This parameter is available in renderer API. + */ + StdpipBagPackagesBBox calculateBbox(const u32& maxVertCount); + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_color_bag.hpp b/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_color_bag.hpp new file mode 100644 index 0000000..e6a7132 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_color_bag.hpp @@ -0,0 +1,32 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "renderer/models/color.hpp" + +namespace Tyra { + +/** + * @brief Color data. At least one color data is required (single/many). + */ +class StdpipColorBag { + public: + StdpipColorBag(); + ~StdpipColorBag(); + + /** Optional. Single color for all vertices. */ + Color* single; + + /** Optional. Color per vertex. */ + Color* many; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_info_bag.hpp b/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_info_bag.hpp new file mode 100644 index 0000000..3e6c94d --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_info_bag.hpp @@ -0,0 +1,34 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "math/m4x4.hpp" +#include "math/vec4.hpp" +#include "../../stdpip_shading_type.hpp" + +namespace Tyra { + +class StdpipInfoBag { + public: + StdpipInfoBag(); + ~StdpipInfoBag(); + + /** Mandatory. Model matrix */ + M4x4* model; + + StdpipShadingType shadingType; + bool blendingEnabled; + bool antiAliasingEnabled; + bool noClipChecks; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_lighting_bag.hpp b/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_lighting_bag.hpp new file mode 100644 index 0000000..f399c9d --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_lighting_bag.hpp @@ -0,0 +1,56 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "renderer/models/color.hpp" +#include "math/m4x4.hpp" +#include "math/vec4.hpp" + +namespace Tyra { + +enum StdpipLightingBagMode { Auto, Manual }; + +class StdpipLightingBag { + public: + explicit StdpipLightingBag(const bool& manual = false); + ~StdpipLightingBag(); + + Vec4* normals; + M4x4* lightMatrix; + + void setAmbientColor(const Color& color); + void setDirectionalLightColors(Color* colors, const u8& count); + void setDirectionalLightDirections(Vec4* directions, const u8& count); + void setDirectionalLightColor(const Color& color, const u8& index); + void setDirectionalLightDirection(const Vec4& direction, const u8& index); + + void setLightsManually(Vec4* colors, Vec4* directions); + void disableManualMode(); + + Vec4* getLightColors() { return lightColors; } + + Vec4* getLightDirections() { return lightDirections; } + + void forceDeallocateColors(); + void forceDeallocateDirections(); + + private: + void allocate(); + void deallocate(); + void forceDeallocate(); + u8 isAllocated; + StdpipLightingBagMode mode; + + Vec4* lightColors; + Vec4* lightDirections; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_texture_bag.hpp b/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_texture_bag.hpp new file mode 100644 index 0000000..9ee3e70 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/bag/stdpip_texture_bag.hpp @@ -0,0 +1,30 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "math/vec4.hpp" +#include "renderer/core/texture/models/texture.hpp" + +namespace Tyra { + +class StdpipTextureBag { + public: + StdpipTextureBag(); + ~StdpipTextureBag(); + + /** Mandatory. Texture coordinates per vertex. */ + Vec4* coordinates; + + /** Mandatory. Texture image. */ + Texture* texture; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_c_vu1_program.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_c_vu1_program.hpp new file mode 100644 index 0000000..3b337ff --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_c_vu1_program.hpp @@ -0,0 +1,30 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "../../stdpip_vu1_program.hpp" + +namespace Tyra { + +class StdpipAsIsCVU1Program : public StdpipVU1Program { + public: + StdpipAsIsCVU1Program(); + ~StdpipAsIsCVU1Program(); + + std::string getStringName() const; + + void addProgramQBufferDataToPacket(packet2_t* packet, + StdpipQBuffer* qbuffer) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_d_vu1_program.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_d_vu1_program.hpp new file mode 100644 index 0000000..9f3f621 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_d_vu1_program.hpp @@ -0,0 +1,29 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "../../stdpip_vu1_program.hpp" + +namespace Tyra { + +class StdpipAsIsDVU1Program : public StdpipVU1Program { + public: + StdpipAsIsDVU1Program(); + ~StdpipAsIsDVU1Program(); + + std::string getStringName() const; + void addProgramQBufferDataToPacket(packet2_t* packet, + StdpipQBuffer* qbuffer) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_tc_vu1_program.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_tc_vu1_program.hpp new file mode 100644 index 0000000..c63e6c8 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_tc_vu1_program.hpp @@ -0,0 +1,29 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "../../stdpip_vu1_program.hpp" + +namespace Tyra { + +class StdpipAsIsTCVU1Program : public StdpipVU1Program { + public: + StdpipAsIsTCVU1Program(); + ~StdpipAsIsTCVU1Program(); + + std::string getStringName() const; + void addProgramQBufferDataToPacket(packet2_t* packet, + StdpipQBuffer* qbuffer) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_td_vu1_program.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_td_vu1_program.hpp new file mode 100644 index 0000000..8c45f9a --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_td_vu1_program.hpp @@ -0,0 +1,29 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "../../stdpip_vu1_program.hpp" + +namespace Tyra { + +class StdpipAsIsTDVU1Program : public StdpipVU1Program { + public: + StdpipAsIsTDVU1Program(); + ~StdpipAsIsTDVU1Program(); + + std::string getStringName() const; + void addProgramQBufferDataToPacket(packet2_t* packet, + StdpipQBuffer* qbuffer) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_c_vu1_program.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_c_vu1_program.hpp new file mode 100644 index 0000000..4979595 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_c_vu1_program.hpp @@ -0,0 +1,29 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "../../stdpip_vu1_program.hpp" + +namespace Tyra { + +class StdpipCullCVU1Program : public StdpipVU1Program { + public: + StdpipCullCVU1Program(); + ~StdpipCullCVU1Program(); + + std::string getStringName() const; + void addProgramQBufferDataToPacket(packet2_t* packet, + StdpipQBuffer* qbuffer) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_d_vu1_program.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_d_vu1_program.hpp new file mode 100644 index 0000000..1d77e12 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_d_vu1_program.hpp @@ -0,0 +1,29 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "../../stdpip_vu1_program.hpp" + +namespace Tyra { + +class StdpipCullDVU1Program : public StdpipVU1Program { + public: + StdpipCullDVU1Program(); + ~StdpipCullDVU1Program(); + + std::string getStringName() const; + void addProgramQBufferDataToPacket(packet2_t* packet, + StdpipQBuffer* qbuffer) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_tc_vu1_program.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_tc_vu1_program.hpp new file mode 100644 index 0000000..e1d62f6 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_tc_vu1_program.hpp @@ -0,0 +1,29 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "../../stdpip_vu1_program.hpp" + +namespace Tyra { + +class StdpipCullTCVU1Program : public StdpipVU1Program { + public: + StdpipCullTCVU1Program(); + ~StdpipCullTCVU1Program(); + + std::string getStringName() const; + void addProgramQBufferDataToPacket(packet2_t* packet, + StdpipQBuffer* qbuffer) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_td_vu1_program.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_td_vu1_program.hpp new file mode 100644 index 0000000..5da4969 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_td_vu1_program.hpp @@ -0,0 +1,29 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "../../stdpip_vu1_program.hpp" + +namespace Tyra { + +class StdpipCullTDVU1Program : public StdpipVU1Program { + public: + StdpipCullTDVU1Program(); + ~StdpipCullTDVU1Program(); + + std::string getStringName() const; + void addProgramQBufferDataToPacket(packet2_t* packet, + StdpipQBuffer* qbuffer) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h new file mode 100644 index 0000000..712c07a --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h @@ -0,0 +1,24 @@ +// +// ______ ____ ___ +// | \/ ____| |___| +// | | | \ | | +//----------------------------------------------------------------------- +// Copyright 2022, tyra - https://github.com/h4570/tyra +// Licenced under Apache License 2.0 +// Sandro Sobczyński +// + +// Updated once per mesh +#define VU1_MVP_MATRIX_ADDR 0 +#define VU1_LIGHTS_MATRIX_ADDR 4 +#define VU1_SINGLE_COLOR_ADDR 7 +#define VU1_OPTIONS_ADDR 8 +#define VU1_LOD_ADDR 9 +#define VU1_CLUT_ADDR 10 +#define VU1_LIGHTS_DIRS_ADDR 11 +#define VU1_LIGHTS_COLORS_ADDR 14 +#define VU1_SET_GIFTAG_ADDR 18 +#define VU1_LAST_ITEM_ADDR 18 + +// Buffer data (xtop) +#define VU1_VERT_DATA_ADDR 2 diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_clipper.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_clipper.hpp new file mode 100644 index 0000000..ab598e9 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_clipper.hpp @@ -0,0 +1,47 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "debug/debug.hpp" +#include "./stdpip_qbuffer.hpp" +#include "renderer/renderer_settings.hpp" +#include "renderer/core/paths/path1/clipper/path1_ee_clip_algorithm.hpp" + +namespace Tyra { + +/** + * @brief This class requires VU1 buffer with max buffsize/3 vertices. + * It will clip triangles and fill the buffer. + * + * To be honest clipping algorithm should be moved to VU1 and "AsIs" VU1 program + * should be renamed to "Clip" - I don't want to do it now, too much time. + */ +class StdpipClipper { + public: + StdpipClipper(); + ~StdpipClipper(); + void clip(StdpipQBuffer* buffer); + void init(const RendererSettings& settings); + void setMaxVertCount(const u32& count); + void setMVP(M4x4* mvp); + + private: + u32 maxVertCount; + Path1EEClipAlgorithm algorithm; + M4x4* mvp; + + void perspectiveDivide(std::vector* vertices); + void moveDataToBuffer(const std::vector& vertices, + StdpipQBuffer* buffer); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_program_name.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_program_name.hpp new file mode 100644 index 0000000..4eba03f --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_program_name.hpp @@ -0,0 +1,31 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +namespace Tyra { + +enum StdpipProgramName { + StdipUndefinedProgram, + + StdpipCullColor, + StdpipAsIsColor, + + StdpipCullDirLights, + StdpipAsIsDirLights, + + StdpipCullTextureDirLights, + StdpipAsIsTextureDirLights, + + StdpipCullTextureColor, + StdpipAsIsTextureColor, +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_program_type.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_program_type.hpp new file mode 100644 index 0000000..3618c56 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_program_type.hpp @@ -0,0 +1,22 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +namespace Tyra { + +enum StdpipProgramType { + StdpipVU1Color, + StdpipVU1DirLights, + StdpipVU1TextureDirLights, + StdpipVU1TextureColor, +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_programs_repository.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_programs_repository.hpp new file mode 100644 index 0000000..26ef7e2 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_programs_repository.hpp @@ -0,0 +1,49 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "debug/debug.hpp" +#include "renderer/core/paths/path1/vu1_program.hpp" + +#include "./programs/as_is/stdpip_as_is_c_vu1_program.hpp" +#include "./programs/as_is/stdpip_as_is_d_vu1_program.hpp" +#include "./programs/as_is/stdpip_as_is_td_vu1_program.hpp" +#include "./programs/as_is/stdpip_as_is_tc_vu1_program.hpp" + +#include "./programs/cull/stdpip_cull_c_vu1_program.hpp" +#include "./programs/cull/stdpip_cull_d_vu1_program.hpp" +#include "./programs/cull/stdpip_cull_td_vu1_program.hpp" +#include "./programs/cull/stdpip_cull_tc_vu1_program.hpp" + +#include "renderer/core/paths/path1/programs/draw_finish/vu1_draw_finish.hpp" + +namespace Tyra { + +class StdpipProgramsRepository { + public: + StdpipProgramsRepository(); + ~StdpipProgramsRepository(); + + StdpipVU1Program* getProgram(const StdpipProgramName& name); + + private: + StdpipAsIsCVU1Program asIsColor; + StdpipCullCVU1Program cullColor; + StdpipAsIsDVU1Program asIsLightingColor; + StdpipCullDVU1Program cullLightingColor; + StdpipAsIsTDVU1Program asIsLightingTextureColor; + StdpipCullTDVU1Program cullLightingTextureColor; + StdpipAsIsTCVU1Program asIsTextureColor; + StdpipCullTCVU1Program cullTextureColor; + VU1DrawFinish drawFinish; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_qbuffer.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_qbuffer.hpp new file mode 100644 index 0000000..588f3bc --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_qbuffer.hpp @@ -0,0 +1,84 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include +#include "debug/debug.hpp" +#include "../bag/packaging/stdpip_bag_package.hpp" +#include "../bag/stdpip_bag.hpp" + +namespace Tyra { + +class StdpipQBuffer { + public: + StdpipQBuffer(); + ~StdpipQBuffer(); + + void setMaxVertCount(const u32& count); + + /** + * @brief Dont allocate any dynamic data in buffer. + * Just copy pointers to input data. + */ + void fillByPointer(const StdpipBagPackage& pkg); + + /** + * @brief Allocate dynamic data in buffer + * And copy input data to it. + */ + void fillByCopyMax(const StdpipBagPackage& pkg1, const StdpipBagPackage& pkg2, + const StdpipBagPackage& pkg3); + + /** + * @brief Allocate dynamic data in buffer + * And copy input data to it. + */ + void fillByCopy1By2(const StdpipBagPackage& pkg1, + const StdpipBagPackage& pkg2); + + /** + * @brief Allocate dynamic data in buffer + * And copy input data to it. + */ + void fillByCopy1By3(const StdpipBagPackage& pkg); + + /** + * @brief Deallocate dynamic data if it was allocated and allocate new data + * specified by size. + * @param size 48 is max + */ + void reallocateManually(const u16& size); + + bool any() const; + + StdpipBag* bag; + + Vec4* vertices; + Vec4* sts; + Vec4* colors; + Vec4* normals; + u32 size; + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; + + private: + u32 maxVertCount; + void deallocateDynamicData(); + void allocateDynamicData(u16 size, StdpipBag* bag); + u8 _isDynamicallyAllocated, _stAllocated, _colorAllocated, _normalAllocated; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_qbuffer_renderer.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_qbuffer_renderer.hpp new file mode 100644 index 0000000..150a028 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_qbuffer_renderer.hpp @@ -0,0 +1,98 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include +#include "debug/debug.hpp" +#include "math/m4x4.hpp" +#include "renderer/renderer_settings.hpp" +#include "renderer/models/color.hpp" +#include "./stdpip_qbuffer.hpp" +#include "./stdpip_program_type.hpp" +#include "./stdpip_program_name.hpp" +#include "./stdpip_programs_repository.hpp" +#include "./stdpip_clipper.hpp" +#include "renderer/core/paths/path1/path1.hpp" +#include "renderer/core/renderer_core.hpp" +#include "renderer/core/texture/renderer_core_texture_buffers.hpp" + +namespace Tyra { + +class StdpipQBufferRenderer { + public: + StdpipQBufferRenderer(); + ~StdpipQBufferRenderer(); + + void init(RendererCore* t_core); + + void reinitVU1(); + + void setClipperMVP(M4x4* mvp) { clipper.setMVP(mvp); } + + StdpipQBuffer* getBuffer(); + + void sendObjectData(StdpipBag* bag, M4x4* mvp, + RendererCoreTextureBuffers* texBuffers) const; + + void setMaxVertCount(const u32& count); + + void setInfo(StdpipInfoBag* bag); + + /** Fast render with culling */ + void cull(StdpipQBuffer* buffer); + + /** Slower render with clipping */ + void clip(StdpipQBuffer* buffer); + + void clearLastProgramName(); + + StdpipVU1Program* getCullProgramByBag(const StdpipBag* bag); + + StdpipVU1Program* getCullProgramByParams(const bool& isLightingEnabled, + const bool& isTextureEnabled); + + const u16& getBufferSize() { return bufferSize; } + + private: + void sendStaticData() const; + void setProgramsCache(); + void uploadPrograms(); + void setDoubleBuffer(); + + StdpipVU1Program* getProgramByName(const StdpipProgramName& name); + void addBufferDataToPacket(StdpipVU1Program* program, StdpipQBuffer* buffer); + void sendPacket(); + StdpipVU1Program* getAsIsProgramByBag(const StdpipBag* bag); + StdpipVU1Program* getCullProgramByType(const StdpipProgramType& programType); + StdpipProgramType getDrawProgramTypeByBag(const StdpipBag* bag) const; + StdpipProgramType getDrawProgramTypeByParams( + const bool& isLightingEnabled, const bool& isTextureEnabled) const; + packet2_t* packets[2]; + packet2_t* programsPacket; + StdpipQBuffer buffers[2]; + packet2_t* currentPacket; + packet2_t* staticDataPacket; + packet2_t* objectDataPacket; + + RendererCore* rendererCore; + + StdpipProgramName lastProgramName; + Path1* path1; + StdpipClipper clipper; + StdpipProgramsRepository repository; + + u16 bufferSize; + u8 context; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_vu1_program.hpp b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_vu1_program.hpp new file mode 100644 index 0000000..c2cecba --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/path1/stdpip_vu1_program.hpp @@ -0,0 +1,50 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "./stdpip_program_name.hpp" +#include "renderer/core/paths/path1/vu1_program.hpp" +#include "./stdpip_qbuffer.hpp" +#include "./programs/stdpip_vu1_shared_defines.h" + +namespace Tyra { + +class StdpipVU1Program : public VU1Program { + public: + StdpipVU1Program(const StdpipProgramName& name, u32* start, u32* end, + const u32& t_reglist, const u8& t_reglistCount, + const u8& t_elementsPerVertex); + ~StdpipVU1Program(); + + u32& getReglist(); + + const StdpipProgramName& getName() const; + + u16 getMaxVertCount(const bool& singleColorEnabled, + const u16& vu1DBufferSize) const; + + void addBufferDataToPacket(packet2_t* packet, StdpipQBuffer* buffer, + prim_t* prim); + + protected: + StdpipProgramName name; + u8 reglistCount, elementsPerVertex; + u32 destinationAddress, reglist; + + virtual void addProgramQBufferDataToPacket(packet2_t* packet, + StdpipQBuffer* qbuffer) const = 0; + + private: + void addStandardBufferDataToPacket(packet2_t* packet, StdpipQBuffer* buffer, + prim_t* prim); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/core/std_pipeline_core.hpp b/engine/inc/renderer/3d/pipeline/std/core/std_pipeline_core.hpp new file mode 100644 index 0000000..9b44486 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/core/std_pipeline_core.hpp @@ -0,0 +1,59 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "./bag/stdpip_bag.hpp" +#include "./bag/packaging/stdpip_bag_packages_bbox.hpp" +#include "./bag/packaging/stdpip_bag_package.hpp" +#include "./bag/packaging/stdpip_bag_packager.hpp" +#include "./path1/stdpip_qbuffer_renderer.hpp" + +namespace Tyra { + +class StdPipelineCore { + public: + StdPipelineCore(); + ~StdPipelineCore(); + + void init(RendererCore* t_core); + + /** Render 3D data via "bags" */ + void render(StdpipBag* data, StdpipBagPackagesBBox* bbox = nullptr); + + /** Get max vert count of VU1 qbuffer (for optimizations) */ + u32 getMaxVertCountByParams(const bool& isSingleColor, + const bool& isLightingEnabled, + const bool& isTextureEnabled); + + /** Get max vert count of VU1 qbuffer (for optimizations) */ + u32 getMaxVertCountByBag(const StdpipBag* bag); + + /** + * - Uploads standard VU1 programs. + * - Sends static "Tyra Renderer3D" VU1 data. + * - Sets double buffers exactly for "Tyra Renderer3D" + * Should be called if VU1 was used by your non standard programs. + */ + void reinitStandardVU1Programs(); + + private: + u32 maxVertCount; + RendererCore* rendererCore; + + void setMaxVertCount(const u32& count); + StdpipBagPackager packager; + StdpipQBufferRenderer qbufferRenderer; + void renderPkgs(StdpipBagPackage* packages, u16 count); + void renderSubpkgs(StdpipBagPackage* packages, u16 count); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/std_pipeline.hpp b/engine/inc/renderer/3d/pipeline/std/std_pipeline.hpp new file mode 100644 index 0000000..44c87d3 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/std_pipeline.hpp @@ -0,0 +1,59 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "../renderer_3d_pipeline.hpp" +#include "./stdpip_options.hpp" +#include "renderer/core/renderer_core.hpp" +#include "renderer/3d/mesh/mesh.hpp" +#include "./core/std_pipeline_core.hpp" + +namespace Tyra { + +class StdPipeline : public Renderer3DPipeline { + public: + StdPipeline(); + ~StdPipeline(); + + void init(RendererCore* core); + + void onUse(); + + /** + * Render 3D data via "meshes". + * This render() method is a bridge to core.renderer3D.render() method. + */ + void render(Mesh* mesh, const StdpipOptions* options = nullptr); + + private: + StdPipelineCore core; + RendererCore* rendererCore; + Vec4* colorsCache; + + void addVertices(Mesh* mesh, MeshMaterial* material, StdpipBag* bag, + MeshFrame* frameFrom, MeshFrame* frameTo) const; + StdpipInfoBag* getInfoBag(Mesh* mesh, const StdpipOptions* options, + M4x4* model) const; + StdpipColorBag* getColorBag(Mesh* mesh, MeshMaterial* material, + MeshFrame* frameFrom, MeshFrame* frameTo) const; + StdpipTextureBag* getTextureBag(Mesh* mesh, MeshMaterial* material, + MeshFrame* frameFrom, MeshFrame* frameTo); + StdpipLightingBag* getLightingBag(Mesh* mesh, MeshMaterial* material, + M4x4* model, MeshFrame* frameFrom, + MeshFrame* frameTo, + const StdpipOptions* options) const; + void deallocDrawBags(StdpipBag* bag, MeshMaterial* material) const; + + void setLightingColorsCache(StdpipLightingOptions* lightingOptions); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/stdpip_lighting_options.hpp b/engine/inc/renderer/3d/pipeline/std/stdpip_lighting_options.hpp new file mode 100644 index 0000000..ae4bc85 --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/stdpip_lighting_options.hpp @@ -0,0 +1,44 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "math/vec4.hpp" +#include "renderer/models/color.hpp" + +namespace Tyra { + +class StdpipLightingOptions { + public: + StdpipLightingOptions() {} + ~StdpipLightingOptions() {} + + /** + * Mandatory. + * Default 128.0F, 128.0F, 128.0F, 128.0F + */ + Color* ambientColor; + + /** + * Mandatory. + * Min/max length - 3. + * Example color value: 64.0F, 0.0F, 0.0F, 1.0F + */ + Color* directionalColors; + + /** + * Mandatory. + * Min/max length - 3. + * Example dir value: 1.0F, 0.0F, 0.0F, 1.0F + */ + Vec4* directionalDirections; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/stdpip_options.hpp b/engine/inc/renderer/3d/pipeline/std/stdpip_options.hpp new file mode 100644 index 0000000..71f207b --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/stdpip_options.hpp @@ -0,0 +1,32 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "./stdpip_shading_type.hpp" +#include "./stdpip_lighting_options.hpp" + +namespace Tyra { + +class StdpipOptions { + public: + StdpipOptions() {} + ~StdpipOptions() {} + + StdpipShadingType shadingType; + bool blendingEnabled; + bool antiAliasingEnabled; + bool noClipChecks; + + /** Optional */ + StdpipLightingOptions* lighting; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/pipeline/std/stdpip_shading_type.hpp b/engine/inc/renderer/3d/pipeline/std/stdpip_shading_type.hpp new file mode 100644 index 0000000..d38b2fb --- /dev/null +++ b/engine/inc/renderer/3d/pipeline/std/stdpip_shading_type.hpp @@ -0,0 +1,20 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +namespace Tyra { + +enum StdpipShadingType { + StdpipShadingFlat, + StdpipShadingGouraud, +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/3d/renderer_3d.hpp b/engine/inc/renderer/3d/renderer_3d.hpp new file mode 100644 index 0000000..70d4401 --- /dev/null +++ b/engine/inc/renderer/3d/renderer_3d.hpp @@ -0,0 +1,28 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "./pipeline/renderer_3d_pipeline.hpp" + +namespace Tyra { + +class Renderer3D { + public: + Renderer3D(); + ~Renderer3D(); + + void usePipeline(Renderer3DPipeline* pipeline); + + private: + Renderer3DPipeline* currentPipeline; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/2d/renderer_core_2d.hpp b/engine/inc/renderer/core/2d/renderer_core_2d.hpp new file mode 100644 index 0000000..3a20422 --- /dev/null +++ b/engine/inc/renderer/core/2d/renderer_core_2d.hpp @@ -0,0 +1,44 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "debug/debug.hpp" +#include "renderer/core/2d/sprite/sprite.hpp" +#include "renderer/core/texture/renderer_core_texture_buffers.hpp" +#include "renderer/core/texture/models/texture.hpp" +#include "renderer/renderer_settings.hpp" +#include +#include + +namespace Tyra { + +class RendererCore2D { + public: + RendererCore2D(); + ~RendererCore2D(); + + void init(RendererSettings* settings, clutbuffer_t* clutBuffer); + + void render(Sprite* sprite, const RendererCoreTextureBuffers& texBuffers, + Texture* texture); + + private: + static const float GS_CENTER; + static const float SCREEN_CENTER; + + u8 context; + RendererSettings* settings; + clutbuffer_t* clutBuffer; + packet2_t* packets[2]; + texrect_t* rects[2]; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/2d/sprite/sprite.hpp b/engine/inc/renderer/core/2d/sprite/sprite.hpp new file mode 100644 index 0000000..04ac169 --- /dev/null +++ b/engine/inc/renderer/core/2d/sprite/sprite.hpp @@ -0,0 +1,71 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "./sprite_mode.hpp" +#include "math/vec2.hpp" +#include "renderer/models/color.hpp" + +namespace Tyra { + +class Sprite { + public: + Sprite(); + ~Sprite(); + + Vec2 position, size; + float scale; + Color color; + + // ---- + // Getters + // ---- + + /** Auto generated unique Id. */ + inline const u32& getId() const { return id; }; + + /** Get sprite drawing mode. */ + inline const SpriteMode& getMode() const { return mode; }; + + // ---- + // Setters + // ---- + + /** Set sprite drawing mode. */ + void setMode(const SpriteMode& t_val) { mode = t_val; } + + // ---- + // Other + // ---- + + /** Flip texture horizontally. */ + inline void flipHorizontally(const u8& t_set) { _flipH = t_set; }; + + /** Flip texture vertically. */ + inline void flipVertically(const u8& t_set) { _flipV = t_set; }; + + /** Check if texture is vertically flipped. */ + inline const u8& isFlippedVertically() const { return _flipV; }; + + /** Check if texture is horizontally flipped. */ + inline const u8& isFlippedHorizontally() const { return _flipH; }; + + private: + u32 id; + SpriteMode mode; + u8 _isSizeSet, _flipH, _flipV; + void setDefaultColor(); + void setDefaultLODAndClut(); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/2d/sprite/sprite_mode.hpp b/engine/inc/renderer/core/2d/sprite/sprite_mode.hpp new file mode 100644 index 0000000..c968d84 --- /dev/null +++ b/engine/inc/renderer/core/2d/sprite/sprite_mode.hpp @@ -0,0 +1,17 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +namespace Tyra { + +enum SpriteMode { MODE_STRETCH, MODE_REPEAT }; + +} diff --git a/engine/inc/renderer/core/3d/bbox/core_bbox.hpp b/engine/inc/renderer/core/3d/bbox/core_bbox.hpp new file mode 100644 index 0000000..7e0c4b1 --- /dev/null +++ b/engine/inc/renderer/core/3d/bbox/core_bbox.hpp @@ -0,0 +1,43 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "./core_bbox_frustum.hpp" +#include "math/m4x4.hpp" +#include "math/plane.hpp" + +namespace Tyra { + +/** Bounding box */ +class CoreBBox { + public: + explicit CoreBBox(Vec4* t_vertices, u32* faces, u32 t_count); + explicit CoreBBox(Vec4* t_vertices, u32 t_count); + explicit CoreBBox(Vec4* t_vertices); + explicit CoreBBox(CoreBBox** t_bboxes, const u32& count); + explicit CoreBBox(const std::vector& t_bboxes, + const u32& startIndex, const u32& stopIndex); + + const Vec4& operator[](const u8& i) { return _vertices[i]; } + const Vec4& getVertex(const u8& i) { return _vertices[i]; } + Vec4* getVertices() { return _vertices; } + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; + + protected: + Vec4 _vertices[8]; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/3d/bbox/core_bbox_frustum.hpp b/engine/inc/renderer/core/3d/bbox/core_bbox_frustum.hpp new file mode 100644 index 0000000..3b64b99 --- /dev/null +++ b/engine/inc/renderer/core/3d/bbox/core_bbox_frustum.hpp @@ -0,0 +1,17 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +namespace Tyra { + +enum CoreBBoxFrustum { IN_FRUSTUM, OUTSIDE_FRUSTUM, PARTIALLY_IN_FRUSTUM }; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/3d/bbox/render_bbox.hpp b/engine/inc/renderer/core/3d/bbox/render_bbox.hpp new file mode 100644 index 0000000..e45f092 --- /dev/null +++ b/engine/inc/renderer/core/3d/bbox/render_bbox.hpp @@ -0,0 +1,35 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "./core_bbox.hpp" + +namespace Tyra { + +/** Bounding box */ +class RenderBBox : public CoreBBox { + public: + explicit RenderBBox(Vec4* t_vertices, u32* faces, u32 t_count); + explicit RenderBBox(Vec4* t_vertices, u32 t_count); + explicit RenderBBox(Vec4* t_vertices); + explicit RenderBBox(CoreBBox** t_bboxes, const u32& count); + explicit RenderBBox(const std::vector& t_bboxes, + const u32& startIndex, const u32& stopIndex); + + CoreBBoxFrustum isInFrustum(const Plane* frustumPlanes, const M4x4& model, + const float* margins = nullptr) const; + + CoreBBoxFrustum clipIsInFrustum(const Plane* frustumPlanes, + const M4x4& model) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/3d/camera_info_3d.hpp b/engine/inc/renderer/core/3d/camera_info_3d.hpp new file mode 100644 index 0000000..895da8c --- /dev/null +++ b/engine/inc/renderer/core/3d/camera_info_3d.hpp @@ -0,0 +1,29 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "math/vec4.hpp" + +namespace Tyra { + +class CameraInfo3D { + public: + CameraInfo3D(Vec4* cameraPosition, Vec4* cameraLooksAt, + Vec4* cameraUp = nullptr); + ~CameraInfo3D(); + + Vec4 *position, *looksAt, *up; + + private: + static Vec4 defaultCameraUp; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/3d/renderer_3d_frustum_planes.hpp b/engine/inc/renderer/core/3d/renderer_3d_frustum_planes.hpp new file mode 100644 index 0000000..3b4ab6d --- /dev/null +++ b/engine/inc/renderer/core/3d/renderer_3d_frustum_planes.hpp @@ -0,0 +1,45 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "renderer/renderer_settings.hpp" +#include "./camera_info_3d.hpp" +#include "math/plane.hpp" + +namespace Tyra { + +class Renderer3DFrustumPlanes { + public: + Renderer3DFrustumPlanes(); + ~Renderer3DFrustumPlanes(); + + void init(RendererSettings* settings, const float& fov); + void update(const CameraInfo3D& cameraInfo, const float& fov); + const Plane& get(u8 index) const { return frustumPlanes[index]; } + const Plane* getAll() const { return frustumPlanes; } + const Plane& operator[](u8 index) const { return frustumPlanes[index]; } + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; + + private: + void computeStaticData(const float& fov); + float lastFov; + RendererSettings* settings; + Plane frustumPlanes[6]; + float nearHeight, nearWidth, farHeight, farWidth; + Vec4 nearCenter, farCenter, X, Y, Z, ntl, ntr, nbl, nbr, ftl, fbr, ftr, fbl; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/3d/renderer_core_3d.hpp b/engine/inc/renderer/core/3d/renderer_core_3d.hpp new file mode 100644 index 0000000..1df90b7 --- /dev/null +++ b/engine/inc/renderer/core/3d/renderer_core_3d.hpp @@ -0,0 +1,90 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "renderer/renderer_settings.hpp" +#include "./renderer_3d_frustum_planes.hpp" +#include "./camera_info_3d.hpp" +#include "renderer/core/texture/models/texture.hpp" +#include "renderer/core/3d/bbox/core_bbox.hpp" +#include "renderer/core/paths/path1/path1.hpp" + +namespace Tyra { + +class RendererCore3D { + public: + RendererCore3D(); + ~RendererCore3D(); + + /** Current camera frustum planes. */ + Renderer3DFrustumPlanes frustumPlanes; + + /** Called by renderer. */ + void init(RendererSettings* settings, Path1* t_path1); + + const float& getFov() const { return fov; } + + void setFov(const float& t_fov); + + /** + * Called by beginFrame(); + * Updates camera info, to get proper frustum culling. + */ + void update(const CameraInfo3D& cameraInfo); + + /** Get projection (screen) matrix */ + const M4x4& getProjection() { return projection; } + + /** + * Get view (camera) matrix + * Updated at every beginFrame() + */ + const M4x4& getView() { return view; } + + /** + * Get projection * view matrix + * Updated at every beginFrame() + */ + const M4x4& getViewProj() { return viewProj; } + + /** + * @brief + * Upload VU1 program. + * Please pay attention that you will be REPLACING + * current VU1 programs + * + * @param address Starting address of your program. + * @return Address of the end of your program, so next program can start from + * this + 1 + */ + u32 uploadVU1Program(VU1Program* program, const u32& address); + + /** + * @brief Set VU1 double buffer + * + * @param startingAddress Starting address. Example 10. + * @param bufferSize Buffer size. Example 490, so second buffer will start + * from 490+10 + */ + void setVU1DoubleBuffers(const u16& startingAddress, const u16& bufferSize); + + private: + M4x4 view, projection, viewProj; + float fov; + + RendererSettings* settings; + + Path1* path1; + void setProjection(); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/gs/renderer_core_gs.hpp b/engine/inc/renderer/core/gs/renderer_core_gs.hpp new file mode 100644 index 0000000..796e162 --- /dev/null +++ b/engine/inc/renderer/core/gs/renderer_core_gs.hpp @@ -0,0 +1,51 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "renderer/renderer_settings.hpp" + +namespace Tyra { + +class RendererCoreGS { + public: + RendererCoreGS(); + ~RendererCoreGS(); + + prim_t prim; + lod_t lod; + zbuffer_t zBuffer; + + void init(RendererSettings* settings); + + void flipBuffers(); + + float getVRamFreeSpaceInMB() const { + return 4.0F - spaceOccupiedByFrameBuffers - spaceOccupiedByZBuffer; + } + + private: + constexpr static float gsCenter = 4096.0F; + constexpr static float screenCenter = gsCenter / 2.0F; + + float spaceOccupiedByFrameBuffers, spaceOccupiedByZBuffer; + RendererSettings* settings; + framebuffer_t frameBuffers[2]; + packet2_t* flipPacket; + u8 context; + + void allocateBuffers(); + void initDrawingEnvironment(); + void setPrim(); + void setLod(); + void initChannels(); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/paths/path1/clipper/path1_clip_vertex.hpp b/engine/inc/renderer/core/paths/path1/clipper/path1_clip_vertex.hpp new file mode 100644 index 0000000..f9d6f3c --- /dev/null +++ b/engine/inc/renderer/core/paths/path1/clipper/path1_clip_vertex.hpp @@ -0,0 +1,21 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include "math/vec4.hpp" + +namespace Tyra { + +struct Path1ClipVertex { + Vec4 position, normal, st, color; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/paths/path1/clipper/path1_ee_clip_algorithm.hpp b/engine/inc/renderer/core/paths/path1/clipper/path1_ee_clip_algorithm.hpp new file mode 100644 index 0000000..bcca06c --- /dev/null +++ b/engine/inc/renderer/core/paths/path1/clipper/path1_ee_clip_algorithm.hpp @@ -0,0 +1,52 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "./path1_clip_vertex.hpp" +#include "debug/debug.hpp" +#include "renderer/renderer_settings.hpp" + +namespace Tyra { + +struct Path1EEClipAlgorithmSettings { + bool lerpNormals, lerpTexCoords, lerpColors; +}; + +class Path1EEClipAlgorithm { + public: + Path1EEClipAlgorithm(); + ~Path1EEClipAlgorithm(); + + void init(const RendererSettings& settings); + + void clip(std::vector* o_vertices, + const std::vector& vertices, + const Path1EEClipAlgorithmSettings& settings); + + static float clipMargin; + + private: + float halfWidth, halfHeight, near, far; + std::vector tempVertices; + + float getValueByPlane(const Path1ClipVertex& v, const int& plane); + + bool isInside(const int& plane, const float& v, const float& w, + const float& planeLimitValue); + + void clipAgainstPlane(const std::vector& original, + std::vector* clipped, const int& plane, + const float& planeLimitValue, + const Path1EEClipAlgorithmSettings& settings); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/paths/path1/path1.hpp b/engine/inc/renderer/core/paths/path1/path1.hpp new file mode 100644 index 0000000..0b74635 --- /dev/null +++ b/engine/inc/renderer/core/paths/path1/path1.hpp @@ -0,0 +1,36 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "debug/debug.hpp" +#include "./vu1_program.hpp" + +namespace Tyra { + +class Path1 { + public: + Path1(); + ~Path1(); + + u32 uploadProgram(VU1Program* program, const u32& address); + + packet2_t* createProgramsCache(VU1Program** programs, const u32& count, + const u32& address); + + void setDoubleBuffer(const u16& startingAddress, const u16& bufferSize); + + private: + packet2_t* doubleBufferPacket; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/paths/path1/programs/draw_finish/vu1_draw_finish.hpp b/engine/inc/renderer/core/paths/path1/programs/draw_finish/vu1_draw_finish.hpp new file mode 100644 index 0000000..538549b --- /dev/null +++ b/engine/inc/renderer/core/paths/path1/programs/draw_finish/vu1_draw_finish.hpp @@ -0,0 +1,28 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "../../vu1_program.hpp" + +namespace Tyra { + +class VU1DrawFinish : public VU1Program { + public: + VU1DrawFinish(); + ~VU1DrawFinish(); + + std::string getStringName() const; + void addTag(packet2_t* packet, prim_t* prim) const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/paths/path1/vu1_program.hpp b/engine/inc/renderer/core/paths/path1/vu1_program.hpp new file mode 100644 index 0000000..5d50a32 --- /dev/null +++ b/engine/inc/renderer/core/paths/path1/vu1_program.hpp @@ -0,0 +1,41 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include + +namespace Tyra { + +class VU1Program { + public: + VU1Program(u32* start, u32* end); + ~VU1Program(); + + const u32& getPacketSize() const; + const u32& getProgramSize() const; + const u32& getDestinationAddress() const; + + u32* getStart() const; + u32* getEnd() const; + + virtual std::string getStringName() const = 0; + + void setDestinationAddress(const u32& addr); + + protected: + u32 packetSize, destinationAddress, programSize; + u32 *start, *end; + u32 calculateProgramSize() const; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/paths/path3/path3.hpp b/engine/inc/renderer/core/paths/path3/path3.hpp new file mode 100644 index 0000000..aebfa05 --- /dev/null +++ b/engine/inc/renderer/core/paths/path3/path3.hpp @@ -0,0 +1,42 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include +#include "renderer/core/texture/models/texture.hpp" +#include "renderer/renderer_settings.hpp" +#include "renderer/models/color.hpp" +#include "renderer/core/texture/renderer_core_texture_buffers.hpp" + +namespace Tyra { + +class Path3 { + public: + Path3(); + ~Path3(); + + void init(RendererSettings* settings); + + void addDrawFinishTag(); + void clearScreen(zbuffer_t* z, const Color& color); + void sendTexture(Texture* texture, + const RendererCoreTextureBuffers& texBuffers); + + private: + packet2_t* drawFinishPacket; + packet2_t* clearScreenPacket; + packet2_t* texturePacket; + RendererSettings* settings; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/renderer_core.hpp b/engine/inc/renderer/core/renderer_core.hpp new file mode 100644 index 0000000..3bef33f --- /dev/null +++ b/engine/inc/renderer/core/renderer_core.hpp @@ -0,0 +1,73 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "./2d/renderer_core_2d.hpp" +#include "./3d/renderer_core_3d.hpp" +#include "./gs/renderer_core_gs.hpp" +#include "./texture/renderer_core_texture.hpp" +#include "./paths/path3/path3.hpp" +#include "./paths/path1/path1.hpp" +#include "./renderer_core_sync.hpp" + +namespace Tyra { + +class RendererCore { + public: + RendererCore(); + ~RendererCore(); + + /** Responsible for initializing GS. */ + RendererCoreGS gs; + + /** All logic responsible for 3D drawing. */ + RendererCore3D renderer3D; + + /** All logic responsible for 2D drawing. */ + RendererCore2D renderer2D; + + /** Texture transferring. */ + RendererCoreTexture texture; + + /** EE <-> VU1 synchronization */ + RendererCoreSync sync; + + /** Called by renderer */ + void init(); + + /** World background color */ + void setClearScreenColor(const Color& color); + + /** Clear screen and update view frustum for frustum culling. */ + void beginFrame(const CameraInfo3D& cameraInfo); + + /** VSync and swap frame double buffer. */ + void endFrame(); + + void setFrameLimit(const bool& onoff) { isFrameLimitOn = onoff; } + + /** Get screen settings */ + const RendererSettings& getSettings() const { return settings; } + + Path1* getPath1() { return &path1; } + + Path3* getPath3() { return &path3; } + + private: + bool isFrameLimitOn; + Color bgColor; + RendererSettings settings; + Path3 path3; + Path1 path1; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/renderer_core_sync.hpp b/engine/inc/renderer/core/renderer_core_sync.hpp new file mode 100644 index 0000000..529d332 --- /dev/null +++ b/engine/inc/renderer/core/renderer_core_sync.hpp @@ -0,0 +1,50 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "./paths/path3/path3.hpp" + +namespace Tyra { + +/** + * Synchronization class. + * Mainly between VU1 and EE. + * + * For example you can set texture, render X vertices, then add() wait, and + * wait() for it. Without it, there is risk for example to send new texture + * during drawing with previous one. + */ +class RendererCoreSync { + public: + RendererCoreSync(); + ~RendererCoreSync(); + + void init(Path3* path3); + + /** Send draw finish tag via VU1 to GS. */ + void add(); + /** Check if finish flag was set by GS. */ + u8 check(); + /** Clear finish tag flag. */ + void clear(); + /** Wait for finish flag and clear it. */ + void waitAndClear(); + + /** Clear() -> Add() -> waitAndClear() */ + void align(); + + private: + Path3* path3; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/texture/cache_manager/renderer_texture_cache_manager.hpp b/engine/inc/renderer/core/texture/cache_manager/renderer_texture_cache_manager.hpp new file mode 100644 index 0000000..fd07635 --- /dev/null +++ b/engine/inc/renderer/core/texture/cache_manager/renderer_texture_cache_manager.hpp @@ -0,0 +1,48 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include +#include "renderer/core/texture/models/texture.hpp" +#include "./renderer_texture_cm_analysis.hpp" +#include "../renderer_core_texture_buffers.hpp" + +namespace Tyra { + +class RendererTextureCacheManager { + public: + RendererTextureCacheManager(); + ~RendererTextureCacheManager(); + + void onFrameChange(); + + void addRequestedTexture(Texture* texture); + + u32 getTextureIdToDealloc( + const std::vector& currentAllocs); + + private: + RendererTextureCMAnalysis statistics; + + void tryAddIdThatNotExistInAnalysis( + std::vector* result, + const std::vector& currentAllocs, + const std::vector& analysis); + + void addIdThatExistInAnalysisButIsOnTheBottom( + std::vector* result, + const std::vector& currentAllocs, + const std::vector& analysis); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/texture/cache_manager/renderer_texture_cm_analysis.hpp b/engine/inc/renderer/core/texture/cache_manager/renderer_texture_cm_analysis.hpp new file mode 100644 index 0000000..e635ec3 --- /dev/null +++ b/engine/inc/renderer/core/texture/cache_manager/renderer_texture_cm_analysis.hpp @@ -0,0 +1,73 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "debug/debug.hpp" +#include +#include + +namespace Tyra { + +struct RendererTextureCMAnalysisWeight { + u32 id; + u16 weight; +}; + +struct RendererTextureCMAnalysisOccurence { + u32 id; + u8 occurence; +}; + +class RendererTextureCMAnalysis { + public: + RendererTextureCMAnalysis(); + ~RendererTextureCMAnalysis(); + + void changeFrame(); + + void addNewRequest(const u32& id); + + /** + * @brief Returns top list (from most used to least used) of textures to be + * deallocated. + * + * @param count Number of textures to be returned. + */ + std::vector getTopTextureIdsForNextChanges(const u8& count); + + bool isReady(); + + bool isProbablyNotCorrect(); + + private: + std::vector*current, *last; + std::vector buffers[2]; + u32 currentIndex; + + void flipBuffers(); + + std::vector getNextTextureIds(const u8& count); + + std::vector getOccurencesForTextureIds( + const std::vector& textureIds); + + std::vector getTextureIdsUnique(const std::vector& textureIds); + + std::vector getWeights( + const std::vector& uniqueTexIds, + const std::vector& occurencies); + + std::vector weightsToResult( + std::vector* weights); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/texture/models/texture.hpp b/engine/inc/renderer/core/texture/models/texture.hpp new file mode 100644 index 0000000..2c57423 --- /dev/null +++ b/engine/inc/renderer/core/texture/models/texture.hpp @@ -0,0 +1,140 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include +#include "./texture_link.hpp" +#include "./texture_wrap.hpp" +#include "../texture_data.hpp" +#include "loaders/texture/builder/texture_builder_data.hpp" +#include "debug/debug.hpp" + +namespace Tyra { + +/** + * Class which contains texture data. + * Textures are paired with meshes/sprites via addLink() and + * removeLink() functions which use meshId/spriteId and materialId (for mesh). + */ +class Texture { + public: + Texture(TextureBuilderData* data); + ~Texture(); + + inline const u32& getId() const { return id; } + + inline const int& getWidth() const { return core->width; } + + inline const int& getHeight() const { return core->height; } + + float getSizeInMB() const; + + inline u32 getTextureLinksCount() const { + return static_cast(links.size()); + } + + inline texwrap_t* getWrapSettings() { return &wrap; } + + const TextureData& getCoreData() const { return *core; } + + TextureData* getClutData() { return clut; } + + /** + * Get texture name. + * For "textures/abc.bmp" result will be: "abc" + */ + inline const std::string& getName() const { return name; } + + /** Array of texture links. Size of getTextureLinksCount() */ + inline const std::vector& getTextureLinks() const { + return links; + } + + /** + * Returns index of link. + * -1 if not found. + * @param t_id + * For 3D: MeshMaterial material id. + * For 2D: Sprite id. + */ + const s32 getIndexOfLink(const u32& t_id) const { + for (u32 i = 0; i < links.size(); i++) + if (links[i].id == t_id) return i; + return -1; + } + + /** Set texture wrapping */ + void setWrapSettings(const TextureWrap t_horizontal, + const TextureWrap t_vertical); + + // ---- + // Other + // ---- + + /** Assign texture to mesh and mesh material. */ + void addLink(const u32& t_meshId, const u32& t_materialId); + + /** Assign texture to sprite. */ + void addLink(const u32& t_spriteId); + + u32 getTextureSize() const; + + /** + * Check if texture is linked with MeshMaterial/Sprite. + * @param t_id + * For 3D: MeshMaterial material id. + * For 2D: Sprite id. + */ + const u8 isLinkedWith(const u32& t_id) const { + s32 index = getIndexOfLink(t_id); + if (index != -1) + return true; + else + return false; + } + + void removeLinkByIndex(const u32& t_index) { + links.erase(links.begin() + t_index); + } + + /** + * Remove texture link with given MeshMaterial/Sprite. + * @param t_id + * For 3D: MeshMaterial material id. + * For 2D: Sprite id. + */ + void removeLinkById(const u32& t_id) { + s32 index = getIndexOfLink(t_id); + TYRA_ASSERT(index != -1, "Cant remove link, because it was not found!"); + removeLinkByIndex(index); + } + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; + + private: + void setPsm(); + void setDefaultWrapSettings(); + + u32 id; + std::string name; + + TextureData* core; + TextureData* clut; + + texwrap_t wrap; + std::vector links; +}; +} // namespace Tyra diff --git a/engine/inc/renderer/core/texture/models/texture_bpp.hpp b/engine/inc/renderer/core/texture/models/texture_bpp.hpp new file mode 100644 index 0000000..69bd7df --- /dev/null +++ b/engine/inc/renderer/core/texture/models/texture_bpp.hpp @@ -0,0 +1,44 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +namespace Tyra { + +enum TextureBpp { + + // Tyra has 1.3MB~ free VRAM for textures. + + /** + * @brief 32-bit RGBA - Slowest + * Tyra can cache about 4~ of these textures (256x256) + */ + bpp32 = 32, + + /** + * @brief 24-bit RGB - Slow + * Tyra can cache about 6~ of these textures (256x256) + */ + bpp24 = 24, + + /** + * @brief 8-bit palletized (indexed) - Super fast + * Tyra can cache about 19~ of these textures (256x256) + */ + bpp8 = 8, + + /** + * @brief 4-bit palletized (indexed) - Super, super fast + * Tyra can cache about 39~ of these textures (256x256) + */ + bpp4 = 4, +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/texture/models/texture_link.hpp b/engine/inc/renderer/core/texture/models/texture_link.hpp new file mode 100644 index 0000000..3a2d47a --- /dev/null +++ b/engine/inc/renderer/core/texture/models/texture_link.hpp @@ -0,0 +1,23 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include + +namespace Tyra { +struct TextureLink { + /** + * For 3D: MeshMaterial material id. + * For 2D: Sprite id. + */ + u32 id; +}; +} // namespace Tyra diff --git a/engine/inc/renderer/core/texture/models/texture_wrap.hpp b/engine/inc/renderer/core/texture/models/texture_wrap.hpp new file mode 100644 index 0000000..758253e --- /dev/null +++ b/engine/inc/renderer/core/texture/models/texture_wrap.hpp @@ -0,0 +1,15 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +namespace Tyra { +enum TextureWrap { Repeat = 0, Clamp = 1, RegionClamp = 2, RegionRepeat = 3 }; +} diff --git a/engine/inc/renderer/core/texture/renderer_core_texture.hpp b/engine/inc/renderer/core/texture/renderer_core_texture.hpp new file mode 100644 index 0000000..1e83929 --- /dev/null +++ b/engine/inc/renderer/core/texture/renderer_core_texture.hpp @@ -0,0 +1,55 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "./texture_repository.hpp" +#include "./renderer_core_texture_sender.hpp" +#include "renderer/core/paths/path3/path3.hpp" +#include "./cache_manager/renderer_texture_cache_manager.hpp" +#include "./renderer_core_texture_buffers.hpp" + +namespace Tyra { + +class RendererCoreTexture { + public: + RendererCoreTexture(); + ~RendererCoreTexture(); + + clutbuffer_t clut; + TextureRepository repository; + + RendererCoreTextureBuffers useTexture(Texture* t_tex); + + /** Called by renderer during initialization */ + void init(RendererCoreGS* gs, Path3* path3); + + /** Called by renderer during beginFrame() */ + void onFrameChange(); + + /** Called by renderer during rendering */ + void updateClutBuffer(texbuffer_t* clutBuffer); + + private: + RendererTextureCacheManager cacheManager; + std::vector currentAllocations; + + void initClut(); + void registerAllocation(const RendererCoreTextureBuffers& t_buffers); + void unregisterAllocation(const u32& textureId); + RendererCoreTextureBuffers getAllocatedBuffersByTextureId(const u32& id); + + RendererCoreTextureSender sender; + Path3* path3; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/texture/renderer_core_texture_buffers.hpp b/engine/inc/renderer/core/texture/renderer_core_texture_buffers.hpp new file mode 100644 index 0000000..3c6e0e9 --- /dev/null +++ b/engine/inc/renderer/core/texture/renderer_core_texture_buffers.hpp @@ -0,0 +1,35 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include + +namespace Tyra { + +struct RendererCoreTextureBuffers { + /** Texture id */ + u32 id; + + /** + * Texture data. + * Used in: 4bpp, 8bpp, 24bpp, 32bpp. + */ + texbuffer_t* core; + + /** + * Texture pallete data. + * Used in: 4bpp, 8bpp (otherwise nullptr). + */ + texbuffer_t* clut; +}; + +} // namespace Tyra \ No newline at end of file diff --git a/engine/inc/renderer/core/texture/renderer_core_texture_sender.hpp b/engine/inc/renderer/core/texture/renderer_core_texture_sender.hpp new file mode 100644 index 0000000..5ee7624 --- /dev/null +++ b/engine/inc/renderer/core/texture/renderer_core_texture_sender.hpp @@ -0,0 +1,49 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include +#include "./models/texture.hpp" +#include "renderer/core/paths/path3/path3.hpp" +#include "renderer/core/gs/renderer_core_gs.hpp" +#include "./renderer_core_texture_buffers.hpp" + +namespace Tyra { + +class RendererCoreTextureSender { + public: + RendererCoreTextureSender(); + ~RendererCoreTextureSender(); + + void init(Path3* path3, RendererCoreGS* gs); + + RendererCoreTextureBuffers allocate(Texture* t_texture); + + void deallocate(const RendererCoreTextureBuffers& texBuffers); + + float getFreeVRamInMB(); + + float getSizeInMB(texbuffer_t* texBuffer); + + private: + float allocatedVRamMemForTextures; + RendererCoreGS* gs; + Path3* path3; + TextureBpp getBppByPsm(const u32& psm); + texbuffer_t* allocateTextureCore(Texture* t_texture); + texbuffer_t* allocateTextureClut(Texture* t_texture); + + u8 isTextureVRAMAllocated, isVSyncEnabled; +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/core/texture/texture_data.hpp b/engine/inc/renderer/core/texture/texture_data.hpp new file mode 100644 index 0000000..a6091b4 --- /dev/null +++ b/engine/inc/renderer/core/texture/texture_data.hpp @@ -0,0 +1,40 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "renderer/core/texture/models/texture_bpp.hpp" + +namespace Tyra { + +class TextureData { + public: + TextureData(unsigned char* data, const TextureBpp& bpp, + const unsigned char& gsComponents, const int& width, + const int& height); + ~TextureData(); + + int width, height; + u32 psm; + unsigned char* data; + TextureBpp bpp; + unsigned char components; + + void print() const; + void print(const unsigned char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const unsigned char* name = nullptr) const; + + private: + u32 getPsmByBpp(const TextureBpp& bpp); +}; +} // namespace Tyra diff --git a/engine/inc/renderer/core/texture/texture_repository.hpp b/engine/inc/renderer/core/texture/texture_repository.hpp new file mode 100644 index 0000000..9ff627d --- /dev/null +++ b/engine/inc/renderer/core/texture/texture_repository.hpp @@ -0,0 +1,128 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include +#include "./models/texture.hpp" +#include "renderer/3d/mesh/mesh.hpp" +#include "loaders/texture/base/texture_loader_selector.hpp" +#include + +namespace Tyra { +class TextureRepository { + public: + TextureRepository(); + ~TextureRepository(); + + /** Returns all repository textures. */ + std::vector* getAll() { return &textures; } + + u32 getTexturesCount() const { return static_cast(textures.size()); } + + /** + * Returns single texture. + * nullptr if not found. + * @param t_id + * For 3D: MeshMaterial id. + * For 2D: Sprite id. + */ + Texture* getBySpriteOrMesh(const u32& t_id) { + for (u32 i = 0; i < textures.size(); i++) + if (textures[i]->isLinkedWith(t_id)) return textures[i]; + return nullptr; + } + + /** + * Returns single texture. + * nullptr if not found. + */ + Texture* getByTextureId(const u32& t_id) const { + for (u32 i = 0; i < textures.size(); i++) + if (t_id == textures[i]->getId()) return textures[i]; + return nullptr; + } + + /** + * Returns index of link. + * -1 if not found. + */ + const s32 getIndexOf(const u32& t_texId) const { + for (u32 i = 0; i < textures.size(); i++) + if (textures[i]->getId() == t_texId) return i; + return -1; + } + + // ---- + // Setters + // ---- + + // ---- + // Other + // ---- + + /** + * Add unlinked texture. + * @param fullpath Full path to texture file. Example: "host:texture.png" + */ + Texture* add(const char* fullpath); + + /** + * Add unlinked texture. + * @param fullpath Full path to texture file. Example: "host:texture.png" + */ + inline Texture* add(const std::string& fullpath) { + return add(fullpath.c_str()); + } + + /** + * Add unlinked texture. + * @param texture Your own texture + */ + Texture* add(Texture* texture); + + /** + * Add linked textures in given path for mesh material names. + */ + void addByMesh(Mesh* mesh, const char* directory, const char* extension); + + /** + * Add linked textures in given path for mesh material names. + */ + inline void addByMesh(Mesh* mesh, const std::string& directory, + const char* extension) { + addByMesh(mesh, directory.c_str(), extension); + } + + /** + * Remove texture from repository. + * Texture is NOT destructed. + */ + void removeByIndex(const u32& t_index) { + textures.erase(textures.begin() + t_index); + } + + /** + * Remove texture from repository. + * Texture is NOT destructed. + */ + const void removeById(const u32& t_texId) { + s32 index = getIndexOf(t_texId); + TYRA_ASSERT(index != -1, "Cant remove texture, because it was not found!"); + removeByIndex(index); + } + + private: + std::vector textures; + TextureLoaderSelector texLoaderSelector; +}; +} // namespace Tyra diff --git a/engine/inc/renderer/models/color.hpp b/engine/inc/renderer/models/color.hpp new file mode 100644 index 0000000..25e0e3e --- /dev/null +++ b/engine/inc/renderer/models/color.hpp @@ -0,0 +1,84 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "math/vec4.hpp" + +namespace Tyra { + +class Color { + public: + union { + struct { + /** 0-255 */ + float r; + /** 0-255 */ + float g; + /** 0-255 */ + float b; + /** 0-128 */ + float a; + }; + VECTOR rgba alignas(sizeof(float) * 4); + }; + + static void copy(Color* out, const float* in); + static inline void copy(Color* out, const Color& in) { copy(out, in.rgba); } + + /** Initialize Color without setting default values */ + Color() {} + + /** Initialize Color with provided values */ + Color(const float& r, const float& g = 255.0F, const float& b = 255.0F, + const float& a = 128.0F) + : r(r), g(g), b(b), a(a) {} + + /** Initialize Color with other Color values */ + Color(const Color& v) { copy(this, v); } + + /** Initialize Color with float[4] values */ + explicit Color(const float* v) { copy(this, v); } + + void operator=(const Color& v); + + inline void set(const Color& v) { copy(this, v.rgba); } + inline void set(const float* v) { copy(this, v); } + void set(const float& r, const float& g = 255.0F, const float& b = 255.0F, + const float& a = 128.0F); + + /** + * Update this vector by linear interpolation between two vertices + * @param v1 Before vertex + * @param v2 After vertex + * @param interp State of interpolation + */ + void lerp(const Color& v1, const Color& v2, const float& interp); + + /** + * Get vector by linear interpolation between two vertices + * @param v1 Before vertex + * @param v2 After vertex + * @param interp State of interpolation + */ + static Color getByLerp(const Color& v1, const Color& v2, const float& interp); + + void print() const; + void print(const char* name) const; + void print(const std::string& name) const { print(name.c_str()); } + std::string getPrint(const char* name = nullptr) const; + + private: + static void setLerp(Color* output, const Color& v1, const Color& v2, + const float& interp); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/renderer.hpp b/engine/inc/renderer/renderer.hpp new file mode 100644 index 0000000..ff5a796 --- /dev/null +++ b/engine/inc/renderer/renderer.hpp @@ -0,0 +1,47 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include "./core/renderer_core.hpp" +#include "./core/3d/camera_info_3d.hpp" +#include "./3d/renderer_3d.hpp" +#include "./2d/renderer_2d.hpp" + +namespace Tyra { + +class Renderer { + public: + Renderer(); + ~Renderer(); + + Renderer2D renderer2D; + Renderer3D renderer3D; + + RendererCore core; + + void init(); + + /** World background color */ + void setClearScreenColor(const Color& color) { + core.setClearScreenColor(color); + } + + void setFrameLimit(const bool& onoff) { core.setFrameLimit(onoff); } + + /** Update view frustum for frustum culling. */ + void beginFrame(const CameraInfo3D& cameraInfo); + + /** VSync and swap frame double buffer. */ + void endFrame(); +}; + +} // namespace Tyra diff --git a/engine/inc/renderer/renderer_settings.hpp b/engine/inc/renderer/renderer_settings.hpp new file mode 100644 index 0000000..f696329 --- /dev/null +++ b/engine/inc/renderer/renderer_settings.hpp @@ -0,0 +1,46 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include + +namespace Tyra { + +class RendererSettings { + public: + RendererSettings() + : width(640.0F), + height(448.0F), + near(0.1F), + far(4096.0F), + projectionScale(4096.0F), + aspectRatio(width / height) {} + ~RendererSettings(); + + const float& getWidth() const { return width; } + const float& getHeight() const { return height; } + const float& getNear() const { return near; } + const float& getFar() const { return far; } + const float& getProjectionScale() const { return projectionScale; } + const float& getAspectRatio() const { return aspectRatio; } + + static void copy(RendererSettings* out, const RendererSettings* in); + void set(const RendererSettings& v); + + void print() const; + std::string getPrint() const; + + private: + float width, height, near, far, projectionScale, aspectRatio; +}; + +} // namespace Tyra diff --git a/engine/inc/thread/threading.hpp b/engine/inc/thread/threading.hpp new file mode 100644 index 0000000..f5e0741 --- /dev/null +++ b/engine/inc/thread/threading.hpp @@ -0,0 +1,27 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include + +namespace Tyra { + +class Threading { + public: + Threading(); + ~Threading(); + + static void sleep(const u32& ms); + + static void switchThread(); +}; + +} // namespace Tyra diff --git a/engine/inc/time/timer.hpp b/engine/inc/time/timer.hpp new file mode 100644 index 0000000..c5d70ad --- /dev/null +++ b/engine/inc/time/timer.hpp @@ -0,0 +1,30 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include + +namespace Tyra { + +class Timer { + public: + Timer(); + ~Timer(); + + u32 getTimeDelta(); + inline void prime() { lastTime = *T3_COUNT; } + + private: + u32 lastTime, time, change; +}; + +} // namespace Tyra diff --git a/engine/src/audio/audio.cpp b/engine/src/audio/audio.cpp new file mode 100644 index 0000000..6338f44 --- /dev/null +++ b/engine/src/audio/audio.cpp @@ -0,0 +1,252 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020 - 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "thread/threading.hpp" +#include "audio/audio.hpp" + +extern void* _gp; + +void audioThread(Tyra::Audio* audio) { + while (true) audio->work(); +} + +namespace Tyra { + +const u16 Audio::chunkSize = 4 * 1024; +const u16 Audio::threadStackSize = 2 * 1024; + +Audio::Audio() { + chunkReadStatus = 0; + tyraVolume = 0; + audsrvVolume = 0; + + songLoaded = false; + songPlaying = false; + songInLoop = false; + songFinished = false; + + chunk = nullptr; + threadStack = nullptr; +} + +Audio::~Audio() { + if (chunk) delete[] chunk; + if (threadStack) delete[] threadStack; +} + +void Audio::init() { + chunk = static_cast(memalign(sizeof(char), chunkSize)); + threadStack = static_cast(memalign(sizeof(u8), threadStackSize)); + + initSema(); + initAUDSRV(); + setSongFormat(); + initThread(); +} + +void Audio::loadSong(const char* t_path) { + if (songLoaded) unloadSong(); + wav = fopen(t_path, "rb"); + TYRA_ASSERT(wav != NULL, "Failed to open wav file!"); + rewindSongToStart(); + songLoaded = true; + TYRA_LOG("Song loaded!"); +} + +void Audio::playSong() { + TYRA_ASSERT(songLoaded, "Cant play song because was not loaded!"); + if (songFinished) rewindSongToStart(); + tyraVolume = audsrvVolume; + audsrv_set_volume(tyraVolume); + songPlaying = true; +} + +void Audio::stopSong() { + tyraVolume = 0; + audsrv_set_volume(tyraVolume); + songPlaying = false; +} + +/** + * Initialize AUDSRV main and ADPCM module. + * Install AUDSRV callback. + */ +void Audio::initAUDSRV() { + TYRA_LOG("Initializing AUDSRV"); + + int ret = audsrv_init(); + TYRA_ASSERT(ret >= 0, + "AUDSRV returned error string: ", audsrv_get_error_string()); + + ret = audsrv_adpcm_init(); + TYRA_ASSERT(ret >= 0, + "AUDSRV returned error string: ", audsrv_get_error_string()); + + ret = audsrv_on_fillbuf(chunkSize, (audsrv_callback_t)iSignalSema, + (void*)fillbufferSema); + + TYRA_ASSERT(ret >= 0, + "AUDSRV returned error string:", audsrv_get_error_string()); + + TYRA_LOG("AUDSRV initialized!"); +} + +/** Initialize semaphore which will wait until chunk of the song is not + * finished. */ +void Audio::initSema() { + TYRA_LOG("Creating audio semaphore"); + sema.init_count = 0; + sema.max_count = 1; + sema.option = 0; + fillbufferSema = CreateSema(&sema); + TYRA_LOG("Audio semaphore created"); +} + +void Audio::initThread() { + thread.gp_reg = &_gp; + thread.func = reinterpret_cast(audioThread); + thread.stack = threadStack; + thread.stack_size = threadStackSize; + thread.initial_priority = 0x5; + threadId = CreateThread(&thread); + TYRA_ASSERT(threadId >= 0, "Create audio thread failed!"); + StartThread(threadId, this); +} + +/** + * Close file. + * Delete song path from memory. + */ +void Audio::unloadSong() { + songLoaded = false; + fclose(wav); +} + +/** Fseek on wav. */ +void Audio::rewindSongToStart() { + if (wav != NULL) fseek(wav, 0x30, SEEK_SET); + chunkReadStatus = 0; + songFinished = false; +} + +/** Set WAV format to 16bit, 22050Hz, stereo. */ +void Audio::setSongFormat() { + format.bits = 16; + format.freq = 22050; + format.channels = 2; + audsrv_set_format(&format); +} + +void Audio::setSongVolume(const u8& t_vol) { + audsrvVolume = t_vol; + if (songPlaying) tyraVolume = t_vol; + audsrv_set_volume(tyraVolume); +} + +void Audio::work() { + Threading::switchThread(); + + if (!songPlaying || !songLoaded) return; + if (songFinished) { + TYRA_LOG("Audio: Song finished. "); + if (songInLoop) { + TYRA_LOG("Running again."); + for (u32 i = 0; i < getSongListenersCount(); i++) + songListeners[i]->listener->onAudioFinish(); + rewindSongToStart(); + } else { + TYRA_LOG("Stopping song."); + stopSong(); + return; + } + } + + if (chunkReadStatus > 0) { + WaitSema(fillbufferSema); // wait until previous chunk wasn't finished + audsrv_play_audio(chunk, chunkReadStatus); + for (u32 i = 0; i < getSongListenersCount(); i++) + songListeners[i]->listener->onAudioTick(); + } + + chunkReadStatus = fread(chunk, 1, chunkSize, wav); + + if (chunkReadStatus < (s32)chunkSize) songFinished = true; +} + +u32 Audio::addSongListener(AudioListener* t_listener) { + AudioListenerRef* ref = new AudioListenerRef; + ref->id = rand() % 1000000; + ref->listener = t_listener; + songListeners.push_back(ref); + return ref->id; +} + +void Audio::removeSongListener(const u32& t_id) { + s32 index = -1; + for (u32 i = 0; i < songListeners.size(); i++) + if (songListeners[i]->id == t_id) { + index = i; + break; + } + TYRA_ASSERT(index != -1, + "Cant remove listener because given id was not found!"); + delete songListeners[index]; + songListeners.erase(songListeners.begin() + index); +} + +audsrv_adpcm_t* Audio::loadADPCM(const char* t_path) { + FILE* file = fopen(t_path, "rb"); + fseek(file, 0, SEEK_END); + u32 adpcmFileSize = ftell(file); + u8 data[adpcmFileSize]; + rewind(file); + fread(data, sizeof(u8), adpcmFileSize, file); + audsrv_adpcm_t* result = new audsrv_adpcm_t(); + result->size = 0; + result->buffer = 0; + result->loop = 0; + result->pitch = 0; + result->channels = 0; + if (audsrv_load_adpcm(result, data, adpcmFileSize)) { + TYRA_LOG("AUDSRV returned error string: ", audsrv_get_error_string()); + TYRA_TRAP("audsrv_load_adpcm() failed!"); + } + fclose(file); + return result; +} + +void Audio::playADPCM(audsrv_adpcm_t* t_adpcm) { + if (audsrv_play_adpcm(t_adpcm)) { + TYRA_LOG("AUDSRV returned error string: ", audsrv_get_error_string()); + TYRA_TRAP("audsrv_play_adpcm() failed!"); + } +} + +void Audio::playADPCM(audsrv_adpcm_t* t_adpcm, const s8& t_ch) { + if (audsrv_ch_play_adpcm(t_ch, t_adpcm)) { + TYRA_LOG("AUDSRV returned error string: ", audsrv_get_error_string()); + TYRA_TRAP("audsrv_ch_play_adpcm() failed!"); + } +} + +} // namespace Tyra diff --git a/engine/src/engine.cpp b/engine/src/engine.cpp new file mode 100644 index 0000000..185f461 --- /dev/null +++ b/engine/src/engine.cpp @@ -0,0 +1,39 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "engine.hpp" + +namespace Tyra { + +Engine::Engine() { + irx.loadDefaultDrivers(); + audio.init(); + pad.init(); + renderer.init(); + srand(time(nullptr)); +} + +Engine::~Engine() {} + +void Engine::run(Game* t_game) { + game = t_game; + game->init(); + while (true) { + realLoop(); + } +} + +void Engine::realLoop() { + pad.update(); + game->loop(); + info.update(); +} + +} // namespace Tyra diff --git a/engine/src/file/file_utils.cpp b/engine/src/file/file_utils.cpp new file mode 100644 index 0000000..1e8429d --- /dev/null +++ b/engine/src/file/file_utils.cpp @@ -0,0 +1,63 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020 - 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Wellington Carvalho +*/ + +#include "debug/debug.hpp" +#include +#include +#include +#include +#include +#include +#include +#include "file/file_utils.hpp" + +namespace Tyra { + +FileUtils::FileUtils() { + getcwd(cwd, sizeof(cwd)); + setPathInfo(cwd); +} + +FileUtils::~FileUtils() {} + +void FileUtils::setPathInfo(const char* path) { + char* ptr; + + strcpy(this->elfName, path); + strcpy(this->elfPath, path); + + ptr = strrchr(this->elfPath, '/'); + if (ptr == NULL) { + ptr = strrchr(this->elfPath, '\\'); + if (ptr == NULL) { + ptr = strrchr(this->elfPath, ':'); + if (ptr == NULL) { + TYRA_TRAP("Did not find path! PATH: ", path); + } + } + } + + ptr++; + *ptr = '\0'; +} + +std::string FileUtils::getCwd() { + std::string result; + char _cwd[NAME_MAX]; + getcwd(_cwd, sizeof(_cwd)); + result = _cwd; + return result; +} + +std::string FileUtils::fromCwd(const char* file) { + auto cwd = getCwd(); + return cwd + file; +} +} // namespace Tyra diff --git a/engine/src/info/info.cpp b/engine/src/info/info.cpp new file mode 100644 index 0000000..d08077b --- /dev/null +++ b/engine/src/info/info.cpp @@ -0,0 +1,39 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020-2022, tyra - https://github.com/h4570/tyrav2 +# Licenced under Apache License 2.0 +# Sandro Sobczyński +# Sandro Wellinator +*/ + +#include "info/info.hpp" + +namespace Tyra { + +Info::Info() { + fps = 0; + fpsDelayer = 0; +} + +Info::~Info() {} + +void Info::update() { + if (fpsDelayer++ >= 4) { + fps = calcFps(); + fpsDelayer = 0; + } + timer.prime(); +} + +float Info::calcFps() { + u32 timeDelta = timer.getTimeDelta(); + + if (timeDelta == 0) return -1.0F; + + return 15625.0F / (float)timeDelta; // PAL +} + +} // Namespace Tyra \ No newline at end of file diff --git a/engine/src/irx/bdm.irx-em b/engine/src/irx/bdm.irx-em new file mode 100644 index 0000000..2eb8b4f --- /dev/null +++ b/engine/src/irx/bdm.irx-em @@ -0,0 +1,2 @@ +$PS2SDK/iop/irx/bdm.irx +bdm_irx diff --git a/engine/src/irx/bdmfs_fatfs.irx-em b/engine/src/irx/bdmfs_fatfs.irx-em new file mode 100644 index 0000000..438f958 --- /dev/null +++ b/engine/src/irx/bdmfs_fatfs.irx-em @@ -0,0 +1,2 @@ +$PS2SDK/iop/irx/bdmfs_fatfs.irx +bdmfs_fatfs_irx diff --git a/engine/src/irx/irx_loader.cpp b/engine/src/irx/irx_loader.cpp new file mode 100644 index 0000000..dec6eb3 --- /dev/null +++ b/engine/src/irx/irx_loader.cpp @@ -0,0 +1,156 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020 - 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Wellington Carvalho +*/ + +#include "irx/irx_loader.hpp" +#include "debug/debug.hpp" +#include +#include +#include +#include +#include +#include + +// external IRX modules +extern u8 bdm_irx[]; +extern int size_bdm_irx; + +extern u8 bdmfs_fatfs_irx[]; +extern int size_bdmfs_fatfs_irx; + +extern u8 usbd_irx[]; +extern int size_usbd_irx; + +extern u8 usbmass_bd_irx[]; +extern int size_usbmass_bd_irx; + +namespace Tyra { + +IrxLoader::IrxLoader() { + SifInitRpc(0); + this->applyRpcPatches(); +} + +IrxLoader::~IrxLoader() {} + +void IrxLoader::loadDefaultDrivers() { + this->loadAudio(); + this->loadPad(); +} + +void IrxLoader::loadUSBDriver() { this->loadUsb(); } + +/** + * @brief Apply the SBV LMB patch to allow modules to be loaded from a buffer in + * EE RAM. + * + */ +int IrxLoader::applyRpcPatches() { + int ret; + TYRA_LOG("Applying SBV Patches"); + + ret = sbv_patch_enable_lmb(); + TYRA_ASSERT(ret >= 0, + "Failed to load Applying SBV Patches sbv_patch_enable_lmb"); + + ret = sbv_patch_disable_prefix_check(); + TYRA_ASSERT( + ret >= 0, + "Failed to load Applying SBV Patches sbv_patch_disable_prefix_check"); + + ret = sbv_patch_fileio(); + TYRA_ASSERT(ret >= 0, "Failed to load Applying SBV Patches sbv_patch_fileio"); + + TYRA_LOG("SBV Patches applyed "); + return ret; +} + +int IrxLoader::loadUsb() { + int ret; + TYRA_LOG("Loading USB modules"); + + // Load Block Device Manager (BDM) + SifExecModuleBuffer(&bdm_irx, size_bdm_irx, 0, NULL, &ret); + TYRA_ASSERT(ret >= 0, "Failed to load module: usbhdfsd"); + + // Load FATFS (mass:) driver + SifExecModuleBuffer(&bdmfs_fatfs_irx, size_bdmfs_fatfs_irx, 0, NULL, &ret); + TYRA_ASSERT(ret >= 0, "Failed to load module: usbd"); + + // Load USB Block Device drivers + SifExecModuleBuffer(&usbd_irx, size_usbd_irx, 0, NULL, &ret); + TYRA_ASSERT(ret >= 0, "Failed to load module: usbd"); + + SifExecModuleBuffer(&usbmass_bd_irx, size_usbmass_bd_irx, 0, NULL, &ret); + TYRA_ASSERT(ret >= 0, "Failed to load module: usbhdfsd"); + + this->waitUntilUsbDeviceIsReady(); + + TYRA_LOG("USB/MASS modules loaded!"); + + return ret; +} + +int IrxLoader::loadAudio() { + int ret; + + TYRA_LOG("Modules loading started (LIBSD, AUDSRV)"); + ret = SifLoadModule("rom0:LIBSD", 0, NULL); + TYRA_ASSERT(ret != -203, "LIBSD loading failed!"); + + ret = SifLoadModule("host:audsrv.irx", 0, NULL); + TYRA_ASSERT(ret != -203, "audsrv.irx loading failed!"); + TYRA_LOG("Audio modules loaded"); + + return ret; +} + +int IrxLoader::loadPad() { + int ret; + + TYRA_LOG("PAD Modules loading started (SIO2MAN, PADMAN)"); + + ret = SifLoadModule("rom0:SIO2MAN", 0, NULL); + TYRA_ASSERT(ret >= 0, + "SifLoadModule (SIO2MAN) failed! Returned value: ", ret); + + ret = SifLoadModule("rom0:PADMAN", 0, NULL); + TYRA_ASSERT(ret >= 0, "SifLoadModule (PADMAN) failed! Returned value: ", ret); + + TYRA_LOG("Pad modules loaded!"); + + return ret; +} + +void IrxLoader::delay(int count) { + int i; + int ret; + for (i = 0; i < count; i++) { + ret = 0x01000000; + while (ret--) asm("nop\nnop\nnop\nnop"); + } +} + +void IrxLoader::waitUntilUsbDeviceIsReady() { + struct stat buffer; + int ret = -1; + int retries = 50; + + delay(5); // some delay is required by usb mass storage driver + + while (ret != 0 && retries > 0) { + ret = stat("mass:/", &buffer); + /* Wait until the device is ready */ + nopdelay(); + + retries--; + } +} + +} // namespace Tyra diff --git a/engine/src/irx/usbd.irx-em b/engine/src/irx/usbd.irx-em new file mode 100644 index 0000000..0ce6689 --- /dev/null +++ b/engine/src/irx/usbd.irx-em @@ -0,0 +1,2 @@ +$PS2SDK/iop/irx/usbd.irx +usbd_irx diff --git a/engine/src/irx/usbmass_bd.irx-em b/engine/src/irx/usbmass_bd.irx-em new file mode 100644 index 0000000..a0d4467 --- /dev/null +++ b/engine/src/irx/usbmass_bd.irx-em @@ -0,0 +1,2 @@ +$PS2SDK/iop/irx/usbmass_bd.irx +usbmass_bd_irx diff --git a/engine/src/loaders/3d/builder/mesh_builder_data.cpp b/engine/src/loaders/3d/builder/mesh_builder_data.cpp new file mode 100644 index 0000000..ad70264 --- /dev/null +++ b/engine/src/loaders/3d/builder/mesh_builder_data.cpp @@ -0,0 +1,64 @@ + +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "loaders/3d/builder/mesh_builder_data.hpp" + +namespace Tyra { + +MeshBuilderData::MeshBuilderData() { + normalsEnabled = false; + textureCoordsEnabled = false; + manyColorsEnabled = false; +} + +MeshBuilderData::~MeshBuilderData() { + if (frames) { + for (u32 i = 0; i < framesCount; i++) { + delete frames[i]; + } + delete[] frames; + } + + if (materials) { + for (u32 i = 0; i < materialsCount; i++) { + delete materials[i]; + } + delete[] materials; + } +} + +void MeshBuilderData::allocate(const u32& framesCount, + const u32& materialsCount) { + allocateFrames(framesCount); + allocateMaterials(materialsCount); +} + +void MeshBuilderData::allocateFrames(const u32& count) { + this->framesCount = count; + + frames = new MeshBuilderFrameData*[count]; + + for (u32 i = 0; i < count; i++) { + frames[i] = new MeshBuilderFrameData(); + } +} + +void MeshBuilderData::allocateMaterials(const u32& count) { + this->materialsCount = count; + + materials = new MeshBuilderMaterialData*[count]; + + for (u32 i = 0; i < count; i++) { + materials[i] = new MeshBuilderMaterialData(); + } +} + +} // namespace Tyra diff --git a/engine/src/loaders/3d/builder/mesh_builder_frame_data.cpp b/engine/src/loaders/3d/builder/mesh_builder_frame_data.cpp new file mode 100644 index 0000000..c8f0b4e --- /dev/null +++ b/engine/src/loaders/3d/builder/mesh_builder_frame_data.cpp @@ -0,0 +1,49 @@ + +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "loaders/3d/builder/mesh_builder_frame_data.hpp" + +namespace Tyra { + +MeshBuilderFrameData::MeshBuilderFrameData() { + vertices = nullptr; + normals = nullptr; + textureCoords = nullptr; + colors = nullptr; + verticesCount = 0; + textureCoordsCount = 0; + normalsCount = 0; + colorsCount = 0; +} + +MeshBuilderFrameData::~MeshBuilderFrameData() {} + +void MeshBuilderFrameData::allocateTextureCoords(const u32& count) { + textureCoordsCount = count; + textureCoords = new Vec4[count]; +} + +void MeshBuilderFrameData::allocateVertices(const u32& count) { + verticesCount = count; + vertices = new Vec4[count]; +} + +void MeshBuilderFrameData::allocateNormals(const u32& count) { + normalsCount = count; + normals = new Vec4[count]; +} + +void MeshBuilderFrameData::allocateColors(const u32& count) { + colorsCount = count; + colors = new Color[count]; +} + +} // namespace Tyra diff --git a/engine/src/loaders/3d/builder/mesh_builder_material_data.cpp b/engine/src/loaders/3d/builder/mesh_builder_material_data.cpp new file mode 100644 index 0000000..712daec --- /dev/null +++ b/engine/src/loaders/3d/builder/mesh_builder_material_data.cpp @@ -0,0 +1,35 @@ + +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "loaders/3d/builder/mesh_builder_material_data.hpp" + +namespace Tyra { + +MeshBuilderMaterialData::MeshBuilderMaterialData() { + vertexFaces = nullptr; + textureCoordFaces = nullptr; + normalFaces = nullptr; + colorFaces = nullptr; + name = ""; + count = 0; +} + +MeshBuilderMaterialData::~MeshBuilderMaterialData() {} + +void MeshBuilderMaterialData::allocateFaces(const u32& t_count) { + vertexFaces = new u32[t_count]; + textureCoordFaces = new u32[t_count]; + normalFaces = new u32[t_count]; + colorFaces = new u32[t_count]; + count = t_count; +} + +} // namespace Tyra diff --git a/engine/src/loaders/3d/md2/md2_loader.cpp b/engine/src/loaders/3d/md2/md2_loader.cpp new file mode 100644 index 0000000..f5d796e --- /dev/null +++ b/engine/src/loaders/3d/md2/md2_loader.cpp @@ -0,0 +1,194 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include +#include "debug/debug.hpp" +#include "loaders/3d/builder/mesh_builder_data.hpp" +#include "loaders/3d/md2/anorms.hpp" +#include "loaders/loader.hpp" +#include "loaders/3d/md2/md2_loader.hpp" + +namespace Tyra { + +// magic number "IDP2" or 844121161 +#define MD2_IDENT (('2' << 24) + ('P' << 16) + ('D' << 8) + 'I') + +// model version +#define MD2_VERSION 8 + +typedef struct { + int ident; // magic number. must be equal to "IDP2" + int version; // md2 version. must be equal to 8 + + int skinwidth; // width of the texture + int skinheight; // height of the texture + int framesize; // size of one frame in bytes + + int num_skins; // number of textures + int num_xyz; // number of vertices + int num_st; // number of texture coordinates + int num_tris; // number of triangles + int num_glcmds; // number of opengl commands + int num_frames; // total number of frames + + int ofs_skins; // offset to skin names (64 bytes each) + int ofs_st; // offset to s-t texture coordinates + int ofs_tris; // offset to triangles + int ofs_frames; // offset to frame data + int ofs_glcmds; // offset to opengl commands + int ofs_end; // offset to end of file +} md2_t; + +typedef struct { + unsigned char v[3]; // compressed vertex (x, y, z) coordinates + unsigned char lightnormalindex; // index to a normal vector for the lighting +} mvertex_t; + +typedef struct { + float scale[3]; // scale values + float translate[3]; // translation vector + char name[16]; // frame name + mvertex_t verts[1]; // first vertex of this frame +} frame_t; + +typedef float vec3_t[3]; + +typedef struct { + s16 index_xyz[3]; // indexes to triangle's vertices + s16 index_st[3]; // indexes to vertices' texture coorinates +} triangle_t; + +typedef struct { + s16 s; + s16 t; +} texCoord_t; + +MD2Loader::MD2Loader() {} + +MD2Loader::~MD2Loader() {} + +MeshBuilderData* MD2Loader::load(const char* fullpath, const float& t_scale, + const u8& t_invertT) { + std::string path = fullpath; + TYRA_ASSERT(!path.empty(), "Provided path is empty!"); + + auto filename = getFilenameFromPath(path); + + FILE* file = fopen(fullpath, "rb"); + TYRA_ASSERT(file != NULL, "Failed to load: ", filename); + md2_t header; + + fread(reinterpret_cast(&header), sizeof(md2_t), 1, file); + + TYRA_ASSERT((header.ident == MD2_IDENT) && (header.version == MD2_VERSION), + "This MD2 file is not in correct format!"); + + u32 framesCount = header.num_frames; + u32 vertexCount = header.num_xyz; + u32 stsCount = header.num_st; + u32 trianglesCount = header.num_tris; + + auto framesBuffer = new char[framesCount * header.framesize]; + fseek(file, header.ofs_frames, SEEK_SET); + fread(framesBuffer, framesCount * header.framesize, 1, file); + + auto stsBuffer = new char[stsCount * sizeof(texCoord_t)]; + fseek(file, header.ofs_st, SEEK_SET); + fread(stsBuffer, stsCount * sizeof(texCoord_t), 1, file); + + auto trianglesBuffer = new char[trianglesCount * sizeof(triangle_t)]; + fseek(file, header.ofs_tris, SEEK_SET); + fread(trianglesBuffer, trianglesCount * sizeof(triangle_t), 1, file); + + fclose(file); + + auto result = new MeshBuilderData(); + result->allocate(framesCount, 1); + result->normalsEnabled = true; + result->textureCoordsEnabled = true; + result->manyColorsEnabled = false; + + frame_t* frame; + Vec4 temp(0.0F, 0.0F, 0.0F, 1.0F); + for (u32 j = 0; j < framesCount; j++) { + result->frames[j]->allocateVertices(vertexCount); + result->frames[j]->allocateNormals(vertexCount); + result->frames[j]->allocateTextureCoords(stsCount); + + result->materials[0]->allocateFaces(trianglesCount * 3); + result->materials[0]->name = getFilenameWithoutExtension(filename); + + frame = reinterpret_cast(&framesBuffer[header.framesize * j]); + + for (u32 i = 0; i < vertexCount; i++) { + temp.set( + ((frame->verts[i].v[0] * frame->scale[0]) + frame->translate[0]) * + t_scale, + ((frame->verts[i].v[1] * frame->scale[1]) + frame->translate[1]) * + t_scale, + ((frame->verts[i].v[2] * frame->scale[2]) + frame->translate[2]) * + t_scale); + + result->frames[j]->vertices[i].set(temp); + + temp.set(ANORMS[frame->verts[i].lightnormalindex][0], + ANORMS[frame->verts[i].lightnormalindex][1], + ANORMS[frame->verts[i].lightnormalindex][2]); + + result->frames[j]->normals[i].set(temp); + } + } + + texCoord_t* texCoord; + + TYRA_LOG("Skin width: ", header.skinwidth, + " Skin height: ", header.skinheight); + + for (u32 i = 0; i < stsCount; i++) { + texCoord = + reinterpret_cast(&stsBuffer[sizeof(texCoord_t) * i]); + + temp.set(static_cast(texCoord->s) / header.skinwidth, + static_cast(texCoord->t) / header.skinheight, 1.0F, 0.0F); + + if (t_invertT) temp.y = 1.0F - temp.y; + + for (u32 j = 0; j < framesCount; j++) + result->frames[j]->textureCoords[i].set(temp); + } + + triangle_t* triangle; + for (u32 i = 0; i < trianglesCount; i++) { + triangle = + reinterpret_cast(&trianglesBuffer[sizeof(triangle_t) * i]); + + for (u8 j = 0; j < 3; j++) { + for (u32 x = 0; x < framesCount; x++) { + result->materials[0]->vertexFaces[(i * 3) + j] = triangle->index_xyz[j]; + + result->materials[0]->textureCoordFaces[(i * 3) + j] = + triangle->index_st[j]; + + result->materials[0]->normalFaces[(i * 3) + j] = triangle->index_xyz[j]; + } + } + } + + TYRA_LOG("MD2 file \"", filename, "\" loaded!"); + + delete[] framesBuffer; + delete[] stsBuffer; + delete[] trianglesBuffer; + + return result; +} + +} // namespace Tyra diff --git a/engine/src/loaders/loader.cpp b/engine/src/loaders/loader.cpp new file mode 100644 index 0000000..bff63ff --- /dev/null +++ b/engine/src/loaders/loader.cpp @@ -0,0 +1,29 @@ + +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "loaders/loader.hpp" + +namespace Tyra { + +std::string Loader::getFilenameFromPath(const std::string& path) { + std::string filename = path.substr(path.find_last_of("/\\") + 1); + if (filename.size() == path.size()) { + filename = path.substr(path.find_last_of(":\\") + 1); + } + return filename; +} + +std::string Loader::getFilenameWithoutExtension(const std::string& filename) { + auto lastindex = filename.find_last_of("."); + return filename.substr(0, lastindex); +} + +} // namespace Tyra diff --git a/engine/src/loaders/texture/base/texture_builder_data.cpp b/engine/src/loaders/texture/base/texture_builder_data.cpp new file mode 100644 index 0000000..c8df01d --- /dev/null +++ b/engine/src/loaders/texture/base/texture_builder_data.cpp @@ -0,0 +1,32 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "loaders/texture/builder/texture_builder_data.hpp" +#include + +namespace Tyra { + +TextureBuilderData::TextureBuilderData() { + width = 0; + height = 0; + data = nullptr; + bpp = bpp32; + gsComponents = TEXTURE_COMPONENTS_RGBA; + + clut = nullptr; + clutWidth = 0; + clutHeight = 0; + clutBpp = bpp32; + clutGsComponents = TEXTURE_COMPONENTS_RGBA; +} + +TextureBuilderData::~TextureBuilderData() {} + +} // namespace Tyra diff --git a/engine/src/loaders/texture/base/texture_loader.cpp b/engine/src/loaders/texture/base/texture_loader.cpp new file mode 100644 index 0000000..fdd3397 --- /dev/null +++ b/engine/src/loaders/texture/base/texture_loader.cpp @@ -0,0 +1,35 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include "debug/debug.hpp" +#include "loaders/texture/base/texture_loader.hpp" + +namespace Tyra { + +u32 TextureLoader::getTextureSize(const u32& width, const u32& height, + const TextureBpp& bpp) { + switch (bpp) { + case bpp32: + return (width * height * 4); + case bpp24: + return (width * height * 3); + case bpp8: + return (width * height); + case bpp4: + return (width * height / 2); + default: + TYRA_TRAP("Unknown texture bpp"); + } + + return -1; +} + +} // namespace Tyra diff --git a/engine/src/loaders/texture/base/texture_loader_selector.cpp b/engine/src/loaders/texture/base/texture_loader_selector.cpp new file mode 100644 index 0000000..a0b1d25 --- /dev/null +++ b/engine/src/loaders/texture/base/texture_loader_selector.cpp @@ -0,0 +1,47 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include +#include "debug/debug.hpp" +#include "renderer/core/texture/models/texture.hpp" +#include "loaders/texture/base/texture_loader_selector.hpp" + +namespace Tyra { + +TextureLoaderSelector::TextureLoaderSelector() {} + +TextureLoaderSelector::~TextureLoaderSelector() {} + +// ---- +// Methods +// ---- + +TextureLoader& TextureLoaderSelector::getLoaderByFileName( + const char* fullpath) { + std::string path = fullpath; + std::string extension = path.substr(path.find_last_of(".") + 1); + return getLoaderByExtension(extension); +} + +TextureLoader& TextureLoaderSelector::getLoaderByExtension( + const std::string& extension) { + std::string extensionLower = extension; + std::transform(extensionLower.begin(), extensionLower.end(), + extensionLower.begin(), ::tolower); + if (extensionLower == "png") { + return pngLoader; + } else { + TYRA_TRAP("There is no texture loader for extension: ", extensionLower); + return pngLoader; + } +} + +} // namespace Tyra diff --git a/engine/src/loaders/texture/png_loader.cpp b/engine/src/loaders/texture/png_loader.cpp new file mode 100644 index 0000000..d697d61 --- /dev/null +++ b/engine/src/loaders/texture/png_loader.cpp @@ -0,0 +1,313 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include +#include +#include +#include +#include +#include +#include "loaders/texture/png_loader.hpp" + +namespace Tyra { + +PngLoader::PngLoader() {} + +PngLoader::~PngLoader() {} + +struct PngClut { + u8 r, g, b, a; +}; + +/** Based on GsKit texture loading - thank you guys! */ +TextureBuilderData* PngLoader::load(const char* fullPath) { + std::string path = fullPath; + TYRA_ASSERT(!path.empty(), "Provided path is empty!"); + + auto filename = getFilenameFromPath(path); + + FILE* file = fopen(fullPath, "rb"); + TYRA_ASSERT(file != nullptr, "Failed to load ", fullPath); + + png_structp pngPtr; + png_infop infoPtr; + png_uint_32 width, height; + png_bytep* rowPointers = nullptr; + + u32 sigRead = 0; + int bitDepth, colorType, interlaceType; + + pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) nullptr, + nullptr, nullptr); + TYRA_ASSERT(pngPtr, "PNG read struct init failed for: ", filename); + + infoPtr = png_create_info_struct(pngPtr); + TYRA_ASSERT(infoPtr, "PNG read struct init failed for: ", filename); + + TYRA_ASSERT(!setjmp(png_jmpbuf(pngPtr)), "PNG read error for: ", filename); + + png_init_io(pngPtr, file); + png_set_sig_bytes(pngPtr, sigRead); + png_read_info(pngPtr, infoPtr); + png_get_IHDR(pngPtr, infoPtr, &width, &height, &bitDepth, &colorType, + &interlaceType, nullptr, nullptr); + + if (bitDepth == 16) png_set_strip_16(pngPtr); + if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 4) png_set_expand(pngPtr); + + png_set_filler(pngPtr, 0xff, PNG_FILLER_AFTER); + + png_read_update_info(pngPtr, infoPtr); + + auto* result = new TextureBuilderData(); + result->width = width; + result->height = height; + result->name = filename; + + auto updatedColorType = png_get_color_type(pngPtr, infoPtr); + + if (updatedColorType == PNG_COLOR_TYPE_PALETTE) { + handlePalletized(result, pngPtr, infoPtr, rowPointers, bitDepth); + } else if (updatedColorType == PNG_COLOR_TYPE_RGB_ALPHA) { + handle32bpp(result, pngPtr, infoPtr, rowPointers); + } else if (updatedColorType == PNG_COLOR_TYPE_RGB) { + handle24bpp(result, pngPtr, infoPtr, rowPointers); + } else + TYRA_TRAP("This texture depth is not supported!"); + + png_read_end(pngPtr, nullptr); + png_destroy_read_struct(&pngPtr, &infoPtr, nullptr); + fclose(file); + + return result; +} + +void PngLoader::handle32bpp(TextureBuilderData* result, png_structp pngPtr, + png_infop infoPtr, png_bytep* rowPointers) { + int rowBytes = png_get_rowbytes(pngPtr, infoPtr); + + result->gsComponents = TEXTURE_COMPONENTS_RGBA; + result->bpp = bpp32; + result->data = static_cast(memalign( + 128, getTextureSize(result->width, result->height, result->bpp))); + + rowPointers = + static_cast(calloc(result->height, sizeof(png_bytep))); + + for (int row = 0; row < result->height; row++) + rowPointers[row] = static_cast(malloc(rowBytes)); + + png_read_image(pngPtr, rowPointers); + + struct Pixel { + u8 r, g, b, a; + }; + + struct Pixel* pixels = (struct Pixel*)result->data; + + int k = 0; + for (int i = 0; i < result->height; i++) { + for (int j = 0; j < result->width; j++) { + pixels[k].r = rowPointers[i][4 * j]; + pixels[k].g = rowPointers[i][4 * j + 1]; + pixels[k].b = rowPointers[i][4 * j + 2]; + pixels[k++].a = ((int)rowPointers[i][4 * j + 3] * 128 / 255); + } + } + + for (int row = 0; row < result->height; row++) free(rowPointers[row]); + + free(rowPointers); +} + +void PngLoader::handle24bpp(TextureBuilderData* result, png_structp pngPtr, + png_infop infoPtr, png_bytep* rowPointers) { + int rowBytes = png_get_rowbytes(pngPtr, infoPtr); + + result->gsComponents = TEXTURE_COMPONENTS_RGB; + result->bpp = bpp24; + result->data = static_cast(memalign( + 128, getTextureSize(result->width, result->height, result->bpp))); + + rowPointers = + static_cast(calloc(result->height, sizeof(png_bytep))); + + for (int row = 0; row < result->height; row++) + rowPointers[row] = static_cast(malloc(rowBytes)); + + png_read_image(pngPtr, rowPointers); + + struct Pixel3 { + u8 r, g, b; + }; + struct Pixel3* pixels = (struct Pixel3*)result->data; + + int k = 0; + for (int i = 0; i < result->height; i++) { + for (int j = 0; j < result->width; j++) { + pixels[k].r = rowPointers[i][4 * j]; + pixels[k].g = rowPointers[i][4 * j + 1]; + pixels[k++].b = rowPointers[i][4 * j + 2]; + } + } + + for (int row = 0; row < result->height; row++) free(rowPointers[row]); + + free(rowPointers); +} + +void PngLoader::handlePalletized(TextureBuilderData* result, png_structp pngPtr, + png_infop infoPtr, png_bytep* rowPointers, + const int& bitDepth) { + png_colorp palette = nullptr; + png_bytep trans = nullptr; + int numPallete = 0; + int numTrans = 0; + + png_get_PLTE(pngPtr, infoPtr, &palette, &numPallete); + png_get_tRNS(pngPtr, infoPtr, &trans, &numTrans, nullptr); + result->clutBpp = bpp32; + result->clutGsComponents = TEXTURE_COMPONENTS_RGBA; + + if (bitDepth == 4) { + handle4bppPalletized(result, pngPtr, infoPtr, rowPointers, palette, trans, + numPallete, numTrans); + } else if (bitDepth == 8) { + handle8bppPalletized(result, pngPtr, infoPtr, rowPointers, palette, trans, + numPallete, numTrans); + } else { + TYRA_TRAP("Only 4 and 8 bits palettes are supported"); + } +} + +void PngLoader::handle8bppPalletized(TextureBuilderData* result, + png_structp pngPtr, png_infop infoPtr, + png_bytep* rowPointers, png_colorp palette, + png_bytep trans, const int& numPallete, + const int& numTrans) { + int rowBytes = png_get_rowbytes(pngPtr, infoPtr); + result->bpp = bpp8; + result->clutWidth = 16; + result->clutHeight = 16; + + result->gsComponents = TEXTURE_COMPONENTS_RGBA; + result->data = static_cast(memalign( + 128, getTextureSize(result->width, result->height, result->bpp))); + + rowPointers = + static_cast(calloc(result->height, sizeof(png_bytep))); + + for (int row = 0; row < result->height; row++) + rowPointers[row] = static_cast(malloc(rowBytes)); + + png_read_image(pngPtr, rowPointers); + + result->clut = + static_cast(memalign(128, getTextureSize(16, 16, bpp32))); + memset(result->clut, 0, getTextureSize(16, 16, bpp32)); + + auto* pixel = static_cast(result->data); + struct PngClut* clut = (struct PngClut*)result->clut; + + for (int i = numPallete; i < 256; i++) { + memset(&clut[i], 0, sizeof(clut[i])); + } + + for (int i = 0; i < numPallete; i++) { + clut[i].r = palette[i].red; + clut[i].g = palette[i].green; + clut[i].b = palette[i].blue; + clut[i].a = 0x80; + } + + for (int i = 0; i < numTrans; i++) clut[i].a = trans[i] >> 1; + + // rotate clut + for (int i = 0; i < numPallete; i++) { + if ((i & 0x18) == 8) { + struct PngClut tmp = clut[i]; + clut[i] = clut[i + 8]; + clut[i + 8] = tmp; + } + } + + int k = 0; + for (int i = 0; i < result->height; i++) { + for (int j = 0; j < result->width; j++) { + memcpy(&pixel[k++], &rowPointers[i][1 * j], 1); + } + } + + for (int row = 0; row < result->height; row++) free(rowPointers[row]); + + free(rowPointers); +} + +void PngLoader::handle4bppPalletized(TextureBuilderData* result, + png_structp pngPtr, png_infop infoPtr, + png_bytep* rowPointers, png_colorp palette, + png_bytep trans, const int& numPallete, + const int& numTrans) { + int rowBytes = png_get_rowbytes(pngPtr, infoPtr); + result->bpp = bpp4; + result->clutWidth = 8; + result->clutHeight = 2; + result->gsComponents = TEXTURE_COMPONENTS_RGBA; + result->data = static_cast(memalign( + 128, getTextureSize(result->width, result->height, result->bpp))); + + rowPointers = + static_cast(calloc(result->height, sizeof(png_bytep))); + + for (int row = 0; row < result->height; row++) + rowPointers[row] = static_cast(malloc(rowBytes)); + + png_read_image(pngPtr, rowPointers); + + result->clut = + static_cast(memalign(128, getTextureSize(8, 2, bpp32))); + memset(result->clut, 0, getTextureSize(8, 2, bpp32)); + + auto* pixel = static_cast(result->data); + struct PngClut* clut = (struct PngClut*)result->clut; + + for (int i = numPallete; i < 16; i++) { + memset(&clut[i], 0, sizeof(clut[i])); + } + + for (int i = 0; i < numPallete; i++) { + clut[i].r = palette[i].red; + clut[i].g = palette[i].green; + clut[i].b = palette[i].blue; + clut[i].a = 0x80; + } + + for (int i = 0; i < numTrans; i++) clut[i].a = trans[i] >> 1; + + int k = 0; + for (int i = 0; i < result->height; i++) { + for (int j = 0; j < result->width / 2; j++) + memcpy(&pixel[k++], &rowPointers[i][1 * j], 1); + } + unsigned char* tmpdst = (unsigned char*)result->data; + unsigned char* tmpsrc = (unsigned char*)pixel; + + for (u32 byte = 0; + byte < getTextureSize(result->width, result->height, result->bpp); + byte++) + tmpdst[byte] = (tmpsrc[byte] << 4) | (tmpsrc[byte] >> 4); + + for (int row = 0; row < result->height; row++) free(rowPointers[row]); + + free(rowPointers); +} + +} // namespace Tyra diff --git a/engine/src/math/m4x4.cpp b/engine/src/math/m4x4.cpp new file mode 100644 index 0000000..bf61b01 --- /dev/null +++ b/engine/src/math/m4x4.cpp @@ -0,0 +1,390 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include +#include +#include "math/m4x4.hpp" + +namespace Tyra { + +VECTOR M4x4::upVec = {0.0F, 1.0F, 0.0F, 1.0F}; +VECTOR M4x4::viewVec = {0.0F, 0.0F, 0.0F, 1.0F}; + +M4x4::M4x4(const bool& t_identity) { + if (t_identity) identity(); +} + +void M4x4::copy(M4x4* out, const float* in) { + asm volatile( + "lqc2 $vf1, 0x00(%1) \n" + "lqc2 $vf2, 0x10(%1) \n" + "lqc2 $vf3, 0x20(%1) \n" + "lqc2 $vf4, 0x30(%1) \n" + "sqc2 $vf1, 0x00(%0) \n" + "sqc2 $vf2, 0x10(%0) \n" + "sqc2 $vf3, 0x20(%0) \n" + "sqc2 $vf4, 0x30(%0) \n" + : + : "r"(out->data), "r"(in)); +} + +void M4x4::operator=(const M4x4& v) { copy(this, v); } + +Vec4 M4x4::operator*(const Vec4& v) const { + Vec4 result; + asm volatile( + "lqc2 $vf1, 0x00(%2) \n" + "lqc2 $vf2, 0x10(%2) \n" + "lqc2 $vf3, 0x20(%2) \n" + "lqc2 $vf4, 0x30(%2) \n" + "lqc2 $vf5, 0x00(%1) \n" + "vmulaw $ACC, $vf4, $vf0\n" + "vmaddax $ACC, $vf1, $vf5\n" + "vmadday $ACC, $vf2, $vf5\n" + "vmaddz $vf6, $vf3, $vf5\n" + "sqc2 $vf6, 0x00(%0) \n" + : + : "r"(result.xyzw), "r"(v.xyzw), "r"(this->data)); + return result; +} + +void M4x4::set(const float& m11, const float& m12, const float& m13, + const float& m14, const float& m21, const float& m22, + const float& m23, const float& m24, const float& m31, + const float& m32, const float& m33, const float& m34, + const float& m41, const float& m42, const float& m43, + const float& m44) { + data[0] = m11; + data[1] = m12; + data[2] = m13; + data[3] = m14; + + data[4] = m21; + data[5] = m22; + data[6] = m23; + data[7] = m24; + + data[8] = m31; + data[9] = m32; + data[10] = m33; + data[11] = m34; + + data[12] = m41; + data[13] = m42; + data[14] = m43; + data[15] = m44; +} + +void M4x4::identity() { + asm volatile( + "vsub.xyzw $vf4, $vf0, $vf0 \n\t" + "vadd.w $vf4, $vf4, $vf0 \n\t" + "vmr32.xyzw $vf5, $vf4 \n\t" + "vmr32.xyzw $vf6, $vf5 \n\t" + "vmr32.xyzw $vf7, $vf6 \n\t" + "sqc2 $vf4, 0x30(%0) \n\t" + "sqc2 $vf5, 0x20(%0) \n\t" + "sqc2 $vf6, 0x10(%0) \n\t" + "sqc2 $vf7, 0x0(%0) \n\t" + : + : "r"(this->data)); +} + +M4x4 M4x4::perspective(const float& fov, const float& width, + const float& height, const float& projectionScale, + const float& aspectRatio, const float& near, + const float& far) { + M4x4 res; + float fovYdiv2 = Math::HALF_ANG2RAD * fov; + float cotFOV = 1.0F / (Math::sin(fovYdiv2) / Math::cos(fovYdiv2)); + float w = cotFOV * (width / projectionScale) / aspectRatio; + float h = cotFOV * (height / projectionScale); + + res.data[0] = w; + res.data[1] = 0.0F; + res.data[2] = 0.0F; + res.data[3] = 0.0F; + + res.data[4] = 0.0F; + res.data[5] = -h; + res.data[6] = 0.0F; + res.data[7] = 0.0F; + + res.data[8] = 0.0F; + res.data[9] = 0.0F; + res.data[10] = (far + near) / (far - near); + res.data[11] = -1.0F; + + res.data[12] = 0.0F; + res.data[13] = 0.0F; + res.data[14] = (2.0F * far * near) / (far - near); + res.data[15] = 0.0F; + return res; +} + +M4x4 M4x4::lookAt(const Vec4& position, const Vec4& target) { + M4x4 res(true); + lookAt(&res, position, target); + return res; +} + +void M4x4::lookAt(M4x4* res, const Vec4& position, const Vec4& target) { + float eye[4] alignas(sizeof(float) * 4) = {position.x, position.y, position.z, + 1.0F}; + + float obj[4] alignas(sizeof(float) * 4) = {target.x, target.y, target.z, + 1.0F}; + + asm volatile( + // eye + "lqc2 $vf4, 0x00(%2) \n\t" + // obj + "lqc2 $vf5, 0x00(%3) \n\t" + // view_vec = $vf7 + "vsub.xyz $vf7, $vf4, $vf5 \n\t" + "vmove.xyzw $vf6, $vf0 \n\t" + // $vf6 = { 0.0f, 1.0f, 0.0f, 1.0f } + "vaddw.y $vf6, $vf0, $vf0 \n\t" + "vopmula.xyz $ACC, $vf6, $vf7 \n\t" + // vec = $vf9 + "vopmsub.xyz $vf9, $vf7, $vf6 \n\t" + "vopmula.xyz $ACC, $vf7, $vf9 \n\t" + // up_vec = $vf8 + "vopmsub.xyz $vf8, $vf9, $vf7 \n\t" + // view_vec + "sqc2 $vf7, 0x00(%0) \n\t" + // up_vec + "sqc2 $vf6, 0x00(%1) \n\t" + : + : "r"(viewVec), "r"(upVec), "r"(eye), "r"(obj)); + + M4x4 temp = setCamera(eye, viewVec, upVec); + res->identity(); + + cross(res->data, res->data, temp.data); +} + +void M4x4::cross(float res[16], const float a[16], const float b[16]) { + asm volatile( + "lqc2 $vf1, 0x00(%1) \n\t" + "lqc2 $vf2, 0x10(%1) \n\t" + "lqc2 $vf3, 0x20(%1) \n\t" + "lqc2 $vf4, 0x30(%1) \n\t" + "lqc2 $vf5, 0x00(%2) \n\t" + "lqc2 $vf6, 0x10(%2) \n\t" + "lqc2 $vf7, 0x20(%2) \n\t" + "lqc2 $vf8, 0x30(%2) \n\t" + "vmulax.xyzw $ACC, $vf5, $vf1 \n\t" + "vmadday.xyzw $ACC, $vf6, $vf1 \n\t" + "vmaddaz.xyzw $ACC, $vf7, $vf1 \n\t" + "vmaddw.xyzw $vf1, $vf8, $vf1 \n\t" + "vmulax.xyzw $ACC, $vf5, $vf2 \n\t" + "vmadday.xyzw $ACC, $vf6, $vf2 \n\t" + "vmaddaz.xyzw $ACC, $vf7, $vf2 \n\t" + "vmaddw.xyzw $vf2, $vf8, $vf2 \n\t" + "vmulax.xyzw $ACC, $vf5, $vf3 \n\t" + "vmadday.xyzw $ACC, $vf6, $vf3 \n\t" + "vmaddaz.xyzw $ACC, $vf7, $vf3 \n\t" + "vmaddw.xyzw $vf3, $vf8, $vf3 \n\t" + "vmulax.xyzw $ACC, $vf5, $vf4 \n\t" + "vmadday.xyzw $ACC, $vf6, $vf4 \n\t" + "vmaddaz.xyzw $ACC, $vf7, $vf4 \n\t" + "vmaddw.xyzw $vf4, $vf8, $vf4 \n\t" + "sqc2 $vf1, 0x00(%0) \n\t" + "sqc2 $vf2, 0x10(%0) \n\t" + "sqc2 $vf3, 0x20(%0) \n\t" + "sqc2 $vf4, 0x30(%0) \n\t" + : + : "r"(res), "r"(b), "r"(a) + : "memory"); +} + +void M4x4::rotationX(const float& v) { + float c = Math::cos(v); + float s = Math::sin(v); + this->data[5] = c; // 1,1 + this->data[6] = s; // 1,2 + this->data[9] = -s; // 2,1 + this->data[10] = c; // 2,2 +} + +void M4x4::rotationY(const float& v) { + float c = Math::cos(v); + float s = Math::sin(v); + this->data[0] = c; // 0,0 + this->data[2] = -s; // 0,3 + this->data[8] = s; // 2,0 + this->data[10] = c; // 2,2 +} + +void M4x4::rotationZ(const float& v) { + float c = Math::cos(v); + float s = Math::sin(v); + this->data[0] = c; // 0,0 + this->data[1] = s; // 0,1 + this->data[4] = -s; // 1,0 + this->data[5] = c; // 1,1 +} + +void M4x4::rotationByAngle(const float& angle, const Vec4& axis) { + Vec4 localAxis = Vec4(axis); + localAxis.normalize(); + float x = localAxis.x; + float y = localAxis.y; + float z = localAxis.z; + + float c = Math::cos(angle); + float s = Math::sin(angle); + + this->data[0] = x * x * (1 - c) + c; + this->data[1] = y * x * (1 - c) + z * s; + this->data[2] = x * z * (1 - c) - y * s; + this->data[3] = 0.0F; + + this->data[4] = x * y * (1 - c) - z * s; + this->data[5] = y * y * (1 - c) + c; + this->data[6] = y * z * (1 - c) + x * s; + this->data[7] = 0.0F; + + this->data[8] = x * z * (1 - c) + y * s; + this->data[9] = y * z * (1 - c) - x * s; + this->data[10] = z * z * (1 - c) + c; + this->data[11] = 0.0F; + + this->data[12] = 0.0F; + this->data[13] = 0.0F; + this->data[14] = 0.0F; + this->data[15] = 1.0F; +} + +void M4x4::translationX(const float& val) { + this->data[12] = val; // 3,0 +} + +void M4x4::translationY(const float& val) { + this->data[13] = val; // 3,1 +} + +void M4x4::translationZ(const float& val) { + this->data[14] = val; // 3,2 +} + +void M4x4::setScale(const Vec4& val) { + this->data[0] = val.x; + this->data[5] = val.y; + this->data[10] = val.z; + this->data[15] = 1.0F; +} + +M4x4 M4x4::setCamera(const float pos[4], const float vz[4], const float vy[4]) { + M4x4 res; + // M4x4 $vf4, $vf5, $vf6, $vf7 + // pos $vf8 + // vz $vf9 + // vy $vf10 + // vtmp $vf11 + asm volatile( + "lqc2 $vf9, 0x00(%2) \n\t" + // mtmp.unit() + "lqc2 $vf10, 0x00(%3) \n\t" + // mtmp[1][PW] = 0.0F + "vsub.w $vf5, $vf0, $vf0 \n\t" + // vtmp.outerProduct(vy, vz); + "vopmula.xyz $ACC, $vf10, $vf9 \n\t" + "vopmsub.xyz $vf11, $vf9, $vf10 \n\t" + // mtmp[0] = vtmp.normalize(); + "vmul.xyz $vf12, $vf11, $vf11 \n\t" + "vaddy.x $vf12, $vf12, $vf12 \n\t" + "vaddz.x $vf12, $vf12, $vf12 \n\t" + "vrsqrt $Q, $vf0w, $vf12x \n\t" + "vsub.xyzw $vf4, $vf0, $vf0 \n\t" + "vwaitq \n\t" + "vmulq.xyz $vf4, $vf11, $Q \n\t" + // mtmp[2] = vz.normalize(); + "vmul.xyz $vf12, $vf9, $vf9 \n\t" + "vaddy.x $vf12, $vf12, $vf12 \n\t" + "vaddz.x $vf12, $vf12, $vf12 \n\t" + "vrsqrt $Q, $vf0w, $vf12x \n\t" + "vsub.xyzw $vf6, $vf0, $vf0 \n\t" + "vwaitq \n\t" + "vmulq.xyz $vf6, $vf9, $Q \n\t" + // mtmp[1].outerProduct(mtmp[2], mtmp[0]); + "vopmula.xyz $ACC, $vf6, $vf4 \n\t" + "vopmsub.xyz $vf5, $vf4, $vf6 \n\t" + // mtmp.transpose(pos); + "lqc2 $vf7, 0x00(%1) \n\t" + // m = mtmp.inverse(); + "qmfc2.ni $11, $vf0 \n\t" + "qmfc2.ni $8, $vf4 \n\t" + "qmfc2.ni $9, $vf5 \n\t" + "qmfc2.ni $10, $vf6 \n\t" + + "pextlw $12, $9, $8 \n\t" + "pextuw $13, $9, $8 \n\t" + "pextlw $14, $11, $10 \n\t" + "pextuw $15, $11, $10 \n\t" + "pcpyld $8, $14, $12 \n\t" + "pcpyud $9, $12, $14 \n\t" + "pcpyld $10, $15, $13 \n\t" + + "qmtc2.ni $8, $vf16 \n\t" + "qmtc2.ni $9, $vf17 \n\t" + "qmtc2.ni $10, $vf18 \n\t" + "vmulax.xyz $ACC, $vf16, $vf7 \n\t" + "vmadday.xyz $ACC, $vf17, $vf7 \n\t" + "vmaddz.xyz $vf5, $vf18, $vf7 \n\t" + "vsub.xyzw $vf5, $vf0, $vf5 \n\t" + + "sq $8, 0x00(%0) \n\t" + "sq $9, 0x10(%0) \n\t" + "sq $10, 0x20(%0) \n\t" + "sqc2 $vf5, 0x30(%0) \n\t" + : + : "r"(res.data), "r"(pos), "r"(vz), "r"(vy)); + return res; +} + +void M4x4::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void M4x4::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string M4x4::getPrint(const char* name) const { + std::stringstream res; + if (name) { + res << name << "("; + } else { + res << "M4x4("; + } + res << std::fixed << std::setprecision(2); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + auto index = (i * 4) + j; + res << data[index]; + if (index != 15) { + res << ", "; + } + } + + if (i != 3) { + res << std::endl; + } + } + res << ")"; + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/math/math.cpp b/engine/src/math/math.cpp new file mode 100644 index 0000000..4efcb83 --- /dev/null +++ b/engine/src/math/math.cpp @@ -0,0 +1,161 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#ifdef __INTELLISENSE__ +#pragma diag_suppress 1118 +#endif + +#include "math/math.hpp" + +namespace Tyra { + +float Math::cos(float x) { + float r; + asm volatile( + "lui $9, 0x3f00 \n\t" + ".set noreorder \n\t" + ".align 3 \n\t" + "abs.s %0, %1 \n\t" + "lui $8, 0xbe22 \n\t" + "mtc1 $9, $f1 \n\t" + "ori $8, $8, 0xf983 \n\t" + "mtc1 $8, $f8 \n\t" + "lui $9, 0x4b00 \n\t" + "mtc1 $9, $f3 \n\t" + "lui $8, 0x3f80 \n\t" + "mtc1 $8, $f2 \n\t" + "mula.s %0, $f8 \n\t" + "msuba.s $f3, $f2 \n\t" + "madda.s $f3, $f2 \n\t" + "lui $8, 0x40c9 \n\t" + "msuba.s %0, $f8 \n\t" + "ori $8, 0x0fdb \n\t" + "msub.s %0, $f1, $f2 \n\t" + "lui $9, 0xc225 \n\t" + "abs.s %0, %0 \n\t" + "lui $10, 0x3e80 \n\t" + "mtc1 $10, $f7 \n\t" + "ori $9, 0x5de1 \n\t" + "sub.s %0, %0, $f7 \n\t" + "lui $10, 0x42a3 \n\t" + "mtc1 $8, $f3 \n\t" + "ori $10, 0x3458 \n\t" + "mtc1 $9, $f4 \n\t" + "lui $8, 0xc299 \n\t" + "mtc1 $10, $f5 \n\t" + "ori $8, 0x2663 \n\t" + "mul.s $f8, %0, %0 \n\t" + "lui $9, 0x421e \n\t" + "mtc1 $8, $f6 \n\t" + "ori $9, 0xd7bb \n\t" + "mtc1 $9, $f7 \n\t" + "nop \n\t" + "mul.s $f1, %0, $f8 \n\t" + "mul.s $f9, $f8, $f8 \n\t" + "mula.s $f3, %0 \n\t" + "mul.s $f2, $f1, $f8 \n\t" + "madda.s $f4, $f1 \n\t" + "mul.s $f1, $f1, $f9 \n\t" + "mul.s %0, $f2, $f9 \n\t" + "madda.s $f5, $f2 \n\t" + "madda.s $f6, $f1 \n\t" + "madd.s %0, $f7, %0 \n\t" + ".set reorder \n\t" + : "=&f"(r) + : "f"(x) + : "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$8", + "$9", "$10"); + return r; +} + +float Math::invSqrt(float x) { return 1.0F / sqrt(x); } + +float Math::asin(float x) { + float r; + asm volatile( + "lui $9, 0x3f00 \n\t" + ".set noreorder \n\t" + ".align 3 \n\t" + "abs.s %0, %1 \n\t" + "lui $8, 0xbe22 \n\t" + "mtc1 $9, $f1 \n\t" + "ori $8, $8, 0xf983 \n\t" + "mtc1 $8, $f8 \n\t" + "lui $9, 0x4b00 \n\t" + "mtc1 $9, $f3 \n\t" + "lui $8, 0x3f80 \n\t" + "mtc1 $8, $f2 \n\t" + "mula.s %0, $f8 \n\t" + "msuba.s $f3, $f2 \n\t" + "madda.s $f3, $f2 \n\t" + "lui $8, 0x40c9 \n\t" + "msuba.s %0, $f8 \n\t" + "ori $8, 0x0fdb \n\t" + "msub.s %0, $f1, $f2 \n\t" + "lui $9, 0xc225 \n\t" + "abs.s %0, %0 \n\t" + "lui $10, 0x3e80 \n\t" + "mtc1 $10, $f7 \n\t" + "ori $9, 0x5de1 \n\t" + "sub.s %0, %0, $f7 \n\t" + "lui $10, 0x42a3 \n\t" + "mtc1 $8, $f3 \n\t" + "ori $10, 0x3458 \n\t" + "mtc1 $9, $f4 \n\t" + "lui $8, 0xc299 \n\t" + "mtc1 $10, $f5 \n\t" + "ori $8, 0x2663 \n\t" + "mul.s $f8, %0, %0 \n\t" + "lui $9, 0x421e \n\t" + "mtc1 $8, $f6 \n\t" + "ori $9, 0xd7bb \n\t" + "mtc1 $9, $f7 \n\t" + "nop \n\t" + "mul.s $f1, %0, $f8 \n\t" + "mul.s $f9, $f8, $f8 \n\t" + "mula.s $f3, %0 \n\t" + "mul.s $f2, $f1, $f8 \n\t" + "madda.s $f4, $f1 \n\t" + "mul.s $f1, $f1, $f9 \n\t" + "mul.s %0, $f2, $f9 \n\t" + "madda.s $f5, $f2 \n\t" + "madda.s $f6, $f1 \n\t" + "madd.s %0, $f7, %0 \n\t" + ".set reorder \n\t" + : "=&f"(r) + : "f"(x) + : "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$8", + "$9", "$10"); + return r; +} + +float Math::mod(float x, float y) { + /* + * Portable fmod(x,y) implementation for systems + * that don't have it. Adapted from code found here: + * http://www.opensource.apple.com/source/python/python-3/python/Python/fmod.c + */ + float i, f; + + if (fabs(y) < 0.00001F) { + return 0.0F; + } + + i = floorf(x / y); + f = x - i * y; + + if ((x < 0.0f) != (y < 0.0f)) { + f = f - y; + } + + return f; +} + +} // namespace Tyra diff --git a/engine/src/math/plane.cpp b/engine/src/math/plane.cpp new file mode 100644 index 0000000..d0b97d8 --- /dev/null +++ b/engine/src/math/plane.cpp @@ -0,0 +1,67 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include "math/vec4.hpp" +#include "math/plane.hpp" + +namespace Tyra { +Plane::Plane() { this->distance = 0; } + +/** Create by specyfying 3 points. + * This function assumes that the points + * are given in counter clockwise order + */ +Plane::Plane(const Vec4& a, const Vec4& b, const Vec4& c) { + this->update(a, b, c); +} + +Plane::~Plane() {} + +// ---- +// Methods +// ---- + +/** Set plane by specyfying 3 points. + * This function assumes that the points + * are given in counter clockwise order + */ +void Plane::update(const Vec4& a, const Vec4& b, const Vec4& c) { + Vec4 aux1 = a - b; + Vec4 aux2 = c - b; + this->normal = aux2.cross(aux1); + this->normal.normalize(); + this->distance = -this->normal.innerProduct(b); +} + +void Plane::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void Plane::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string Plane::getPrint(const char* name) const { + std::stringstream res; + if (name) { + res << name << "("; + } else { + res << "Plane("; + } + + res << std::fixed << std::setprecision(4); + res << "distance: " << distance << ", " << normal.getPrint("normal") << ")"; + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/math/vec2.cpp b/engine/src/math/vec2.cpp new file mode 100644 index 0000000..68613dc --- /dev/null +++ b/engine/src/math/vec2.cpp @@ -0,0 +1,78 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "math/vec2.hpp" + +namespace Tyra { + +Vec2::Vec2(const float& t_x, const float& t_y) { + x = t_x; + y = t_y; +} + +Vec2::Vec2(const Vec2& v) { + x = v.x; + y = v.y; +} + +Vec2::Vec2() { + x = 0; + y = 0; +} + +Vec2::~Vec2() {} + +void Vec2::set(const float& t_x, const float& t_y) { + x = t_x; + y = t_y; +} + +void Vec2::set(const Vec2& v) { + x = v.x; + y = v.y; +} + +void Vec2::rotate(const float& t_angle, const float& t_x, const float& t_y) { + float s = Math::sin(t_angle); + float c = Math::cos(t_angle); + + x -= t_x; + y -= t_y; + + float xnew = x * c - y * s; + float ynew = x * s + y * c; + + x = xnew + t_x; + y = ynew + t_y; +} + +void Vec2::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void Vec2::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string Vec2::getPrint(const char* name) const { + std::stringstream res; + if (name) { + res << name << "("; + } else { + res << "Vec2("; + } + res << std::fixed << std::setprecision(4); + res << x << ", " << y << ")"; + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/math/vec4.cpp b/engine/src/math/vec4.cpp new file mode 100644 index 0000000..d53320a --- /dev/null +++ b/engine/src/math/vec4.cpp @@ -0,0 +1,299 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "math/vec4.hpp" + +namespace Tyra { + +void Vec4::set(const float& t_x, const float& t_y, const float& t_z, + const float& t_w) { + x = t_x; + y = t_y; + z = t_z; + w = t_w; +} + +Vec4 Vec4::operator+(const Vec4& v) const { + Vec4 res; + asm volatile( + "lqc2 $vf4, 0x0(%1) \n\t" + "lqc2 $vf5, 0x0(%2) \n\t" + "vadd.xyz $vf6, $vf4, $vf5 \n\t" + "sqc2 $vf6, 0x0(%0) \n\t" + : + : "r"(res.xyzw), "r"(this->xyzw), "r"(v.xyzw)); + return res; +} + +Vec4 Vec4::operator-(const Vec4& v) const { + Vec4 res; + asm volatile( + "lqc2 $vf4, 0x0(%1) \n\t" + "lqc2 $vf5, 0x0(%2) \n\t" + "vsub.xyz $vf6, $vf4, $vf5 \n\t" + "sqc2 $vf6, 0x0(%0) \n\t" + : + : "r"(res.xyzw), "r"(this->xyzw), "r"(v.xyzw)); + return res; +} + +Vec4 Vec4::operator*(const Vec4& v) const { + Vec4 res; + asm volatile( + "lqc2 $vf4, 0x0(%1) \n\t" + "lqc2 $vf5, 0x0(%2) \n\t" + "vmul.xyzw $vf6, $vf4, $vf5 \n\t" + "sqc2 $vf6, 0x0(%0) \n\t" + : + : "r"(res.xyzw), "r"(this->xyzw), "r"(v.xyzw)); + return res; +} + +Vec4 Vec4::operator*(const float& v) const { + Vec4 res; + asm volatile( + "lqc2 $vf4, 0x0(%1) \n\t" + "mfc1 $8, %2 \n\t" + "qmtc2 $8, $vf5 \n\t" + "vmulx.xyz $vf6, $vf4, $vf5 \n\t" + "sqc2 $vf6, 0x0(%0) \n\t" + : + : "r"(res.xyzw), "r"(this->xyzw), "f"(v)); + res.w = w; // Comment below. TODO: fix it in asm + return res; +} + +Vec4 Vec4::operator/(const float& v) const { + return Vec4(x / v, y / v, z / v, w); +} + +void Vec4::operator+=(const Vec4& v) { + asm volatile( + "lqc2 $vf4, 0x0(%0) \n\t" + "lqc2 $vf5, 0x0(%1) \n\t" + "vadd.xyz $vf4, $vf4, $vf5 \n\t" + "sqc2 $vf4, 0x0(%0) \n\t" + : + : "r"(this->xyzw), "r"(v.xyzw)); +} + +void Vec4::operator*=(const Vec4& v) { + asm volatile( + "lqc2 $vf4, 0x0(%0) \n\t" + "lqc2 $vf5, 0x0(%1) \n\t" + "vmul.xyzw $vf6, $vf4, $vf5 \n\t" + "sqc2 $vf6, 0x0(%0) \n\t" + : + : "r"(this->xyzw), "r"(v.xyzw)); +} + +void Vec4::operator*=(const float& v) { + // Hmm... we don't want to modify W in most of the cases + // It should be touched only during rendering process + auto tempW = w; + + asm volatile( + "lqc2 $vf4, 0x0(%0) \n\t" + "mfc1 $8, %1 \n\t" + "qmtc2 $8, $vf5 \n\t" + "vmulx.xyz $vf4, $vf4, $vf5 \n\t" + "sqc2 $vf4, 0x0(%0) \n\t" + : + : "r"(this->xyzw), "f"(v)); + + w = tempW; +} + +void Vec4::operator/=(const float& v) { + x /= v; + y /= v; + z /= v; +} + +void Vec4::operator=(const Vec4& v) { copy(this, v); } + +Vec4 Vec4::operator-(void) const { return Vec4(-x, -y, -z); } + +void Vec4::copy(Vec4* out, const float* in) { + asm volatile( + "lqc2 $vf1, 0x00(%1) \n" + "sqc2 $vf1, 0x00(%0) \n" + : + : "r"(out->xyzw), "r"(in)); +} + +Vec4 Vec4::cross(const Vec4& v) const { + Vec4 res; + asm volatile( + "lqc2 $vf4, 0x0(%1) \n\t" + "lqc2 $vf5, 0x0(%2) \n\t" + "vopmula.xyz $ACC, $vf4, $vf5 \n\t" + "vopmsub.xyz $vf8, $vf5, $vf4 \n\t" + "vsub.w $vf8, $vf0, $vf0 \n\t" + "sqc2 $vf8, 0x0(%0) \n\t" + : + : "r"(res.xyzw), "r"(this->xyzw), "r"(v.xyzw)); + return res; +} + +void Vec4::rotateZ(const int& angle) { + auto s = Math::sin(angle); + auto c = Math::cos(angle); + x = x * c - y * s; + y = x * s + y * c; +} + +int Vec4::getRelativeCosBetween(const Vec4& v) const { + return dot3(v) / (length() * v.length()); +} +int Vec4::getRelativeAngleBetween(const Vec4& v) const { + return acos(getRelativeCosBetween(v)); +} + +float Vec4::innerProduct(const Vec4& v) const { + float result; + asm volatile( + "lqc2 $vf4, 0x0(%1) \n\t" + "lqc2 $vf5, 0x0(%2) \n\t" + "vmul.xyz $vf6, $vf4, $vf5 \n\t" + "vaddy.x $vf6, $vf6, $vf6 \n\t" + "vaddz.x $vf6, $vf6, $vf6 \n\t" + "qmfc2 $2, $vf6 \n\t" + "mtc1 $2, %0 \n\t" + : "=f"(result) + : "r"(this->xyzw), "r"(v.xyzw)); + return result; +} + +float Vec4::length() const { + float result; + asm volatile( + "lqc2 $vf4, 0x0(%1) \n\t" + "vmul.xyz $vf5, $vf4, $vf4 \n\t" + "vaddy.x $vf5, $vf5, $vf5 \n\t" + "vaddz.x $vf5, $vf5, $vf5 \n\t" + "vsqrt $Q , $vf5x \n\t" + "vwaitq \n\t" + "vaddq.x $vf8, $vf0, $Q \n\t" + "qmfc2 $2, $vf8 \n\t" + "mtc1 $2, %0 \n\t" + : "=f"(result) + : "r"(this->xyzw)); + return result; +} + +void Vec4::normalize() { + asm volatile( + "lqc2 $vf4, 0x0(%0) \n\t" + "vmul.xyz $vf5, $vf4, $vf4 \n\t" + "vaddy.x $vf5, $vf5, $vf5 \n\t" + "vaddz.x $vf5, $vf5, $vf5 \n\t" + "vrsqrt $Q, $vf0w, $vf5x \n\t" + "vwaitq \n\t" + "vsub.xyz $vf6, $vf0, $vf0 \n\t" + "vaddw.xyz $vf6, $vf6, $vf4 \n\t" + "vwaitq \n\t" + "vmulq.xyz $vf6, $vf4, $Q \n\t" + "sqc2 $vf6, 0x0(%0) \n\t" + : + : "r"(this->xyzw)); +} + +float Vec4::distanceTo(const Vec4& v) const { + float result; + asm volatile( + "lqc2 $vf4, 0x0(%1) \n\t" + "lqc2 $vf5, 0x0(%2) \n\t" + "vsub.xyz $vf6, $vf4, $vf5 \n\t" + "vmul.xyz $vf7, $vf6, $vf6 \n\t" + "vaddy.x $vf7, $vf7, $vf7 \n\t" + "vaddz.x $vf7, $vf7, $vf7 \n\t" + "vsqrt $Q , $vf7x \n\t" + "vwaitq \n\t" + "vaddq.x $vf8, $vf0, $Q \n\t" + "qmfc2 $2, $vf8 \n\t" + "mtc1 $2, %0 \n\t" + : "=f"(result) + : "r"(this->xyzw), "r"(v.xyzw)); + return result; +} + +u8 Vec4::shouldBeBackfaceCulled(const Vec4* cameraPos, const Vec4* v0, + const Vec4* v1, const Vec4* v2) { + float dot; + asm volatile( + "lqc2 $vf4, 0x0(%1) \n\t" // $vf4 = cameraPos + "lqc2 $vf5, 0x0(%2) \n\t" // $vf5 = v0 + "lqc2 $vf6, 0x0(%3) \n\t" // $vf6 = v1 + "lqc2 $vf7, 0x0(%4) \n\t" // $vf7 = v2 + "vsub.xyz $vf8, $vf7, $vf5 \n\t" // $vf8 = $vf7(v2) - $vf5(v0) + "vsub.xyz $vf9, $vf6, $vf5 \n\t" // $vf9 = $vf6(v1) - $vf5(v0) + "vopmula.xyz $ACC, $vf8, $vf9 \n\t" // $vf6 = cross($vf8, $vf9) + "vopmsub.xyz $vf6, $vf9, $vf8 \n\t" + "vsub.w $vf6, $vf6, $vf6 \n\t" + "vsub.xyz $vf7, $vf5, $vf4 \n\t" // $vf7 = $vf5(v0) - $vf4(cameraPos) + "vmul.xyz $vf5, $vf7, $vf6 \n\t" // $vf5 = dot($vf7, $vf6) + "vaddy.x $vf5, $vf5, $vf5 \n\t" + "vaddz.x $vf5, $vf5, $vf5 \n\t" + "qmfc2 $2, $vf5 \n\t" // store result on `dot` variable + "mtc1 $2, %0 \n\t" + : "=f"(dot) + : "r"(cameraPos->xyzw), "r"(v0->xyzw), "r"(v1->xyzw), "r"(v2->xyzw)); + return dot <= 0.0F; +} + +void Vec4::lerp(const Vec4& v1, const Vec4& v2, const float& interp) { + setLerp(this, v1, v2, interp); +} + +Vec4 Vec4::getByLerp(const Vec4& v1, const Vec4& v2, const float& interp) { + Vec4 result; + setLerp(&result, v1, v2, interp); + return result; +} + +void Vec4::setLerp(Vec4* output, const Vec4& v1, const Vec4& v2, + const float& interp) { + asm volatile( + "lqc2 $vf4, 0x0(%1) \n\t" // $vf4 = v1 + "lqc2 $vf5, 0x0(%2) \n\t" // $vf5 = v2 + "mfc1 $8, %3 \n\t" // $vf6 = t + "qmtc2 $8, $vf6 \n\t" // lerp: + "vsub.xyzw $vf7, $vf5, $vf4 \n\t" // $vf7 = v2 - v1 + "vmulx.xyzw $vf8, $vf7, $vf6 \n\t" // $vf8 = $vf7 * t + "vadd.xyzw $vf9, $vf8, $vf4 \n\t" // $vf9 = $vf8 + $vf4 + "sqc2 $vf9, 0x0(%0) \n\t" // v0 = $vf9 + : + : "r"(&output->xyzw), "r"(&v1.xyzw), "r"(&v2.xyzw), "f"(interp)); +} + +void Vec4::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void Vec4::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string Vec4::getPrint(const char* name) const { + std::stringstream res; + if (name) { + res << name << "("; + } else { + res << "Vec4("; + } + res << std::fixed << std::setprecision(4); + res << x << ", " << y << ", " << z << ", " << w << ")"; + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/pad/pad.cpp b/engine/src/pad/pad.cpp new file mode 100644 index 0000000..18232a7 --- /dev/null +++ b/engine/src/pad/pad.cpp @@ -0,0 +1,229 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020-2022, tyra - https://github.com/h4570/tyrav2 +# Licenced under Apache License 2.0 +# Sandro Sobczyński +# Sandro Wellinator +*/ + +#include +#include +#include +#include +#include +#include "debug/debug.hpp" +#include "pad/pad.hpp" + +namespace Tyra { + +/** Init vars, load modules, opens pad port and initializes pad */ +Pad::Pad() {} + +Pad::~Pad() {} + +// ---- +// Methods +// ---- + +void Pad::init() { + this->oldPad = 0; + padInit(0); + this->port = 0; // 0 -> Connector 1, 1 -> Connector 2 + this->slot = 0; // Always zero if not using multitap + + this->ret = padPortOpen(this->port, this->slot, padBuf); + TYRA_ASSERT(this->ret != 0, + "padPortOpen failed! padPortOpen returned: ", this->ret); + TYRA_ASSERT(this->initPad(), "initPad failed!"); +} + +/** Wait when pad will be ready (stable and ready) */ +int Pad::waitPadReady() { + int state; + int lastState; + char* stateString = new char[16]; + state = padGetState(this->port, this->slot); + lastState = -1; + while ((state != PAD_STATE_STABLE) && (state != PAD_STATE_FINDCTP1)) { + if (state != lastState) { + padStateInt2String(state, stateString); + TYRA_LOG("Pad state changed"); + printf("Curent pad(%d,%d) status: %s\n", this->port, this->slot, + stateString); + } + lastState = state; + state = padGetState(this->port, this->slot); + } + // Were the pad ever 'out of sync'? + if (lastState != -1) TYRA_LOG("Pad is ready!"); + + delete[] stateString; + return 0; +} + +/** Initializes and checks type of pad */ +int Pad::initPad() { + TYRA_LOG("Initializing pad"); + this->waitPadReady(); + + int modes = padInfoMode(this->port, this->slot, PAD_MODETABLE, -1); + TYRA_ASSERT(modes, "Connected device is not a dual shock controller!"); + + // Verify that the controller has a DUAL SHOCK mode + int i = 0; + do { + if (padInfoMode(this->port, this->slot, PAD_MODETABLE, i) == + PAD_TYPE_DUALSHOCK) + break; + i++; + } while (i < modes); + + TYRA_ASSERT(i < modes, "Connected device is not a dual shock controller!"); + + // If ExId != 0x0 => This controller has actuator engines + // This check should always pass if the Dual Shock test above passed + this->ret = padInfoMode(this->port, this->slot, PAD_MODECUREXID, 0); + TYRA_ASSERT(this->ret, "Connected device is not a dual shock controller!"); + + TYRA_LOG("Enabling dual shock functions."); + + // When using MMODE_LOCK, user cant change mode with Select button + padSetMainMode(this->port, this->slot, PAD_MMODE_DUALSHOCK, PAD_MMODE_LOCK); + + this->waitPadReady(); + TYRA_LOG("Pad has pressure sensitive buttons? ", + padInfoPressMode(this->port, this->slot)); + this->waitPadReady(); + padEnterPressMode(this->port, this->slot); // Set pressure sensitive mode + this->waitPadReady(); + this->actuators = padInfoAct(this->port, this->slot, -1, 0); + TYRA_LOG("# of actuators: ", this->actuators); + if (actuators != 0) { + this->actAlign[0] = 0; // Enable small engine + this->actAlign[1] = 1; // Enable big engine + this->actAlign[2] = 0xff; + this->actAlign[3] = 0xff; + this->actAlign[4] = 0xff; + this->actAlign[5] = 0xff; + this->waitPadReady(); + TYRA_LOG("padSetActAlign: ", + padSetActAlign(this->port, this->slot, actAlign)); + } else + TYRA_LOG("Did not find any actuators."); + this->waitPadReady(); + TYRA_LOG("Pad initialized!"); + return 1; +} + +/** Updates state of joys/buttons. Called by engine */ +void Pad::update() { + int x = 0; + this->ret = padGetState(this->port, this->slot); + while ((this->ret != PAD_STATE_STABLE) && (this->ret != PAD_STATE_FINDCTP1)) { + if (this->ret == PAD_STATE_DISCONN) + printf("Pad(%d, %d) is disconnected\n", this->port, this->slot); + this->ret = padGetState(this->port, this->slot); + } + if (x == 1) TYRA_LOG("Pad: OK!\n"); + + this->ret = padRead(this->port, this->slot, &this->buttons); + + if (this->ret != 0) { + this->padData = 0xffff ^ this->buttons.btns; + + this->newPad = this->padData & ~this->oldPad; + this->oldPad = this->padData; + this->reset(); + + // Digital buttons + this->rightJoyPad.h = this->buttons.rjoy_h; + this->rightJoyPad.v = this->buttons.rjoy_v; + this->leftJoyPad.h = this->buttons.ljoy_h; + this->leftJoyPad.v = this->buttons.ljoy_v; + + this->rightJoyPad.isCentered = + this->buttons.rjoy_h == 127 && this->buttons.rjoy_v == 127; + this->rightJoyPad.isMoved = !this->rightJoyPad.isCentered; + + this->leftJoyPad.isCentered = + this->buttons.ljoy_h == 127 && this->buttons.ljoy_v == 127; + this->leftJoyPad.isMoved = !this->leftJoyPad.isCentered; + + this->handleClickedButtons(); + this->handlePressedButtons(); + } +} + +/** Update clicked buttons state */ +void Pad::handleClickedButtons() { + if (this->newPad & PAD_CROSS) this->clicked.Cross = 1; + if (this->newPad & PAD_SQUARE) this->clicked.Square = 1; + if (this->newPad & PAD_TRIANGLE) this->clicked.Triangle = 1; + if (this->newPad & PAD_CIRCLE) this->clicked.Circle = 1; + if (this->newPad & PAD_UP) this->clicked.DpadUp = 1; + if (this->newPad & PAD_DOWN) this->clicked.DpadDown = 1; + if (this->newPad & PAD_LEFT) this->clicked.DpadLeft = 1; + if (this->newPad & PAD_RIGHT) this->clicked.DpadRight = 1; + if (this->newPad & PAD_L1) this->clicked.L1 = 1; + if (this->newPad & PAD_L2) this->clicked.L2 = 1; + if (this->newPad & PAD_L3) this->clicked.L3 = 1; + if (this->newPad & PAD_R1) this->clicked.R1 = 1; + if (this->newPad & PAD_R2) this->clicked.R2 = 1; + if (this->newPad & PAD_R3) this->clicked.R3 = 1; + if (this->newPad & PAD_START) this->clicked.Start = 1; + if (this->newPad & PAD_SELECT) this->clicked.Select = 1; +} + +/** Update pressed buttons state */ +void Pad::handlePressedButtons() { + if (this->buttons.cross_p) this->pressed.Cross = 1; + if (this->buttons.square_p) this->pressed.Square = 1; + if (this->buttons.triangle_p) this->pressed.Triangle = 1; + if (this->buttons.circle_p) this->pressed.Circle = 1; + if (this->buttons.up_p) this->pressed.DpadUp = 1; + if (this->buttons.down_p) this->pressed.DpadDown = 1; + if (this->buttons.left_p) this->pressed.DpadLeft = 1; + if (this->buttons.right_p) this->pressed.DpadRight = 1; + if (this->buttons.l1_p) this->pressed.L1 = 1; + if (this->buttons.l2_p) this->pressed.L2 = 1; + if (this->buttons.r1_p) this->pressed.R1 = 1; + if (this->buttons.r2_p) this->pressed.R2 = 1; +} + +/** Resets state of joys/buttons */ +void Pad::reset() { + this->clicked.Cross = 0; + this->clicked.Square = 0; + this->clicked.Triangle = 0; + this->clicked.Circle = 0; + this->clicked.DpadUp = 0; + this->clicked.DpadDown = 0; + this->clicked.DpadLeft = 0; + this->clicked.DpadRight = 0; + this->clicked.L1 = 0; + this->clicked.L2 = 0; + this->clicked.L3 = 0; + this->clicked.R1 = 0; + this->clicked.R2 = 0; + this->clicked.R3 = 0; + this->clicked.Start = 0; + this->clicked.Select = 0; + + this->pressed.Cross = 0; + this->pressed.Square = 0; + this->pressed.Triangle = 0; + this->pressed.Circle = 0; + this->pressed.DpadUp = 0; + this->pressed.DpadDown = 0; + this->pressed.DpadLeft = 0; + this->pressed.DpadRight = 0; + this->pressed.L1 = 0; + this->pressed.L2 = 0; + this->pressed.R1 = 0; + this->pressed.R2 = 0; +} + +} // Namespace Tyra \ No newline at end of file diff --git a/engine/src/renderer/2d/renderer_2d.cpp b/engine/src/renderer/2d/renderer_2d.cpp new file mode 100644 index 0000000..8da61a5 --- /dev/null +++ b/engine/src/renderer/2d/renderer_2d.cpp @@ -0,0 +1,27 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/2d/renderer_2d.hpp" + +namespace Tyra { + +Renderer2D::Renderer2D() {} +Renderer2D::~Renderer2D() {} + +void Renderer2D::init(RendererCore* t_rendererCore) { core = t_rendererCore; } + +void Renderer2D::render(Sprite* sprite) { + auto* texture = core->texture.repository.getBySpriteOrMesh(sprite->getId()); + auto texBuffers = core->texture.useTexture(texture); + core->texture.updateClutBuffer(texBuffers.clut); + core->renderer2D.render(sprite, texBuffers, texture); +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/bbox/bbox.cpp b/engine/src/renderer/3d/bbox/bbox.cpp new file mode 100644 index 0000000..64e5d20 --- /dev/null +++ b/engine/src/renderer/3d/bbox/bbox.cpp @@ -0,0 +1,53 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/bbox/bbox.hpp" + +namespace Tyra { + +BBox::BBox(CoreBBox** t_bboxes, const u32& count) : CoreBBox(t_bboxes, count) { + setData(); +} + +BBox::BBox(Vec4* t_vertices, u32 count) : CoreBBox(t_vertices, count) { + setData(); +} + +BBox::BBox(Vec4* t_vertices, u32* faces, u32 count) + : CoreBBox(t_vertices, faces, count) { + setData(); +} + +BBox::BBox(Vec4* t_vertices) : CoreBBox(t_vertices) { setData(); } + +void BBox::setData() { + // This might be shortened with Vec4 operator overloading, but current + // implementation is more human readable. + _height = _vertices[0].y - _vertices[2].y; + _width = _vertices[0].x - _vertices[4].x; + _depth = _vertices[0].z - _vertices[1].z; + + _centerVector = _vertices[0]; + _centerVector.x += (_width / 2); + _centerVector.y += (_height / 2); + _centerVector.z += (_depth / 2); + + // Z-Axis faces + _frontFace = BBoxFace(_vertices[1], _vertices[7], _vertices[1].z); + _backFace = BBoxFace(_vertices[0], _vertices[6], _vertices[0].z); + // X-Axis faces + _leftFace = BBoxFace(_vertices[0], _vertices[3], _vertices[0].x); + _rightFace = BBoxFace(_vertices[4], _vertices[7], _vertices[4].x); + // Y-Axis faces + _topFace = BBoxFace(_vertices[2], _vertices[7], _vertices[2].y); + _bottomFace = BBoxFace(_vertices[0], _vertices[5], _vertices[0].y); +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/mesh/mesh.cpp b/engine/src/renderer/3d/mesh/mesh.cpp new file mode 100644 index 0000000..78d30e8 --- /dev/null +++ b/engine/src/renderer/3d/mesh/mesh.cpp @@ -0,0 +1,144 @@ + +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include "math/m4x4.hpp" +#include "renderer/3d/mesh/mesh.hpp" + +namespace Tyra { + +Mesh::Mesh(const MeshBuilderData& data) { + id = rand() % 1000000; + + framesCount = data.framesCount; + TYRA_ASSERT(framesCount > 0, "Frames count must be greater than 0"); + + materialsCount = data.materialsCount; + TYRA_ASSERT(materialsCount > 0, "Materials count must be greater than 0"); + + frames = new MeshFrame*[framesCount]; + for (u32 i = 0; i < framesCount; i++) { + frames[i] = new MeshFrame(data, i); + } + + materials = new MeshMaterial*[materialsCount]; + for (u32 i = 0; i < materialsCount; i++) { + materials[i] = new MeshMaterial(data, i); + } + + translation.translate(Vec4(0.0F, 0.0F, 0.0F, 1.0F)); + + initMesh(); + + _isMother = true; +} + +Mesh::Mesh(const Mesh& mesh) { + id = rand() % 1000000; + + framesCount = mesh.framesCount; + materialsCount = mesh.materialsCount; + + frames = new MeshFrame*[framesCount]; + for (u32 i = 0; i < framesCount; i++) { + frames[i] = new MeshFrame(*mesh.frames[i]); + } + + materials = new MeshMaterial*[materialsCount]; + for (u32 i = 0; i < materialsCount; i++) { + materials[i] = new MeshMaterial(*mesh.materials[i]); + } + + translation.translate(Vec4(0.0F, 0.0F, 0.0F, 1.0F)); + + initMesh(); + + _isMother = false; +} + +Mesh::~Mesh() { + for (u32 i = 0; i < framesCount; i++) { + delete frames[i]; + } + delete[] frames; + + for (u32 i = 0; i < materialsCount; i++) { + delete materials[i]; + + delete[] materials; + } +} + +M4x4 Mesh::getModelMatrix() const { return translation * rotation * scale; } + +void Mesh::initMesh() { + animState.startFrame = 0; + animState.endFrame = 0; + animState.interpolation = 0.0F; + animState.animType = 0; + animState.currentFrame = 0; + animState.stayFrame = 0; + animState.isStayFrameSet = false; + animState.nextFrame = 0; + animState.speed = 0.1F; +} + +void Mesh::playAnimation(const u32& t_startFrame, const u32& t_endFrame) { + TYRA_ASSERT(framesCount > 0, + "Cant play animation, because no mesh data was loaded!"); + TYRA_ASSERT(framesCount != 1, + "Cant play animation, because this mesh have only one frame."); + TYRA_ASSERT( + t_endFrame < framesCount, + "End frame value is too high. Valid range: (0, getFramesCount()-1)"); + animState.startFrame = t_startFrame; + animState.endFrame = t_endFrame; + if (animState.currentFrame == t_startFrame) + animState.nextFrame = t_endFrame; + else + animState.nextFrame = t_startFrame; +} + +void Mesh::playAnimation(const u32& t_startFrame, const u32& t_endFrame, + const u32& t_stayFrame) { + TYRA_ASSERT(framesCount > 0, + "Cant play animation, because no mesh data was loaded!"); + TYRA_ASSERT(framesCount != 1, + "Cant play animation, because this mesh have only one frame."); + TYRA_ASSERT( + t_endFrame < framesCount, + "End frame value is too high. Valid range: (0, getFramesCount()-1)"); + animState.startFrame = t_startFrame; + animState.endFrame = t_endFrame; + animState.isStayFrameSet = true; + animState.stayFrame = t_stayFrame; + animState.nextFrame = t_startFrame; +} + +void Mesh::animate() { + animState.interpolation += animState.speed; + if (animState.interpolation >= 1.0F) { + animState.interpolation = 0.0F; + animState.currentFrame = animState.nextFrame; + if (++animState.nextFrame > animState.endFrame) { + if (animState.isStayFrameSet) { + animState.isStayFrameSet = false; + animState.nextFrame = animState.stayFrame; + animState.startFrame = animState.stayFrame; + animState.endFrame = animState.stayFrame; + } else { + animState.nextFrame = animState.startFrame; + } + } + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/mesh/mesh_frame.cpp b/engine/src/renderer/3d/mesh/mesh_frame.cpp new file mode 100644 index 0000000..4e5219b --- /dev/null +++ b/engine/src/renderer/3d/mesh/mesh_frame.cpp @@ -0,0 +1,150 @@ + +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include +#include +#include "math/vec4.hpp" +#include "renderer/models/color.hpp" +#include "renderer/3d/mesh/mesh_frame.hpp" + +namespace Tyra { + +MeshFrame::MeshFrame(const MeshBuilderData& data, const u32& index) { + TYRA_ASSERT(index < data.framesCount && index >= 0, "Provided index \"", + index, "\" is out of range"); + + id = rand() % 1000000; + + vertices = data.frames[index]->vertices; + TYRA_ASSERT(vertices != nullptr, "Vertices are required"); + vertexCount = data.frames[index]->verticesCount; + TYRA_ASSERT(vertexCount > 0, "Vertices count must be greater than 0"); + + if (data.normalsEnabled) { + normals = data.frames[index]->normals; + normalsCount = data.frames[index]->normalsCount; + TYRA_ASSERT(normals != nullptr, "Normals are required"); + } else { + normalsCount = 0; + normals = nullptr; + } + + if (data.textureCoordsEnabled) { + textureCoords = data.frames[index]->textureCoords; + textureCoordsCount = data.frames[index]->textureCoordsCount; + TYRA_ASSERT(textureCoords != nullptr, "Texture coordinates are required"); + } else { + textureCoordsCount = 0; + textureCoords = nullptr; + } + + if (data.manyColorsEnabled) { + colors = data.frames[index]->colors; + colorsCount = data.frames[index]->colorsCount; + TYRA_ASSERT(colors != nullptr, "Colors are required"); + } else { + colorsCount = 0; + colors = nullptr; + } + + bbox = + new BBox(data.frames[index]->vertices, data.frames[index]->verticesCount); + + _isMother = true; +} + +MeshFrame::MeshFrame(const MeshFrame& frame) { + id = rand() % 1000000; + + vertices = frame.vertices; + normals = frame.normals; + textureCoords = frame.textureCoords; + colors = frame.colors; + + vertexCount = frame.vertexCount; + normalsCount = frame.normalsCount; + textureCoordsCount = frame.textureCoordsCount; + colorsCount = frame.colorsCount; + + bbox = frame.bbox; + + _isMother = false; +} + +MeshFrame::~MeshFrame() { + if (_isMother) { + delete[] vertices; + if (normals) delete[] normals; + if (textureCoords) delete[] textureCoords; + if (colors) delete[] colors; + delete bbox; + } +} + +void MeshFrame::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void MeshFrame::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string MeshFrame::getPrint(const char* name) const { + std::stringstream res; + if (name) { + res << name << "("; + } else { + res << "MeshFrame("; + } + res << std::fixed << std::setprecision(2); + + res << "Id: " << id << ", " << std::endl; + res << "VertexCount: " << vertexCount << ", " << std::endl; + res << "NormalsCount: " << normalsCount << ", " << std::endl; + res << "TextureCoordsCount: " << textureCoordsCount << ", " << std::endl; + res << "ColorsCount: " << colorsCount << ", " << std::endl; + res << "BBox: " << bbox->getPrint() << ", " << std::endl; + + res << "Vertices: "; + for (u32 i = 0; i < vertexCount; i++) { + res << vertices[i].getPrint() << ", " << std::endl; + } + + if (normals) { + res << "Normals: "; + for (u32 i = 0; i < normalsCount; i++) { + res << normals[i].getPrint() << ", " << std::endl; + } + } + + if (textureCoords) { + res << "TextureCoords: "; + for (u32 i = 0; i < textureCoordsCount; i++) { + res << textureCoords[i].getPrint() << ", " << std::endl; + } + } + + if (colors) { + res << "Colors: "; + for (u32 i = 0; i < colorsCount; i++) { + res << colors[i].getPrint() << ", " << std::endl; + } + } + + res << ")"; + + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/mesh/mesh_material.cpp b/engine/src/renderer/3d/mesh/mesh_material.cpp new file mode 100644 index 0000000..d0087df --- /dev/null +++ b/engine/src/renderer/3d/mesh/mesh_material.cpp @@ -0,0 +1,179 @@ + +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include +#include +#include "renderer/3d/mesh/mesh_material.hpp" + +namespace Tyra { + +MeshMaterial::MeshMaterial(const MeshBuilderData& data, + const u32& materialIndex) + : singleColor(false) { + TYRA_ASSERT(materialIndex < data.materialsCount && materialIndex >= 0, + "Provided index \"", materialIndex, "\" is out of range"); + + id = rand() % 1000000; + + vertexFaces = data.materials[materialIndex]->vertexFaces; + TYRA_ASSERT(vertexFaces != nullptr, "Vertex faces are required"); + + if (data.textureCoordsEnabled) { + textureCoordFaces = data.materials[materialIndex]->textureCoordFaces; + TYRA_ASSERT(textureCoordFaces != nullptr, + "Texture coord faces are required"); + } else { + textureCoordFaces = nullptr; + } + + if (data.normalsEnabled) { + normalFaces = data.materials[materialIndex]->normalFaces; + TYRA_ASSERT(normalFaces != nullptr, "Normal faces are required"); + } else { + normalFaces = nullptr; + } + + if (data.manyColorsEnabled) { + colorFaces = data.materials[materialIndex]->colorFaces; + singleColorFlag = false; + TYRA_ASSERT(colorFaces != nullptr, "Colors faces are required"); + } else { + colorFaces = nullptr; + singleColorFlag = true; + } + + singleColor.set(128.0F, 128.0F, 128.0F, 128.0F); + + facesCount = data.materials[materialIndex]->count; + TYRA_ASSERT(facesCount > 0, "Faces count must be greater than 0"); + + _name = data.materials[materialIndex]->name; + TYRA_ASSERT(_name.length() > 0, "MeshMaterial name cannot be empty"); + + framesCount = data.framesCount; + frames = new MeshMaterialFrame*[framesCount]; + for (u32 i = 0; i < framesCount; i++) { + frames[i] = new MeshMaterialFrame(data, i, materialIndex); + } + + _isMother = true; +} + +MeshMaterial::MeshMaterial(const MeshMaterial& mesh) { + id = rand() % 1000000; + + vertexFaces = mesh.vertexFaces; + textureCoordFaces = mesh.textureCoordFaces; + normalFaces = mesh.normalFaces; + colorFaces = mesh.colorFaces; + + facesCount = mesh.facesCount; + framesCount = mesh.framesCount; + _name = mesh._name; + + singleColor.set(128.0F, 128.0F, 128.0F, 128.0F); + + frames = new MeshMaterialFrame*[framesCount]; + for (u32 i = 0; i < framesCount; i++) { + frames[i] = new MeshMaterialFrame(*mesh.frames[i]); + } + + _isMother = false; +} + +MeshMaterial::~MeshMaterial() { + if (_isMother) { + delete[] vertexFaces; + if (textureCoordFaces) delete[] textureCoordFaces; + if (normalFaces) delete[] normalFaces; + if (colorFaces) delete[] colorFaces; + } + + for (u32 i = 0; i < framesCount; i++) { + delete frames[i]; + } + delete[] frames; +} + +const BBox& MeshMaterial::getBBox(const u32& frame) const { + return frames[frame]->getBBox(); +} + +void MeshMaterial::setSingleColorFlag(const u8& flag) { + TYRA_ASSERT( + colorFaces != nullptr, + "Colors and color faces are required to use color-per-vertex mode"); + + singleColorFlag = flag; +} + +void MeshMaterial::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void MeshMaterial::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string MeshMaterial::getPrint(const char* name) const { + std::stringstream res; + if (name) { + res << name << "("; + } else { + res << "MeshMaterial("; + } + + res << std::endl; + res << std::fixed << std::setprecision(2); + res << "Id: " << id << ", " << std::endl; + res << "Name: " << _name << ", " << std::endl; + res << "FacesCount: " << facesCount << ", " << std::endl; + res << "FramesCount: " << framesCount << ", " << std::endl; + + res << "vertexFaces: " << std::endl; + for (u32 i = 0; i < facesCount; i++) { + res << vertexFaces[i] << ", "; + if (i % 3 == 2) res << std::endl; + } + + if (textureCoordFaces) { + res << "TextureCoordFaces: " << std::endl; + for (u32 i = 0; i < facesCount; i++) { + res << textureCoordFaces[i] << ", "; + if (i % 3 == 2) res << std::endl; + } + } + + if (normalFaces) { + res << "NormalFaces: " << std::endl; + for (u32 i = 0; i < facesCount; i++) { + res << normalFaces[i] << ", "; + if (i % 3 == 2) res << std::endl; + } + } + + if (colorFaces) { + res << "ColorFaces: " << std::endl; + for (u32 i = 0; i < facesCount; i++) { + res << colorFaces[i] << ", "; + if (i % 3 == 2) res << std::endl; + } + } + + res << ")"; + + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/mesh/mesh_material_frame.cpp b/engine/src/renderer/3d/mesh/mesh_material_frame.cpp new file mode 100644 index 0000000..f4e8519 --- /dev/null +++ b/engine/src/renderer/3d/mesh/mesh_material_frame.cpp @@ -0,0 +1,51 @@ + +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include +#include "renderer/models/color.hpp" +#include "loaders/3d/builder/mesh_builder_data.hpp" +#include "renderer/3d/mesh/mesh_material_frame.hpp" + +namespace Tyra { + +MeshMaterialFrame::MeshMaterialFrame(const MeshBuilderData& data, + const u32& frameIndex, + const u32& materialIndex) { + TYRA_ASSERT(frameIndex < data.framesCount && frameIndex >= 0, + "Provided index \"", frameIndex, "\" is out of range"); + TYRA_ASSERT(materialIndex < data.materialsCount && materialIndex >= 0, + "Provided index \"", materialIndex, "\" is out of range"); + + id = rand() % 1000000; + + bbox = new BBox(data.frames[frameIndex]->vertices, + data.materials[materialIndex]->vertexFaces, + data.materials[materialIndex]->count); + + _isMother = true; +} + +MeshMaterialFrame::MeshMaterialFrame(const MeshMaterialFrame& frame) { + id = rand() % 1000000; + + bbox = frame.bbox; + + _isMother = false; +} + +MeshMaterialFrame::~MeshMaterialFrame() { + if (_isMother) { + delete bbox; + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/minecraft/data/mcpip_block_data.cpp b/engine/src/renderer/3d/pipeline/minecraft/data/mcpip_block_data.cpp new file mode 100644 index 0000000..6568f65 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/data/mcpip_block_data.cpp @@ -0,0 +1,25 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/minecraft/data/mcpip_block_data.hpp" + +namespace Tyra { + +McpipBlockData::McpipBlockData() { + vertices = nullptr; + textureCoords = nullptr; + comboData = nullptr; + offset = 0.0F; + count = 0; +} + +McpipBlockData::~McpipBlockData() {} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/minecraft/data/mcpip_multi_tex_block_data.cpp b/engine/src/renderer/3d/pipeline/minecraft/data/mcpip_multi_tex_block_data.cpp new file mode 100644 index 0000000..37f43e8 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/data/mcpip_multi_tex_block_data.cpp @@ -0,0 +1,132 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0F +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/minecraft/data/mcpip_multi_tex_block_data.hpp" + +namespace Tyra { + +McpipMultiTexBlockData::McpipMultiTexBlockData() { + allocateTempData(); + unroll(); + dellocateTempData(); +} + +McpipMultiTexBlockData::~McpipMultiTexBlockData() { + if (comboData != nullptr) { + delete[] comboData; + } +} + +void McpipMultiTexBlockData::allocateTempData() { + u32 tempVertsStsCount = 24; + + tempVerts = new Tyra::Vec4[tempVertsStsCount]; + tempVerts[0].set(-1.0F, -1.0F, -1.0F); + tempVerts[1].set(1.0F, -1.0F, -1.0F); + tempVerts[2].set(1.0F, -1.0F, 1.0F); + tempVerts[3].set(-1.0F, -1.0F, 1.0F); + tempVerts[4].set(1.0F, -1.0F, 1.0F); + tempVerts[5].set(1.0F, 1.0F, 1.0F); + tempVerts[6].set(-1.0F, 1.0F, 1.0F); + tempVerts[7].set(-1.0F, -1.0F, 1.0F); + tempVerts[8].set(-1.0F, -1.0F, 1.0F); + tempVerts[9].set(-1.0F, 1.0F, 1.0F); + tempVerts[10].set(-1.0F, 1.0F, -1.0F); + tempVerts[11].set(-1.0F, -1.0F, -1.0F); + tempVerts[12].set(1.0F, -1.0F, -1.0F); + tempVerts[13].set(1.0F, 1.0F, -1.0F); + tempVerts[14].set(1.0F, 1.0F, 1.0F); + tempVerts[15].set(1.0F, -1.0F, 1.0F); + tempVerts[16].set(-1.0F, -1.0F, -1.0F); + tempVerts[17].set(-1.0F, 1.0F, -1.0F); + tempVerts[18].set(1.0F, 1.0F, -1.0F); + tempVerts[19].set(1.0F, -1.0F, -1.0F); + tempVerts[20].set(1.0F, 1.0F, -1.0F); + tempVerts[21].set(-1.0F, 1.0F, -1.0F); + tempVerts[22].set(-1.0F, 1.0F, 1.0F); + tempVerts[23].set(1.0F, 1.0F, 1.0F); + + tempTexCoords = new Tyra::Vec4[tempVertsStsCount]; + tempTexCoords[0].set(0.062721F, 0.813282F, 1.0F, 0.0F); + tempTexCoords[1].set(0.062721F, 0.875327F, 1.0F, 0.0F); + tempTexCoords[2].set(0.000676F, 0.875327F, 1.0F, 0.0F); + tempTexCoords[3].set(0.000676F, 0.813282F, 1.0F, 0.0F); + tempTexCoords[4].set(0.062095F, 0.750461F, 1.0F, 0.0F); + tempTexCoords[5].set(0.062095F, 0.812244F, 1.0F, 0.0F); + tempTexCoords[6].set(0.000311F, 0.812244F, 1.0F, 0.0F); + tempTexCoords[7].set(0.000311F, 0.750461F, 1.0F, 0.0F); + tempTexCoords[8].set(0.062629F, 0.687779F, 1.0F, 0.0F); + tempTexCoords[9].set(0.000000F, 0.687779F, 1.0F, 0.0F); + tempTexCoords[10].set(0.000000F, 0.624816F, 1.0F, 0.0F); + tempTexCoords[11].set(0.062629F, 0.624816F, 1.0F, 0.0F); + tempTexCoords[12].set(0.062667F, 0.626382F, 1.0F, 0.0F); + tempTexCoords[13].set(0.000000F, 0.626382F, 1.0F, 0.0F); + tempTexCoords[14].set(0.000000F, 0.561642F, 1.0F, 0.0F); + tempTexCoords[15].set(0.062667F, 0.561642F, 1.0F, 0.0F); + tempTexCoords[16].set(0.000000F, 0.937621F, 1.0F, 0.0F); + tempTexCoords[17].set(0.000000F, 0.875134F, 1.0F, 0.0F); + tempTexCoords[18].set(0.062128F, 0.875134F, 1.0F, 0.0F); + tempTexCoords[19].set(0.062128F, 0.937621F, 1.0F, 0.0F); + tempTexCoords[20].set(0.000000F, 0.937223F, 1.0F, 0.0F); + tempTexCoords[21].set(0.062223F, 0.937223F, 1.0F, 0.0F); + tempTexCoords[22].set(0.062223F, 0.999641F, 1.0F, 0.0F); + tempTexCoords[23].set(0.000000F, 0.999641F, 1.0F, 0.0F); + + // Because 16 blocks can fit in single column of 256x256 tex atlas + offset = 1.0F / 16.0F; + + for (u32 i = 0; i < tempVertsStsCount; i++) + tempTexCoords[i].y = 1.0F - tempTexCoords[i].y; + + tempVertFaces = new u32[36]; + tempTexCoordsFaces = new u32[36]; + + std::string vertexFaces = + "1,2,3,1,3,4,5,6,7,5,7,8,9,10,11,9,11,12,13,14,15,13,15,16,17,18,19,17," + "19,20,21,22,23,21,23,24"; + std::stringstream ssVertexFaces(vertexFaces); + + std::string texCoordFaces = + "1,2,3,1,3,4,5,6,7,5,7,8,9,10,11,9,11,12,13,14,15,13,15,16,17,18,19,17," + "19,20,21,22,23,21,23,24"; + std::stringstream ssTexCoordFaces(texCoordFaces); + + int i = 0; + std::string item; + + while (std::getline(ssVertexFaces, item, ',')) + tempVertFaces[i++] = std::stoi(item) - 1; + + i = 0; + + while (std::getline(ssTexCoordFaces, item, ',')) + tempTexCoordsFaces[i++] = std::stoi(item) - 1; +} + +void McpipMultiTexBlockData::unroll() { + count = 36; + comboData = new Tyra::Vec4[36 * 2]; + vertices = &comboData[0]; + textureCoords = &comboData[36]; + + for (u32 i = 0; i < count; i++) { + vertices[i] = tempVerts[tempVertFaces[i]]; + textureCoords[i] = tempTexCoords[tempTexCoordsFaces[i]]; + } +} + +void McpipMultiTexBlockData::dellocateTempData() { + delete[] tempVerts; + delete[] tempTexCoords; + delete[] tempVertFaces; + delete[] tempTexCoordsFaces; +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/minecraft/data/mcpip_single_tex_block_data.cpp b/engine/src/renderer/3d/pipeline/minecraft/data/mcpip_single_tex_block_data.cpp new file mode 100644 index 0000000..826b556 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/data/mcpip_single_tex_block_data.cpp @@ -0,0 +1,132 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0F +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/minecraft/data/mcpip_single_tex_block_data.hpp" + +namespace Tyra { + +McpipSingleTexBlockData::McpipSingleTexBlockData() { + allocateTempData(); + unroll(); + dellocateTempData(); +} + +McpipSingleTexBlockData::~McpipSingleTexBlockData() { + if (comboData != nullptr) { + delete[] comboData; + } +} + +void McpipSingleTexBlockData::allocateTempData() { + u32 tempVertsStsCount = 24; + + tempVerts = new Tyra::Vec4[tempVertsStsCount]; + tempVerts[0].set(-1.0F, -1.0F, -1.0F); + tempVerts[1].set(1.0F, -1.0F, -1.0F); + tempVerts[2].set(1.0F, -1.0F, 1.0F); + tempVerts[3].set(-1.0F, -1.0F, 1.0F); + tempVerts[4].set(1.0F, -1.0F, 1.0F); + tempVerts[5].set(1.0F, 1.0F, 1.0F); + tempVerts[6].set(-1.0F, 1.0F, 1.0F); + tempVerts[7].set(-1.0F, -1.0F, 1.0F); + tempVerts[8].set(-1.0F, -1.0F, 1.0F); + tempVerts[9].set(-1.0F, 1.0F, 1.0F); + tempVerts[10].set(-1.0F, 1.0F, -1.0F); + tempVerts[11].set(-1.0F, -1.0F, -1.0F); + tempVerts[12].set(1.0F, -1.0F, -1.0F); + tempVerts[13].set(1.0F, 1.0F, -1.0F); + tempVerts[14].set(1.0F, 1.0F, 1.0F); + tempVerts[15].set(1.0F, -1.0F, 1.0F); + tempVerts[16].set(-1.0F, -1.0F, -1.0F); + tempVerts[17].set(-1.0F, 1.0F, -1.0F); + tempVerts[18].set(1.0F, 1.0F, -1.0F); + tempVerts[19].set(1.0F, -1.0F, -1.0F); + tempVerts[20].set(1.0F, 1.0F, -1.0F); + tempVerts[21].set(-1.0F, 1.0F, -1.0F); + tempVerts[22].set(-1.0F, 1.0F, 1.0F); + tempVerts[23].set(1.0F, 1.0F, 1.0F); + + tempTexCoords = new Tyra::Vec4[tempVertsStsCount]; + tempTexCoords[0].set(0.000618F, 0.937685F, 1.0F, 0.0F); + tempTexCoords[1].set(0.062592F, 0.937685F, 1.0F, 0.0F); + tempTexCoords[2].set(0.062592F, 0.999658F, 1.0F, 0.0F); + tempTexCoords[3].set(0.000618F, 0.999658F, 1.0F, 0.0F); + tempTexCoords[4].set(0.062608F, 0.937379F, 1.0F, 0.0F); + tempTexCoords[5].set(0.062576F, 0.999352F, 1.0F, 0.0F); + tempTexCoords[6].set(0.000602F, 0.999320F, 1.0F, 0.0F); + tempTexCoords[7].set(0.000634F, 0.937346F, 1.0F, 0.0F); + tempTexCoords[8].set(0.062592F, 0.937381F, 1.0F, 0.0F); + tempTexCoords[9].set(0.062592F, 0.999354F, 1.0F, 0.0F); + tempTexCoords[10].set(0.000618F, 0.999354F, 1.0F, 0.0F); + tempTexCoords[11].set(0.000618F, 0.937381F, 1.0F, 0.0F); + tempTexCoords[12].set(0.062618F, 0.937600F, 1.0F, 0.0F); + tempTexCoords[13].set(0.062618F, 0.999542F, 1.0F, 0.0F); + tempTexCoords[14].set(0.000603F, 0.999576F, 1.0F, 0.0F); + tempTexCoords[15].set(0.000616F, 0.937585F, 1.0F, 0.0F); + tempTexCoords[16].set(0.062561F, 0.937533F, 1.0F, 0.0F); + tempTexCoords[17].set(0.062561F, 0.999446F, 1.0F, 0.0F); + tempTexCoords[18].set(0.000649F, 0.999446F, 1.0F, 0.0F); + tempTexCoords[19].set(0.000649F, 0.937533F, 1.0F, 0.0F); + tempTexCoords[20].set(0.000556F, 0.937533F, 1.0F, 0.0F); + tempTexCoords[21].set(0.062530F, 0.937533F, 1.0F, 0.0F); + tempTexCoords[22].set(0.062530F, 0.999506F, 1.0F, 0.0F); + tempTexCoords[23].set(0.000556F, 0.999506F, 1.0F, 0.0F); + + // Because 16 blocks can fit in single column of 256x256 tex atlas + offset = 1.0F / 16.0F; + + for (u32 i = 0; i < tempVertsStsCount; i++) + tempTexCoords[i].y = 1.0F - tempTexCoords[i].y; + + tempVertFaces = new u32[36]; + tempTexCoordsFaces = new u32[36]; + + std::string vertexFaces = + "1,2,3,1,3,4,5,6,7,5,7,8,9,10,11,9,11,12,13,14,15,13,15,16,17,18,19,17," + "19,20,21,22,23,21,23,24"; + std::stringstream ssVertexFaces(vertexFaces); + + std::string texCoordFaces = + "1,2,3,1,3,4,5,6,7,5,7,8,9,10,11,9,11,12,13,14,15,13,15,16,17,18,19,17," + "19,20,21,22,23,21,23,24"; + std::stringstream ssTexCoordFaces(texCoordFaces); + + int i = 0; + std::string item; + + while (std::getline(ssVertexFaces, item, ',')) + tempVertFaces[i++] = std::stoi(item) - 1; + + i = 0; + + while (std::getline(ssTexCoordFaces, item, ',')) + tempTexCoordsFaces[i++] = std::stoi(item) - 1; +} + +void McpipSingleTexBlockData::unroll() { + count = 36; + comboData = new Tyra::Vec4[36 * 2]; + vertices = &comboData[0]; + textureCoords = &comboData[36]; + + for (u32 i = 0; i < count; i++) { + vertices[i] = tempVerts[tempVertFaces[i]]; + textureCoords[i] = tempTexCoords[tempTexCoordsFaces[i]]; + } +} + +void McpipSingleTexBlockData::dellocateTempData() { + delete[] tempVerts; + delete[] tempTexCoords; + delete[] tempVertFaces; + delete[] tempTexCoordsFaces; +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/minecraft/minecraft_pipeline.cpp b/engine/src/renderer/3d/pipeline/minecraft/minecraft_pipeline.cpp new file mode 100644 index 0000000..2212b45 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/minecraft_pipeline.cpp @@ -0,0 +1,143 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include "renderer/3d/pipeline/minecraft/minecraft_pipeline.hpp" +#include "thread/threading.hpp" + +namespace Tyra { + +MinecraftPipeline::MinecraftPipeline() { latestMode = UndefinedMcpipProgram; } + +MinecraftPipeline::~MinecraftPipeline() { + if (bbox) { + delete bbox; + } +} + +void MinecraftPipeline::init(RendererCore* core) { + rendererCore = core; + manager.init(core); + + initBBox(); +} + +void MinecraftPipeline::onUse() { + manager.uploadVU1Programs(); + + changeMode(McPipCull, true); +} + +void MinecraftPipeline::initBBox() { + const auto& block = manager.getBlockData(); + bbox = new RenderBBox(block.vertices, block.count); +} + +void MinecraftPipeline::render(McpipBlock* blocks, const u32& count, + Texture* t_tex, const bool& isMulti, + const bool& noClipChecks) { + auto texBuffers = rendererCore->texture.useTexture(t_tex); + rendererCore->gs.prim.mapping = 1; + + manager.clearLastProgram(); + std::vector cullIndexes; + + if (noClipChecks) { + for (u32 i = 0; i < count; i++) cullIndexes.push_back(i); + cull(blocks, cullIndexes, &texBuffers, isMulti); + } else { + u32 culled = 0, clipped = 0; + std::vector clipIndexes; + + for (u32 i = 0; i < count; i++) { + auto frustum = isInFrustum(blocks[i]); + if (frustum == CoreBBoxFrustum::IN_FRUSTUM) { + cullIndexes.push_back(i); + culled++; + } else if (frustum == CoreBBoxFrustum::PARTIALLY_IN_FRUSTUM) { + clipIndexes.push_back(i); + clipped++; + } + } + + if (culled > 0) cull(blocks, cullIndexes, &texBuffers, isMulti); + if (clipped > 0) clip(blocks, clipIndexes, &texBuffers, isMulti); + } + + Threading::switchThread(); +} + +CoreBBoxFrustum MinecraftPipeline::isInFrustum(const McpipBlock& block) const { + const auto* frustumPlanes = rendererCore->renderer3D.frustumPlanes.getAll(); + return bbox->clipIsInFrustum(frustumPlanes, block.model); +} + +void MinecraftPipeline::cull(McpipBlock* blocks, + const std::vector& indexes, + RendererCoreTextureBuffers* texBuffers, + const bool& isMulti) { + changeMode(McPipCull, false); + + auto maxBlocksPerQBuffer = manager.culler.getMaxBlocksCountPerQBuffer(); + auto partsCount = static_cast( + ceil(indexes.size() / static_cast(maxBlocksPerQBuffer))); + + for (u32 i = 0; i < partsCount; i++) { + u32 subArraySize = i != partsCount - 1 + ? maxBlocksPerQBuffer + : indexes.size() - i * maxBlocksPerQBuffer; + + McpipBlock** blockPointerArray = new McpipBlock*[subArraySize]; + u32 blockPointerArrayCount = 0; + for (u32 j = 0; j < subArraySize; j++) { + blockPointerArray[blockPointerArrayCount++] = + &blocks[indexes[i * maxBlocksPerQBuffer + j]]; + } + + manager.cull(blockPointerArray, blockPointerArrayCount, texBuffers, + isMulti); + + delete[] blockPointerArray; + } +} + +// -- 2nd qbuff = 36 * 3 (ST,RGBA,STQ) +// * 2 (We will have at least 2x more verts after clip) +// + Set, lod, set, clut, prim = 221 +// -- 1st qbuff: 500 - 221 = 279 +// Tags: 279 - for example 20 = 259 +// Vert + ST from EE = 259 / 2 = 129 verts | OK! +void MinecraftPipeline::clip(McpipBlock* blocks, + const std::vector& indexes, + RendererCoreTextureBuffers* texBuffers, + const bool& isMulti) { + changeMode(McPipAsIs, false); + + for (u32 i = 0; i < indexes.size(); i++) { + manager.clip(&blocks[indexes[i]], texBuffers, isMulti); + } +} + +void MinecraftPipeline::changeMode(const McpipProgramName& requestedMode, + const u8& force) { + if (!force) { + if (latestMode == requestedMode) return; + } + + if (requestedMode == McPipCull) { + manager.culler.configureVU1AndSendStaticData(); + latestMode = McPipCull; + } else { + manager.clipper.configureVU1AndSendStaticData(); + latestMode = McPipAsIs; + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/minecraft/programs/as_is/macros.i b/engine/src/renderer/3d/pipeline/minecraft/programs/as_is/macros.i new file mode 100644 index 0000000..25c2611 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/programs/as_is/macros.i @@ -0,0 +1,34 @@ +;//-------------------------------------------------------------------------------- +;// MinecraftPipeline cull macros library +;//-------------------------------------------------------------------------------- + +;//--------------------------------------------------------- +;// LoadStaticData - Load lod, set and viewproj matrix +;//--------------------------------------------------------- +#macro LoadStaticData: t_lod, t_set + lq t_lod, VU1_MCPIP_AS_IS_STATIC_LOD(vi00) + lq t_set, VU1_MCPIP_AS_IS_STATIC_SET_TAG(vi00) +#endmacro + +;//--------------------------------------------------------- +;// LoadDynamicData - Load scale, prim and clut +;//--------------------------------------------------------- +#macro LoadDynamicData: t_scale, t_prim, t_clut, t_color, t_vertexCount, t_buffer + lq t_scale, VU1_MCPIP_AS_IS_DYNAMIC_SCALE(t_buffer) + lq t_prim, VU1_MCPIP_AS_IS_DYNAMIC_PRIM(t_buffer) + lq t_clut, VU1_MCPIP_AS_IS_DYNAMIC_CLUT(t_buffer) + lq t_color, VU1_MCPIP_AS_IS_DYNAMIC_COLOR(t_buffer) + ilw.w t_vertexCount, VU1_MCPIP_AS_IS_DYNAMIC_SCALE(t_buffer) +#endmacro + +;//--------------------------------------------------------- +;// StoreTags - Store lod, prim, clut +;//--------------------------------------------------------- +#macro StoreTags: t_lodTag, t_setTag, t_primTag, t_clut, t_destAddress + sq t_setTag, 0(t_destAddress) + sq t_lodTag, 1(t_destAddress) + sq t_setTag, 2(t_destAddress) + sq t_clut, 3(t_destAddress) + sq t_primTag, 4(t_destAddress) + iaddiu t_destAddress, t_destAddress, 5 +#endmacro diff --git a/engine/src/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_as_is_vu1.vclpp b/engine/src/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_as_is_vu1.vclpp new file mode 100644 index 0000000..a211973 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_as_is_vu1.vclpp @@ -0,0 +1,118 @@ +; ______ ____ ___ +; | \/ ____| |___| +; | | | \ | | +;--------------------------------------------------------------- +; Copyright 2022, tyra - https://github.com/h4570/tyra +; Licenced under Apache License 2.0 +; Sandro Sobczyński +; +;--------------------------------------------------------------- +; Super fast renderer of minecraft blocks. +; Block data is statically allocated in vi00 +; +; - Triangle list +; - AsIs = NO TRANSFORM +; - Colors +;--------------------------------------------------------------- + +.syntax new +.name VU1BlocksAsIs +.vu +.init_vf_all +.init_vi_all + +#include "src/renderer/core/paths/path1/programs/vcl_sml.i" +#include "inc/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_vu1_as_is_shared_defines.h" +#include "src/renderer/3d/pipeline/minecraft/programs/as_is/macros.i" + +#define STQ_STORE_OFFSET 0 +#define RGBA_STORE_OFFSET 1 +#define XYZ2_STORE_OFFSET 2 + +--enter +--endenter + +#vuprog VU1BlocksAsIs + + LoadStaticData{ lodTag, setTag } + +begin: + + xtop buffer + LoadDynamicData{ scale, primTag, clut, color, vertexCount, buffer } + + iaddiu vertexData, buffer, VU1_MCPIP_AS_IS_DYNAMIC_VERTEX_DATA_ADDR + iadd stqData, vertexData, vertexCount + iadd destAddress, stqData, vertexCount + iadd kickAddress, stqData, vertexCount + + StoreTags{ lodTag, setTag, primTag, clut, destAddress } + FixColor{ color } + + ;--- Loop + iadd vertexCounter, buffer, vertexCount +vertexLoop: + + ;--- Load vertex1 + lq vertex1, (vertexData) + lq stq1, (stqData) + + ;--- Load vertex2 + lq vertex2, 1(vertexData) + lq stq2, 1(stqData) + + ;--- Load vertex3 + lq vertex3, 2(vertexData) + lq stq3, 2(stqData) + + ;--- Calculate vertex1 + ScaleVertexToGSFormat{ scale, vertex1 } + div q, vf00[w], vertex1[w] + PerformTexturePerspectiveCorrection{ outputStq1, stq1 } + + ;--- Calculate vertex2 + ScaleVertexToGSFormat{ scale, vertex2 } + div q, vf00[w], vertex2[w] + PerformTexturePerspectiveCorrection{ outputStq2, stq2 } + + ;--- Calculate vertex3 + ScaleVertexToGSFormat{ scale, vertex3 } + div q, vf00[w], vertex3[w] + PerformTexturePerspectiveCorrection{ outputStq3, stq3 } + + ;--- Store vertex1 + sq outputStq1, STQ_STORE_OFFSET(destAddress) + sq color, RGBA_STORE_OFFSET(destAddress) + sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress) + + ;--- Store vertex2 + sq outputStq2, STQ_STORE_OFFSET+3(destAddress) + sq color, RGBA_STORE_OFFSET+3(destAddress) + sq.xyz vertex2, XYZ2_STORE_OFFSET+3(destAddress) + + ;--- Store vertex3 + sq outputStq3, STQ_STORE_OFFSET+6(destAddress) + sq color, RGBA_STORE_OFFSET+6(destAddress) + sq.xyz vertex3, XYZ2_STORE_OFFSET+6(destAddress) + + ;------------------------------- + + iaddiu vertexData, vertexData, 3 + iaddiu stqData, stqData, 3 + iaddiu destAddress, destAddress, 9 + + ;--- Fix loop + iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter + ibne vertexCounter, buffer, vertexLoop ; and repeat if needed + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it... +--cont + + b begin + +#endvuprog + +--exit +--endexit diff --git a/engine/src/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_as_is_vu1_program.cpp b/engine/src/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_as_is_vu1_program.cpp new file mode 100644 index 0000000..5d6c811 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_as_is_vu1_program.cpp @@ -0,0 +1,28 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/minecraft/programs/as_is/mcpip_as_is_vu1_program.hpp" + +extern u32 VU1BlocksAsIs_CodeStart __attribute__((section(".vudata"))); +extern u32 VU1BlocksAsIs_CodeEnd __attribute__((section(".vudata"))); + +namespace Tyra { + +McpipAsIsVU1Program::McpipAsIsVU1Program() + : McpipProgram(McpipProgramName::McPipAsIs, &VU1BlocksAsIs_CodeStart, + &VU1BlocksAsIs_CodeEnd) {} + +McpipAsIsVU1Program::~McpipAsIsVU1Program() {} + +std::string McpipAsIsVU1Program::getStringName() const { + return std::string("As is"); +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_clip.cpp b/engine/src/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_clip.cpp new file mode 100644 index 0000000..40746df --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/programs/as_is/mcpip_clip.cpp @@ -0,0 +1,162 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/minecraft/programs/as_is/mcpip_clip.hpp" + +namespace Tyra { + +McpipClip::McpipClip() { + staticPacket = packet2_create(8, P2_TYPE_NORMAL, P2_MODE_CHAIN, true); + algoSettings.lerpColors = false; + algoSettings.lerpTexCoords = true; + algoSettings.lerpNormals = false; +} + +McpipClip::~McpipClip() { packet2_free(staticPacket); } + +void McpipClip::init(RendererCore* core, McpipBlockData* t_singleBlockData, + McpipBlockData* t_multiBlockData) { + singleBlockData = t_singleBlockData; + multiBlockData = t_multiBlockData; + rendererCore = core; + algorithm.init(rendererCore->getSettings()); + setDBufferSize(); + initStaticPacket(); +} + +u32 McpipClip::uploadVU1Program(McpipProgramsRepository* repo, + const u32& addr) { + auto* program = repo->getProgram(McpipProgramName::McPipAsIs); + return rendererCore->renderer3D.uploadVU1Program(program, addr); +} + +void McpipClip::configureVU1AndSendStaticData() { + u16 start = VU1_MCPIP_AS_IS_STATIC_LAST_DATA_ADDR + 1; + rendererCore->renderer3D.setVU1DoubleBuffers(start, vu1DBufferSize); + sendVU1StaticData(); +} + +void McpipClip::initStaticPacket() { + packet2_utils_vu_open_unpack(staticPacket, VU1_MCPIP_AS_IS_STATIC_LOD, false); + { + packet2_utils_gs_add_lod(staticPacket, &rendererCore->gs.lod); + packet2_utils_gif_add_set(staticPacket, 1); + } + packet2_utils_vu_close_unpack(staticPacket); + + packet2_utils_vu_add_end_tag(staticPacket); +} + +void McpipClip::addData(McpipBlock* block, const bool& isMulti, + RendererCoreTextureBuffers* texBuffers, + packet2_t* packet, const u8& context) { + std::vector clippedVertices; + auto mvp = rendererCore->renderer3D.getViewProj() * block->model; + + const auto* blockData = isMulti ? multiBlockData : singleBlockData; + + for (u32 i = 0; i < blockData->count / 3; i++) { + for (u8 j = 0; j < 3; j++) { + Path1ClipVertex vert = {mvp * blockData->vertices[i * 3 + j], Vec4(), + blockData->textureCoords[i * 3 + j], Vec4()}; + + inputTriangle.push_back(vert); + } + + clippedTriangle.clear(); + + algorithm.clip(&clippedTriangle, inputTriangle, algoSettings); + + inputTriangle.clear(); + + if (clippedTriangle.size() == 0) continue; + + auto va = clippedTriangle.at(0); + for (u32 j = 1; j <= clippedTriangle.size() - 2; j++) { + auto vb = clippedTriangle.at(j); + auto vc = clippedTriangle.at((j + 1) % clippedTriangle.size()); + clippedVertices.push_back(va); + clippedVertices.push_back(vb); + clippedVertices.push_back(vc); + } + } + + addCorrections(&clippedVertices, block); + moveDataToBuffer(&clippedVertices, context); + addDataToPacket(packet, context, block, clippedVertices.size(), texBuffers); +} + +void McpipClip::addCorrections(std::vector* vertices, + McpipBlock* block) { + for (u32 i = 0; i < vertices->size(); i++) { + (*vertices)[i].position /= (*vertices)[i].position.w; // Perspective divide + (*vertices)[i].st += block->textureOffset; // Texture offset + } +} + +void McpipClip::moveDataToBuffer(std::vector* vertices, + const u8& context) { + for (u32 i = 0; i < vertices->size(); i++) { + vertexBuffers[context][i].set(vertices->at(i).position); + texCoordBuffers[context][i].set(vertices->at(i).st); + } +} + +void McpipClip::addDataToPacket(packet2_t* packet, const u8& context, + McpipBlock* block, const int& count, + RendererCoreTextureBuffers* texBuffers) { + packet2_reset(packet, false); + + rendererCore->texture.updateClutBuffer(texBuffers->clut); + + packet2_utils_vu_open_unpack(packet, VU1_MCPIP_AS_IS_DYNAMIC_SCALE, true); + { + packet2_add_float(packet, 2048.0F); // scale + packet2_add_float(packet, 2048.0F); // scale + packet2_add_float(packet, + static_cast(0xFFFFFF) / 32.0F); // scale + packet2_add_s32(packet, count); // vertex count + + packet2_utils_gs_add_prim_giftag(packet, &rendererCore->gs.prim, count, + ((u64)GIF_REG_ST) << 0 | + ((u64)GIF_REG_RGBAQ) << 4 | + ((u64)GIF_REG_XYZ2) << 8, + 3, 0); + + packet2_utils_gs_add_texbuff_clut(packet, texBuffers->core, + &rendererCore->texture.clut); + + Packet2TyraUtils::addColor(packet, block->color); + } + packet2_utils_vu_close_unpack(packet); + + u32 addr = VU1_MCPIP_AS_IS_DYNAMIC_VERTEX_DATA_ADDR; + + packet2_utils_vu_add_unpack_data(packet, addr, vertexBuffers[context], count, + true); + addr += count; + + packet2_utils_vu_add_unpack_data(packet, addr, texCoordBuffers[context], + count, true); +} + +void McpipClip::setDBufferSize() { + vu1DBufferSize = 1000; // VU1 mem size + vu1DBufferSize -= VU1_MCPIP_AS_IS_STATIC_LAST_DATA_ADDR; // static data + vu1DBufferSize -= 1; + vu1DBufferSize /= 2; // xtop double buffer +} + +void McpipClip::sendVU1StaticData() { + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + dma_channel_send_packet2(staticPacket, DMA_CHANNEL_VIF1, true); +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/minecraft/programs/cull/macros.i b/engine/src/renderer/3d/pipeline/minecraft/programs/cull/macros.i new file mode 100644 index 0000000..ccb8bb2 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/programs/cull/macros.i @@ -0,0 +1,93 @@ +;//-------------------------------------------------------------------------------- +;// MinecraftPipeline cull macros library +;//-------------------------------------------------------------------------------- + +;//--------------------------------------------------------- +;// StoreBlockizerStaticData - Load and store lod, clut and prim. +;// Push dest address by 5 qwords +;//--------------------------------------------------------- +#macro StoreBlockizerLodClutTexPrim: t_destAddr, t_clutTex + lq setTag, VU1_MCPIP_CULL_STATIC_SET_TAG(vi00) + lq lodTag, VU1_MCPIP_CULL_STATIC_LOD(vi00) + sq setTag, 0(t_destAddr) + sq lodTag, 1(t_destAddr) + sq setTag, 2(t_destAddr) + sq t_clutTex, 3(t_destAddr) + lq primTag, VU1_MCPIP_CULL_STATIC_PRIM(vi00) + sq primTag, 4(t_destAddr) + iaddiu t_destAddr, t_destAddr, 5 +#endmacro + +;//--------------------------------------------------------- +;// GetDestinationAddress - Load VU1 options and check +;// in which double buffer we are. If 0, return first double buff +;// if 1, return second double buffer. +;// Also toggle VU1 dbuff option, so next call of this macro will result +;// opposite buffer. +;//--------------------------------------------------------- +#macro GetDestinationAddress: t_destAddr, t_kickAddr + ilw.w static1, VU1_MCPIP_CULL_STATIC_VU1_OPTIONS(vi00) + ilw.x currDBufferOffset, VU1_MCPIP_CULL_STATIC_VU1_OPTIONS(vi00) + ibgtz currDBufferOffset, get_dest_addr_second + + get_dest_addr_first: + iaddiu t_destAddr, vi00, VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF1_ADDR + isw.x static1, VU1_MCPIP_CULL_STATIC_VU1_OPTIONS(vi00) + b get_dest_addr_finish + + get_dest_addr_second: + iaddiu t_destAddr, vi00, VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF2_ADDR + isw.x vi00, VU1_MCPIP_CULL_STATIC_VU1_OPTIONS(vi00) + + get_dest_addr_finish: + iaddiu t_kickAddr, t_destAddr, 0 +#endmacro + +;//--------------------------------------------------------- +;// LoadScaleAndBlocksCount - Load scale vec3 and blocks count +;// from w component +;//--------------------------------------------------------- +#macro LoadBufferDynamicData: t_buffer, t_scale, t_clutTex, t_blocksCount, t_viewProj + lq.xyz t_scale, VU1_MCPIP_CULL_DYNAMIC_SCALE_AND_BLOCKS_COUNT_ADDR(t_buffer) + ilw.w t_blocksCount, VU1_MCPIP_CULL_DYNAMIC_SCALE_AND_BLOCKS_COUNT_ADDR(t_buffer) + lq t_clutTex, VU1_MCPIP_CULL_DYNAMIC_CLUT_TEX(t_buffer) + lq t_viewProj[0], 0+VU1_MCPIP_CULL_DYNAMIC_VIEW_PROJ_MATRIX_ADDR(t_buffer) + lq t_viewProj[1], 1+VU1_MCPIP_CULL_DYNAMIC_VIEW_PROJ_MATRIX_ADDR(t_buffer) + lq t_viewProj[2], 2+VU1_MCPIP_CULL_DYNAMIC_VIEW_PROJ_MATRIX_ADDR(t_buffer) + lq t_viewProj[3], 3+VU1_MCPIP_CULL_DYNAMIC_VIEW_PROJ_MATRIX_ADDR(t_buffer) +#endmacro + +;//--------------------------------------------------------- +;// GetVertAndStData - Get static vertex data and tex coords +;//--------------------------------------------------------- +#macro GetVertAndStData: t_vertData, t_stData + iaddiu t_vertData, vi00, VU1_MCPIP_CULL_STATIC_VERTEX_DATA + iaddiu t_stData, vi00, VU1_MCPIP_CULL_STATIC_TEX_COORD_DATA +#endmacro + +;//--------------------------------------------------------- +;// GetBlockData - Get mvp matrix, color, st offset for single block +;//--------------------------------------------------------- +#macro GetBlockData: t_blockData, t_model, t_color, t_stOffset + lq t_model[0], 0(t_blockData) + lq t_model[1], 1(t_blockData) + lq t_model[2], 2(t_blockData) + lq t_model[3], 3(t_blockData) + lq t_color, 4(t_blockData) + lq t_stOffset, 5(t_blockData) +#endmacro + +;//--------------------------------------------------------- +;// GetVertexData - Get vertex and tex coord +;//--------------------------------------------------------- +#macro GetVertexData: t_vertData, t_stqData, t_vertex, t_stq, t_offset + lq t_vertex, t_offset(t_vertData) + lq t_stq, t_offset(t_stqData) +#endmacro + +;//--------------------------------------------------------- +;// GetVertexData - Get vertex and tex coord +;//--------------------------------------------------------- +#macro AddSTOffset: t_stq, t_stqOffset + add t_stq, t_stq, t_stqOffset +#endmacro diff --git a/engine/src/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull.cpp b/engine/src/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull.cpp new file mode 100644 index 0000000..e3c3fe4 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull.cpp @@ -0,0 +1,139 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull.hpp" + +namespace Tyra { + +McpipCull::McpipCull() { + staticPacket = packet2_create(8, P2_TYPE_NORMAL, P2_MODE_CHAIN, true); +} + +McpipCull::~McpipCull() { packet2_free(staticPacket); } + +void McpipCull::init(RendererCore* core, McpipBlockData* t_blockData) { + blockData = t_blockData; + rendererCore = core; + initStaticPacket(); + setDBufferSize(); +} + +void McpipCull::setDBufferSize() { + vu1DBufferSize = 1000; // VU1 mem size + vu1DBufferSize -= VU1_MCPIP_CULL_STATIC_LAST_DATA_ADDR; // static data + + // Our additional, mini output double buffer at the bottom of VU1 mem + u16 miniOutputDoubleBufferSize = + blockData->count * 3 + + 5; // 36 * (ST, XYZ2, RGBAQ) + Set, lod, set, clut, prim + + vu1DBufferSize -= miniOutputDoubleBufferSize; // First + vu1DBufferSize -= miniOutputDoubleBufferSize; // Second + + TYRA_ASSERT(1000 - (miniOutputDoubleBufferSize * 2) == + VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF1_ADDR, + "There is mismatch in output dbuffer1 size. Should be: ", + 1000 - (miniOutputDoubleBufferSize * 2), + "but is: ", VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF1_ADDR); + + TYRA_ASSERT(1000 - miniOutputDoubleBufferSize == + VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF2_ADDR, + "There is mismatch in output dbuffer2 size. Should be: ", + 1000 - miniOutputDoubleBufferSize, + "but is: ", VU1_MCPIP_CULL_DYNAMIC_OUTPUT_DOUBLE_BUFF2_ADDR); + + vu1DBufferSize /= 2; // xtop double buffer +} + +u32 McpipCull::uploadVU1Program(McpipProgramsRepository* repo, + const u32& addr) { + auto* program = repo->getProgram(McpipProgramName::McPipCull); + return rendererCore->renderer3D.uploadVU1Program(program, addr); +} + +void McpipCull::configureVU1AndSendStaticData() { + rendererCore->renderer3D.setVU1DoubleBuffers( + VU1_MCPIP_CULL_STATIC_LAST_DATA_ADDR, vu1DBufferSize); + + sendVU1StaticData(); +} + +u32 McpipCull::getMaxBlocksCountPerQBuffer() const { + u32 result = vu1DBufferSize; + result -= 1; // VU1_MCPIP_CULL_DYNAMIC_SCALE_AND_BLOCKS_COUNT; + result -= 1; // Lod tag + result -= 4; // View proj + + result /= VU1_MCPIP_CULL_QWORDS_PER_BLOCK; + + return result; +} + +void McpipCull::initStaticPacket() { + packet2_utils_vu_open_unpack(staticPacket, VU1_MCPIP_CULL_STATIC_LOD, false); + { + packet2_utils_gs_add_lod(staticPacket, &rendererCore->gs.lod); + + packet2_utils_gs_add_prim_giftag(staticPacket, &rendererCore->gs.prim, 36, + ((u64)GIF_REG_ST) << 0 | + ((u64)GIF_REG_RGBAQ) << 4 | + ((u64)GIF_REG_XYZ2) << 8, + 3, 0); + packet2_utils_gif_add_set(staticPacket, 1); + + packet2_add_u32(staticPacket, 0); // Param1 - Mini double buffer switcher + packet2_add_u32(staticPacket, 0); + packet2_add_u32(staticPacket, 0); + packet2_add_u32(staticPacket, + 1); // static 1 - used in VU1's GetDestinationAddress{} + } + packet2_utils_vu_close_unpack(staticPacket); + + packet2_utils_vu_add_end_tag(staticPacket); +} + +void McpipCull::sendVU1StaticData() { + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + dma_channel_send_packet2(staticPacket, DMA_CHANNEL_VIF1, true); +} + +void McpipCull::addData(packet2_t* packet, McpipBlock** blockPointerArray, + u32 blockPointerArrayCount, + RendererCoreTextureBuffers* texBuffers, bool isMulti) { + packet2_reset(packet, false); + + rendererCore->texture.updateClutBuffer(texBuffers->clut); + + packet2_utils_vu_open_unpack( + packet, VU1_MCPIP_CULL_DYNAMIC_SCALE_AND_BLOCKS_COUNT_ADDR, true); + { + packet2_add_float(packet, 2048.0F); // scale + packet2_add_float(packet, 2048.0F); // scale + packet2_add_float(packet, + static_cast(0xFFFFFF) / 32.0F); // scale + packet2_add_u32(packet, blockPointerArrayCount); // blocks count + + packet2_utils_gs_add_texbuff_clut(packet, texBuffers->core, + &rendererCore->texture.clut); + + Packet2TyraUtils::addM4x4(packet, rendererCore->renderer3D.getViewProj()); + } + packet2_utils_vu_close_unpack(packet); + + u32 addr = VU1_MCPIP_CULL_DYNAMIC_BLOCKS_DATA; + + for (u32 i = 0; i < blockPointerArrayCount; i++) { + packet2_utils_vu_add_unpack_data(packet, addr, blockPointerArray[i], + VU1_MCPIP_CULL_QWORDS_PER_BLOCK, true); + addr += VU1_MCPIP_CULL_QWORDS_PER_BLOCK; + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull_vu1.vclpp b/engine/src/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull_vu1.vclpp new file mode 100644 index 0000000..ea42007 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull_vu1.vclpp @@ -0,0 +1,129 @@ +; ______ ____ ___ +; | \/ ____| |___| +; | | | \ | | +;--------------------------------------------------------------- +; Copyright 2022, tyra - https://github.com/h4570/tyra +; Licenced under Apache License 2.0 +; Sandro Sobczyński +; +;--------------------------------------------------------------- +; Super fast renderer of minecraft blocks. +; Block data is statically allocated in vi00 +; +; - Triangle list +; - Cull = transform +; - Colors +;--------------------------------------------------------------- + +.syntax new +.name VU1BlocksCull +.vu +.init_vf_all +.init_vi_all + +#include "src/renderer/core/paths/path1/programs/vcl_sml.i" +#include "inc/renderer/3d/pipeline/minecraft/programs/cull/mcpip_vu1_cull_shared_defines.h" +#include "src/renderer/3d/pipeline/minecraft/programs/cull/macros.i" + +#define STQ_STORE_OFFSET 0 +#define RGBA_STORE_OFFSET 1 +#define XYZ2_STORE_OFFSET 2 + +--enter +--endenter + +#vuprog VU1BlocksCull + +GetVertAndStData{ staticVertexData, staticStqData } +ResetClipFlags{ } + +begin: + +xtop buffer +iaddiu blockData, buffer, VU1_MCPIP_CULL_DYNAMIC_BLOCKS_DATA +LoadBufferDynamicData{ buffer, scale, clutTex, blocksCount, viewProj } + +;--- Loop +iadd blockCounter, buffer, blocksCount + +blocksLoop: + + GetBlockData{ blockData, model, color, stOffset } + MatrixMultiply{ mvp, model, viewProj } + FixColor{ color } + GetDestinationAddress{ destAddress, kickAddress } + StoreBlockizerLodClutTexPrim{ destAddress, clutTex } + iaddiu vertexData, staticVertexData, 0 + iaddiu stqData, staticStqData, 0 + + iaddiu vertexCounter, buffer, VU1_MCPIP_CULL_VERTEX_COUNT +vertexLoop: + + GetVertexData{ vertexData, stqData, vertex1, stq1, 0 } + GetVertexData{ vertexData, stqData, vertex2, stq2, 1 } + GetVertexData{ vertexData, stqData, vertex3, stq3, 2 } + + ;--- Calculate vertex1 + MatrixMultiplyVertex{ vertex1, mvp, vertex1 } + PerformClipCheck{ vertex1, destAddress, XYZ2_STORE_OFFSET } + VertexPersCorr{ vertex1, vertex1 } + ScaleVertexToGSFormat{ scale, vertex1 } + AddSTOffset{ stq1, stOffset } + PerformTexturePerspectiveCorrection{ outputStq1, stq1 } + + ;--- Calculate vertex2 + MatrixMultiplyVertex{ vertex2, mvp, vertex2 } + PerformClipCheck{ vertex2, destAddress, XYZ2_STORE_OFFSET+3 } + VertexPersCorr{ vertex2, vertex2 } + ScaleVertexToGSFormat{ scale, vertex2 } + AddSTOffset{ stq2, stOffset } + PerformTexturePerspectiveCorrection{ outputStq2, stq2 } + + ;--- Calculate vertex3 + MatrixMultiplyVertex{ vertex3, mvp, vertex3 } + PerformClipCheck{ vertex3, destAddress, XYZ2_STORE_OFFSET+6 } + VertexPersCorr{ vertex3, vertex3 } + ScaleVertexToGSFormat{ scale, vertex3 } + AddSTOffset{ stq3, stOffset } + PerformTexturePerspectiveCorrection{ outputStq3, stq3 } + + ;--- Store vertex1 + sq outputStq1, STQ_STORE_OFFSET(destAddress) + sq color, RGBA_STORE_OFFSET(destAddress) + sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress) + + ;--- Store vertex2 + sq outputStq2, STQ_STORE_OFFSET+3(destAddress) + sq color, RGBA_STORE_OFFSET+3(destAddress) + sq.xyz vertex2, XYZ2_STORE_OFFSET+3(destAddress) + + ;--- Store vertex3 + sq outputStq3, STQ_STORE_OFFSET+6(destAddress) + sq color, RGBA_STORE_OFFSET+6(destAddress) + sq.xyz vertex3, XYZ2_STORE_OFFSET+6(destAddress) + + iaddiu vertexData, vertexData, 3 + iaddiu stqData, stqData, 3 + iaddiu destAddress, destAddress, 9 + + iaddi vertexCounter, vertexCounter, -3 + ibne vertexCounter, buffer, vertexLoop + ; End of vertex loop + +--barrier + + xgkick kickAddress + + iaddiu blockData, blockData, VU1_MCPIP_CULL_QWORDS_PER_BLOCK + iaddi blockCounter, blockCounter, -1 + ibne blockCounter, buffer, blocksLoop + ; End of block loop + +--cont + +b begin + +#endvuprog + +--exit +--endexit diff --git a/engine/src/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull_vu1_program.cpp b/engine/src/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull_vu1_program.cpp new file mode 100644 index 0000000..3ec49c9 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull_vu1_program.cpp @@ -0,0 +1,28 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/minecraft/programs/cull/mcpip_cull_vu1_program.hpp" + +extern u32 VU1BlocksCull_CodeStart __attribute__((section(".vudata"))); +extern u32 VU1BlocksCull_CodeEnd __attribute__((section(".vudata"))); + +namespace Tyra { + +McpipCullVU1Program::McpipCullVU1Program() + : McpipProgram(McpipProgramName::McPipCull, &VU1BlocksCull_CodeStart, + &VU1BlocksCull_CodeEnd) {} + +McpipCullVU1Program::~McpipCullVU1Program() {} + +std::string McpipCullVU1Program::getStringName() const { + return std::string("Cull"); +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/minecraft/programs/mcpip_program.cpp b/engine/src/renderer/3d/pipeline/minecraft/programs/mcpip_program.cpp new file mode 100644 index 0000000..c061b4a --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/programs/mcpip_program.cpp @@ -0,0 +1,23 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/minecraft/programs/mcpip_program.hpp" + +namespace Tyra { + +McpipProgram::McpipProgram(const McpipProgramName& t_name, u32* t_start, + u32* t_end) + : VU1Program(t_start, t_end), name(t_name) {} + +McpipProgram::~McpipProgram() {} + +const McpipProgramName& McpipProgram::getName() const { return name; } + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/minecraft/programs/mcpip_programs_manager.cpp b/engine/src/renderer/3d/pipeline/minecraft/programs/mcpip_programs_manager.cpp new file mode 100644 index 0000000..aa89c39 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/programs/mcpip_programs_manager.cpp @@ -0,0 +1,130 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/minecraft/programs/mcpip_programs_manager.hpp" + +namespace Tyra { + +BlockizerProgramsManager::BlockizerProgramsManager() { + lastProgramName = UndefinedMcpipProgram; + context = 0; + vu1BlockData = BlockNotUploaded; + dynamicPackets[0] = packet2_create(100, P2_TYPE_NORMAL, P2_MODE_CHAIN, true); + dynamicPackets[1] = packet2_create(100, P2_TYPE_NORMAL, P2_MODE_CHAIN, true); + staticPacket = packet2_create(2, P2_TYPE_NORMAL, P2_MODE_CHAIN, true); + setProgramsCache(); +} + +BlockizerProgramsManager::~BlockizerProgramsManager() { + packet2_free(dynamicPackets[0]); + packet2_free(dynamicPackets[1]); + packet2_free(staticPacket); + packet2_free(programsPacket); +} + +void BlockizerProgramsManager::init(RendererCore* core) { + culler.init(core, &singleTexBlockData); + clipper.init(core, &singleTexBlockData, &multiTexBlockData); +} + +void BlockizerProgramsManager::setProgramsCache() { + VU1Program** programs = new VU1Program*[2]; + programs[0] = repo.getProgram(McpipProgramName::McPipCull); + programs[1] = repo.getProgram(McpipProgramName::McPipAsIs); + programsPacket = + renderer->core.getPath1()->createProgramsCache(programs, 2, 0); + delete[] programs; +} + +void BlockizerProgramsManager::uploadVU1Programs() { + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + dma_channel_send_packet2(programsPacket, DMA_CHANNEL_VIF1, true); + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + lastProgramName = UndefinedMcpipProgram; + vu1BlockData = BlockNotUploaded; +} + +void BlockizerProgramsManager::uploadBlock(bool isMulti) { + if (vu1BlockData != BlockNotUploaded) { + if (isMulti && vu1BlockData == BlockMultiUploaded) { + return; + } + } else { + if (!isMulti && vu1BlockData == BlockSingleUploaded) { + return; + } + } + + packet2_reset(staticPacket, false); + + const McpipBlockData& blockData = + isMulti ? static_cast(multiTexBlockData) + : static_cast(singleTexBlockData); + + packet2_utils_vu_add_unpack_data( + staticPacket, VU1_MCPIP_CULL_STATIC_VERTEX_DATA, blockData.comboData, + blockData.getComboCount(), false); + + packet2_utils_vu_add_end_tag(staticPacket); + + vu1BlockData = isMulti ? BlockMultiUploaded : BlockSingleUploaded; + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + dma_channel_send_packet2(staticPacket, DMA_CHANNEL_VIF1, true); +} + +void BlockizerProgramsManager::cull(McpipBlock** blockPointerArray, + u32 blockPointerArrayCount, + RendererCoreTextureBuffers* texBuffers, + const bool& isMulti) { + uploadBlock(isMulti); + + auto* currentPacket = dynamicPackets[context]; + + auto* program = repo.getProgram(McpipProgramName::McPipCull); + + culler.addData(currentPacket, blockPointerArray, blockPointerArrayCount, + texBuffers, isMulti); + + sendPacket(program); +} + +void BlockizerProgramsManager::clip(McpipBlock* block, + RendererCoreTextureBuffers* texBuffers, + const bool& isMulti) { + vu1BlockData = BlockNotUploaded; + + auto* currentPacket = dynamicPackets[context]; + + auto* program = repo.getProgram(McpipProgramName::McPipAsIs); + + clipper.addData(block, isMulti, texBuffers, currentPacket, context); + + sendPacket(program); +} + +void BlockizerProgramsManager::sendPacket(McpipProgram* program) { + auto* currentPacket = dynamicPackets[context]; + + if (lastProgramName != program->getName()) { + packet2_utils_vu_add_start_program(currentPacket, + program->getDestinationAddress()); + lastProgramName = program->getName(); + } else { + packet2_utils_vu_add_continue_program(currentPacket); + } + + packet2_utils_vu_add_end_tag(currentPacket); + + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + dma_channel_send_packet2(currentPacket, DMA_CHANNEL_VIF1, true); + context = !context; +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/minecraft/programs/mcpip_programs_repository.cpp b/engine/src/renderer/3d/pipeline/minecraft/programs/mcpip_programs_repository.cpp new file mode 100644 index 0000000..f09a088 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/minecraft/programs/mcpip_programs_repository.cpp @@ -0,0 +1,33 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/minecraft/programs/mcpip_programs_repository.hpp" + +namespace Tyra { + +McpipProgramsRepository::McpipProgramsRepository() {} + +McpipProgramsRepository::~McpipProgramsRepository() {} + +McpipProgram* McpipProgramsRepository::getProgram( + const McpipProgramName& name) { + switch (name) { + case McpipProgramName::McPipCull: + return &cull; + case McpipProgramName::McPipAsIs: + return &asIs; + + default: + TYRA_TRAP("Unknown VU1 program name"); + return &cull; + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_package.cpp b/engine/src/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_package.cpp new file mode 100644 index 0000000..f79a60f --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_package.cpp @@ -0,0 +1,89 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include +#include +#include "renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_package.hpp" + +namespace Tyra { + +StdpipBagPackage::StdpipBagPackage() { + size = 0; + bag = nullptr; + vertices = nullptr; + sts = nullptr; + normals = nullptr; + colors = nullptr; +} +StdpipBagPackage::~StdpipBagPackage() {} + +void StdpipBagPackage::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void StdpipBagPackage::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string StdpipBagPackage::getPrint(const char* name) const { + std::stringstream res; + if (name) { + res << name << "("; + } else { + res << "StdpipBagPackage("; + } + res << std::fixed << std::setprecision(2); + res << std::endl; + res << "Size: " << static_cast(size) << std::endl; + + res << "Vectors: " << std::endl; + for (u32 i = 0; i < size; i++) { + res << i << ": " << vertices[i].getPrint() << std::endl; + } + + if (sts != nullptr) { + res << "STs: " << std::endl; + for (u32 i = 0; i < size; i++) + res << i << ": " << sts[i].getPrint() << std::endl; + } + + if (colors != nullptr) { + res << "Colors: " << std::endl; + for (u32 i = 0; i < size; i++) + res << i << ": " << colors[i].getPrint() << std::endl; + } + + if (normals != nullptr) { + res << "Normals: " << std::endl; + for (u32 i = 0; i < size; i++) + res << i << ": " << normals[i].getPrint() << std::endl; + } + + res << "Is in frustum: "; + switch (isInFrustum) { + case CoreBBoxFrustum::IN_FRUSTUM: + res << "Yes"; + break; + case CoreBBoxFrustum::OUTSIDE_FRUSTUM: + res << "No"; + break; + case CoreBBoxFrustum::PARTIALLY_IN_FRUSTUM: + res << "Partially"; + break; + } + + res << ")"; + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packager.cpp b/engine/src/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packager.cpp new file mode 100644 index 0000000..9d8e169 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packager.cpp @@ -0,0 +1,119 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include +#include "debug/debug.hpp" +#include "renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packager.hpp" + +namespace Tyra { + +StdpipBagPackager::StdpipBagPackager() {} +StdpipBagPackager::~StdpipBagPackager() {} + +void StdpipBagPackager::init(Renderer3DFrustumPlanes* t_frustumPlanes) { + frustumPlanes = t_frustumPlanes; +} + +/** + * @brief Create render packages from provided render data + * + * @param size Max maxVertCount verts (VU1 buffer size) + */ +StdpipBagPackage* StdpipBagPackager::create(u16* o_size, StdpipBag* data, + u16 size) { + TYRA_ASSERT(size <= maxVertCount, "StdpipBagPackage can have max ", + maxVertCount, " verts. Provided \"", size, "\""); + + *o_size = ceil(data->count / static_cast(size)); + StdpipBagPackage* result = new StdpipBagPackage[*o_size]; + + for (u16 i = 0; i < *o_size; i++) { + result[i].bag = data; + result[i].vertices = &data->vertices[i * size]; + + if (data->texture) result[i].sts = &data->texture->coordinates[i * size]; + + if (data->color->many) + result[i].colors = reinterpret_cast(&data->color->many[i * size]); + + if (data->lighting) result[i].normals = &data->lighting->normals[i * size]; + + result[i].indexOf1By3BBox = (i * size) / (maxVertCount / 3); + + if (i == *o_size - 1) { + result[i].size = data->count - i * size; + } else { + result[i].size = size; + } + + result[i].isInFrustum = checkFrustum(result[i]); + } + + return result; +} + +/** + * @brief Split render package to smaller packages + * + * @param size Max maxVertCount verts (VU1 buffer size) + */ +StdpipBagPackage* StdpipBagPackager::create(u16* o_count, + const StdpipBagPackage& pkg, + u16 size) { + TYRA_ASSERT(size <= maxVertCount, "StdpipBagPackage can have max ", + maxVertCount, " verts. Provided \"", size, "\""); + + *o_count = ceil(pkg.size / static_cast(size)); + auto* result = new StdpipBagPackage[*o_count]; + + for (u16 i = 0; i < *o_count; i++) { + result[i].bag = pkg.bag; + result[i].vertices = &pkg.vertices[i * size]; + + if (pkg.bag->texture) result[i].sts = &pkg.sts[i * size]; + + if (pkg.bag->color->many) result[i].colors = &pkg.colors[i * size]; + + if (pkg.bag->lighting) result[i].normals = &pkg.normals[i * size]; + + result[i].indexOf1By3BBox = + pkg.indexOf1By3BBox + ((i * size) / (maxVertCount / 3)); + + if (i == *o_count - 1) { + result[i].size = pkg.size - i * size; + } else { + result[i].size = size; + } + + result[i].isInFrustum = checkFrustum(result[i]); + } + + return result; +} + +CoreBBoxFrustum StdpipBagPackager::checkFrustum(const StdpipBagPackage& pkg) { + if (pkg.size <= (maxVertCount / 3)) { // Is subpackage + auto& bbox = renderBBox->getChildBBox1By3(pkg.indexOf1By3BBox); + return bbox.clipIsInFrustum(frustumPlanes->getAll(), *pkg.bag->info->model); + } else { // Is package + auto bbox = renderBBox->createChildBBox( + pkg.indexOf1By3BBox, + ceil(pkg.size / static_cast(maxVertCount / 3))); + + return bbox.clipIsInFrustum(frustumPlanes->getAll(), *pkg.bag->info->model); + } +} + +void StdpipBagPackager::setMaxVertCount(const u32& count) { + maxVertCount = count; +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packages_bbox.cpp b/engine/src/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packages_bbox.cpp new file mode 100644 index 0000000..f08f25f --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packages_bbox.cpp @@ -0,0 +1,113 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include +#include +#include +#include "debug/debug.hpp" +#include "renderer/3d/pipeline/std/core/bag/packaging/stdpip_bag_packages_bbox.hpp" + +namespace Tyra { + +StdpipBagPackagesBBox::StdpipBagPackagesBBox(Vec4* t_vertices, u32* t_faces, + const u32& t_facesCount, + const u32& t_maxVertCount) { + u32 splitPartSize = t_maxVertCount / 3; + vertexCount = t_facesCount; + partsCount = ceil(vertexCount / static_cast(splitPartSize)); + + bboxParts = new std::vector; + for (u32 i = 0; i < partsCount; i++) { + u32 partSize = + i == partsCount - 1 ? t_facesCount - i * splitPartSize : splitPartSize; + bboxParts->push_back( + CoreBBox(t_vertices, t_faces + i * splitPartSize, partSize)); + } + mainBBox = new RenderBBox(*bboxParts, 0, partsCount); +} + +StdpipBagPackagesBBox::StdpipBagPackagesBBox(Vec4* t_vertices, + const u32& t_count, + const u32& t_maxVertCount) { + u32 splitPartSize = t_maxVertCount / 3; + vertexCount = t_count; + partsCount = ceil(vertexCount / static_cast(splitPartSize)); + + bboxParts = new std::vector; + for (u32 i = 0; i < partsCount; i++) { + u32 partSize = + i == partsCount - 1 ? t_count - i * splitPartSize : splitPartSize; + bboxParts->push_back(RenderBBox(t_vertices + i * splitPartSize, partSize)); + } + + mainBBox = new RenderBBox(*bboxParts, 0, partsCount); +} + +const RenderBBox& StdpipBagPackagesBBox::getChildBBox1By3( + const u32& index) const { + TYRA_ASSERT(index < partsCount, + "Index out of range. Provided index: ", index); + return static_cast(bboxParts->at(index)); +} + +RenderBBox* StdpipBagPackagesBBox::getMainBBox() { return mainBBox; } + +const u32& StdpipBagPackagesBBox::getPartsCount() const { return partsCount; } + +const u32& StdpipBagPackagesBBox::getVertexCount() const { return vertexCount; } + +RenderBBox StdpipBagPackagesBBox::createChildBBox(const u32& index, + const u16& partsSize) const { + return RenderBBox(*bboxParts, index, partsSize); +} + +void StdpipBagPackagesBBox::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void StdpipBagPackagesBBox::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string StdpipBagPackagesBBox::getPrint(const char* name) const { + std::stringstream res; + if (name) { + res << name << "("; + } else { + res << "StdpipBagPackagesBBox("; + } + res << std::fixed << std::setprecision(2); + res << std::endl; + res << "Vertices count: " << static_cast(vertexCount) << std::endl; + + res << "Main CoreBBox: " << std::endl; + res << mainBBox->getPrint() << std::endl; + + res << "Child BBoxes: " << std::endl; + for (u32 i = 0; i < partsCount; i++) { + res << i << ": " << bboxParts->at(i).getPrint(); + if (i != partsCount - 1) { + res << std::endl; + } + } + + res << ")"; + return res.str(); +} + +StdpipBagPackagesBBox::~StdpipBagPackagesBBox() { + delete bboxParts; + delete mainBBox; +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_bag.cpp b/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_bag.cpp new file mode 100644 index 0000000..16c3f9b --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_bag.cpp @@ -0,0 +1,84 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include "renderer/3d/pipeline/std/core/bag/stdpip_bag.hpp" + +namespace Tyra { + +StdpipBag::StdpipBag() { + info = nullptr; + color = nullptr; + texture = nullptr; + lighting = nullptr; +} + +StdpipBag::~StdpipBag() {} + +StdpipBagPackagesBBox StdpipBag::calculateBbox(const u32& maxVertCount) { + TYRA_ASSERT(vertices != nullptr, "Vertices are required to calculate bbox"); + TYRA_ASSERT(count > 0, "Count must be greater than 0 to calculate bbox"); + return StdpipBagPackagesBBox(vertices, count, maxVertCount); +} + +void StdpipBag::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void StdpipBag::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string StdpipBag::getPrint(const char* name) const { + std::stringstream res; + if (name) { + res << name << "("; + } else { + res << "StdpipBag("; + } + res << std::fixed << std::setprecision(4); + res << std::endl; + res << "Count: " << count << ", " << std::endl; + res << "Vertices present: " << (vertices != nullptr ? "Yes" : "No") << ", " + << std::endl; + res << "Info present: " << (info != nullptr ? "Yes" : "No") << ", " + << std::endl; + res << "Color present: " << (color != nullptr ? "Yes" : "No") << ", " + << std::endl; + res << "Texture present: " << (texture != nullptr ? "Yes" : "No") << ", " + << std::endl; + res << "Lighting present: " << (lighting != nullptr ? "Yes" : "No") << ", " + << std::endl; + res << "Model matrix: " << info->model->getPrint() << ", " << std::endl; + if (color->single) { + res << "Color single: " << color->single->getPrint() << ", " << std::endl; + } else { + res << "Color many: " << color->many->getPrint() << ", " << std::endl; + } + if (texture) { + res << "Texture coords present: " + << (texture->coordinates != nullptr ? "Yes" : "No") << ", " + << std::endl; + res << "Texture: " << texture->texture->getPrint() << ", " << std::endl; + } + if (lighting) { + res << "Lighting normals present: " << (lighting->normals ? "Yes" : "No") + << ", " << std::endl; + res << "Lighting matrix: " << lighting->lightMatrix->getPrint() << ", " + << std::endl; + } + res << ")"; + + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_color_bag.cpp b/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_color_bag.cpp new file mode 100644 index 0000000..2d50dbc --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_color_bag.cpp @@ -0,0 +1,22 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/std/core/bag/stdpip_color_bag.hpp" + +namespace Tyra { + +StdpipColorBag::StdpipColorBag() { + single = nullptr; + many = nullptr; +} + +StdpipColorBag::~StdpipColorBag() {} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_info_bag.cpp b/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_info_bag.cpp new file mode 100644 index 0000000..64d5640 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_info_bag.cpp @@ -0,0 +1,24 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/std/core/bag/stdpip_info_bag.hpp" + +namespace Tyra { + +StdpipInfoBag::StdpipInfoBag() { + shadingType = StdpipShadingFlat; + blendingEnabled = true; + antiAliasingEnabled = false; + model = nullptr; +} + +StdpipInfoBag::~StdpipInfoBag() {} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_lighting_bag.cpp b/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_lighting_bag.cpp new file mode 100644 index 0000000..68dd97a --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_lighting_bag.cpp @@ -0,0 +1,122 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include "renderer/3d/pipeline/std/core/bag/stdpip_lighting_bag.hpp" + +namespace Tyra { + +StdpipLightingBag::StdpipLightingBag(const bool& manual) { + isAllocated = false; + mode = Auto; + normals = nullptr; + lightMatrix = nullptr; + lightColors = nullptr; + lightDirections = nullptr; + if (manual) { + mode = Manual; + } else { + allocate(); + } +} + +StdpipLightingBag::~StdpipLightingBag() { deallocate(); } + +void StdpipLightingBag::setAmbientColor(const Color& color) { + TYRA_ASSERT(mode != Manual, "Ambient color cannot be set in manual mode"); + lightColors[3].set(reinterpret_cast(color)); +} + +void StdpipLightingBag::setDirectionalLightColors(Color* colors, + const u8& count) { + for (u8 i = 0; i < count; i++) setDirectionalLightColor(colors[i], i); +} + +void StdpipLightingBag::setDirectionalLightDirections(Vec4* directions, + const u8& count) { + for (u8 i = 0; i < count; i++) setDirectionalLightDirection(directions[i], i); +} + +void StdpipLightingBag::setDirectionalLightColor(const Color& color, + const u8& index) { + TYRA_ASSERT(mode != Manual, + "Directional lights cannot be set in manual mode"); + TYRA_ASSERT(index < 3, "There are max 3 directional lights"); + lightColors[index].set(reinterpret_cast(color)); +} + +void StdpipLightingBag::setDirectionalLightDirection(const Vec4& direction, + const u8& index) { + TYRA_ASSERT(mode != Manual, + "Directional lights cannot be set in manual mode"); + TYRA_ASSERT(index < 3, "There are max 3 directional lights"); + + lightDirections[index].set(direction); +} + +void StdpipLightingBag::setLightsManually(Vec4* colors, Vec4* directions) { + deallocate(); + lightColors = colors; + lightDirections = directions; + mode = Manual; +} + +void StdpipLightingBag::disableManualMode() { + allocate(); + mode = Auto; +} + +void StdpipLightingBag::allocate() { + if (isAllocated) return; + + lightColors = new Vec4[4]; + lightDirections = new Vec4[3]; + + for (u8 i = 0; i < 3; i++) { + lightColors[i].set(0.0F, 0.0F, 0.0F, 1.0F); + } + + lightColors[3].set(.5F, .5F, .5F, 1.0F); // Ambient + + for (u8 i = 0; i < 3; i++) { + lightDirections[i].set(1.0F, 1.0F, 1.0F, 1.0F); + } + + isAllocated = true; +} + +void StdpipLightingBag::deallocate() { + if (!isAllocated) return; + + forceDeallocate(); +} + +void StdpipLightingBag::forceDeallocate() { + forceDeallocateColors(); + forceDeallocateDirections(); + + isAllocated = false; +} + +void StdpipLightingBag::forceDeallocateColors() { + if (lightColors != nullptr) { + delete[] lightColors; + lightColors = nullptr; + } +} + +void StdpipLightingBag::forceDeallocateDirections() { + if (lightDirections != nullptr) { + delete[] lightDirections; + lightDirections = nullptr; + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_texture_bag.cpp b/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_texture_bag.cpp new file mode 100644 index 0000000..34542a5 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/bag/stdpip_texture_bag.cpp @@ -0,0 +1,22 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/std/core/bag/stdpip_texture_bag.hpp" + +namespace Tyra { + +StdpipTextureBag::StdpipTextureBag() { + coordinates = nullptr; + texture = nullptr; +} + +StdpipTextureBag::~StdpipTextureBag() {} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_c_vu1.vclpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_c_vu1.vclpp new file mode 100644 index 0000000..49620cb --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_c_vu1.vclpp @@ -0,0 +1,125 @@ +; ______ ____ ___ +; | \/ ____| |___| +; | | | \ | | +;--------------------------------------------------------------- +; Copyright 2022, tyra - https://github.com/h4570/tyra +; Licenced under Apache License 2.0 +; Sandro Sobczyński +; +;--------------------------------------------------------------- +; Triangle list +; AsIs = NO TRANSFORM +; Colors +;--------------------------------------------------------------- + +.syntax new +.name StdpipVU1As_Is_C +.vu +.init_vf_all +.init_vi_all + +#include "src/renderer/core/paths/path1/programs/vcl_sml.i" +#include "src/renderer/core/paths/path1/programs/tyra_macros.i" +#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h" + +#define RGBA_STORE_OFFSET 0 +#define XYZ2_STORE_OFFSET 1 + +--enter +--endenter + +#vuprog StdpipVU1AsIsC + + LoadTyraStaticData{ gifSetTag } + LoadTyraSingleColor{ singleColor, singleColorEnabled, VU1_SINGLE_COLOR_ADDR, VU1_OPTIONS_ADDR } + LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR } + +begin: + xtop buffer + LoadTyraBufferTags{ scale, primTag, buffer } + + iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR + ilw.w vertexCount, 0(buffer) + iadd colorData, vertexData, vertexCount + iblez singleColorEnabled, setDestAddrMultiColor + iadd kickAddress, vertexData, vertexCount + b setDestAddr +setDestAddrMultiColor: + iadd kickAddress, colorData, vertexCount +setDestAddr: + iaddiu destAddress, kickAddress, 0 + + StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress } + + ;--- Loop + iadd vertexCounter, buffer, vertexCount +vertexLoop: + + iblez singleColorEnabled, multiColor + ;--- Load vertices single color + add color1, vf00, singleColor + add color2, vf00, singleColor + add color3, vf00, singleColor + b processing + +multiColor: + ;--- Load vertices colors + lq color1, (colorData) + lq color2, 1(colorData) + lq color3, 2(colorData) + +processing: + ;--- Load vertex1 + lq.xyz vertex1, (vertexData) + + ;--- Load vertex2 + lq.xyz vertex2, 1(vertexData) + + ;--- Load vertex3 + lq.xyz vertex3, 2(vertexData) + + ;--- Calculate vertex1 + ScaleVertexToGSFormat{ scale, vertex1 } + FixColor{ color1 } + + ;--- Calculate vertex2 + ScaleVertexToGSFormat{ scale, vertex2 } + FixColor{ color2 } + + ;--- Calculate vertex3 + ScaleVertexToGSFormat{ scale, vertex3 } + FixColor{ color3 } + + ;--- Store vertex1 + sq color1, RGBA_STORE_OFFSET(destAddress) + sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress) + + ;--- Store vertex2 + sq color2, RGBA_STORE_OFFSET+2(destAddress) + sq.xyz vertex2, XYZ2_STORE_OFFSET+2(destAddress) + + ;--- Store vertex3 + sq color3, RGBA_STORE_OFFSET+4(destAddress) + sq.xyz vertex3, XYZ2_STORE_OFFSET+4(destAddress) + + ;------------------------------- + + iaddiu vertexData, vertexData, 3 + iaddiu colorData, colorData, 3 + iaddiu destAddress, destAddress, 6 + + ;--- Fix loop + iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter + ibne vertexCounter, buffer, vertexLoop ; and repeat if needed + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it... +--cont + + b begin + +#endvuprog + +--exit +--endexit diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_c_vu1_program.cpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_c_vu1_program.cpp new file mode 100644 index 0000000..34091f6 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_c_vu1_program.cpp @@ -0,0 +1,47 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include "renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_c_vu1_program.hpp" + +extern u32 StdpipVU1As_Is_C_CodeStart __attribute__((section(".vudata"))); +extern u32 StdpipVU1As_Is_C_CodeEnd __attribute__((section(".vudata"))); + +namespace Tyra { + +StdpipAsIsCVU1Program::StdpipAsIsCVU1Program() + : StdpipVU1Program(StdpipAsIsColor, &StdpipVU1As_Is_C_CodeStart, + &StdpipVU1As_Is_C_CodeEnd, + ((u64)GIF_REG_RGBAQ) << 0 | ((u64)GIF_REG_XYZ2) << 4, 2, + 2) {} + +StdpipAsIsCVU1Program::~StdpipAsIsCVU1Program() {} + +std::string StdpipAsIsCVU1Program::getStringName() const { + return std::string("As is - C"); +} + +void StdpipAsIsCVU1Program::addProgramQBufferDataToPacket( + packet2_t* packet, StdpipQBuffer* qbuffer) const { + u32 addr = VU1_VERT_DATA_ADDR; + + // Add vertices + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices, + qbuffer->size, true); + + // Add colors + if (qbuffer->bag->color->single == nullptr) { + addr += qbuffer->size; + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors, + qbuffer->size, true); + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_d_vu1.vclpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_d_vu1.vclpp new file mode 100644 index 0000000..52ea541 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_d_vu1.vclpp @@ -0,0 +1,111 @@ +; ______ ____ ___ +; | \/ ____| |___| +; | | | \ | | +;--------------------------------------------------------------- +; Copyright 2022, tyra - https://github.com/h4570/tyra +; Licenced under Apache License 2.0 +; Sandro Sobczyński +; +;--------------------------------------------------------------- +; Triangle list +; AsIs = NO TRANSFORM +; Directional lights +;--------------------------------------------------------------- + +.syntax new +.name StdpipVU1As_Is_D +.vu +.init_vf_all +.init_vi_all + +#include "src/renderer/core/paths/path1/programs/vcl_sml.i" +#include "src/renderer/core/paths/path1/programs/tyra_macros.i" +#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h" + +#define RGBA_STORE_OFFSET 0 +#define XYZ2_STORE_OFFSET 1 + +--enter +--endenter + +#vuprog StdpipVU1AsIsD + + LoadTyraStaticData{ gifSetTag } + LoadTyraDirectionalLights{ lightMatrix, lightDirections, lightColors, ambientColor, VU1_LIGHTS_DIRS_ADDR, VU1_LIGHTS_COLORS_ADDR, VU1_LIGHTS_MATRIX_ADDR } + LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR } + +begin: + xtop buffer + LoadTyraBufferTags{ scale, primTag, buffer } + + iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR + ilw.w vertexCount, 0(buffer) + iadd normalData, vertexData, vertexCount + iadd kickAddress, normalData, vertexCount + iadd destAddress, normalData, vertexCount + + StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress } + + ;--- Loop + iadd vertexCounter, buffer, vertexCount +vertexLoop: + ;--- Load vertex1 + lq.xyz vertex1, (vertexData) + lq.xyz normal1, (normalData) + + ;--- Load vertex2 + lq.xyz vertex2, 1(vertexData) + lq.xyz normal2, 1(normalData) + + ;--- Load vertex3 + lq.xyz vertex3, 2(vertexData) + lq.xyz normal3, 2(normalData) + + ;--- Calculate vertex1 + ScaleVertexToGSFormat{ scale, vertex1 } + CalculateTyraDirectionalLights{ outputColor1, normal1, lightDirections, lightColors, lightMatrix, ambientColor } + FixColor{ outputColor1 } + + ;--- Calculate vertex2 + ScaleVertexToGSFormat{ scale, vertex2 } + CalculateTyraDirectionalLights{ outputColor2, normal2, lightDirections, lightColors, lightMatrix, ambientColor } + FixColor{ outputColor2 } + + ;--- Calculate vertex3 + ScaleVertexToGSFormat{ scale, vertex3 } + CalculateTyraDirectionalLights{ outputColor3, normal3, lightDirections, lightColors, lightMatrix, ambientColor } + FixColor{ outputColor3 } + + ;--- Store vertex1 + sq outputColor1, RGBA_STORE_OFFSET(destAddress) + sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress) + + ;--- Store vertex2 + sq outputColor2, RGBA_STORE_OFFSET+2(destAddress) + sq.xyz vertex2, XYZ2_STORE_OFFSET+2(destAddress) + + ;--- Store vertex3 + sq outputColor3, RGBA_STORE_OFFSET+4(destAddress) + sq.xyz vertex3, XYZ2_STORE_OFFSET+4(destAddress) + + ;------------------------------- + + iaddiu vertexData, vertexData, 3 + iaddiu normalData, normalData, 3 + iaddiu destAddress, destAddress, 6 + + ;--- Fix loop + iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter + ibne vertexCounter, buffer, vertexLoop ; and repeat if needed + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it... +--cont + + b begin + +#endvuprog + +--exit +--endexit diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_d_vu1_program.cpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_d_vu1_program.cpp new file mode 100644 index 0000000..dac410e --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_d_vu1_program.cpp @@ -0,0 +1,52 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include "renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_d_vu1_program.hpp" + +extern u32 StdpipVU1As_Is_D_CodeStart __attribute__((section(".vudata"))); +extern u32 StdpipVU1As_Is_D_CodeEnd __attribute__((section(".vudata"))); + +namespace Tyra { + +StdpipAsIsDVU1Program::StdpipAsIsDVU1Program() + : StdpipVU1Program(StdpipAsIsDirLights, &StdpipVU1As_Is_D_CodeStart, + &StdpipVU1As_Is_D_CodeEnd, + ((u64)GIF_REG_RGBAQ) << 0 | ((u64)GIF_REG_XYZ2) << 4, 2, + 3) {} + +StdpipAsIsDVU1Program::~StdpipAsIsDVU1Program() {} + +std::string StdpipAsIsDVU1Program::getStringName() const { + return std::string("As is - LC"); +} + +void StdpipAsIsDVU1Program::addProgramQBufferDataToPacket( + packet2_t* packet, StdpipQBuffer* qbuffer) const { + u32 addr = VU1_VERT_DATA_ADDR; + + // Add vertices + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices, + qbuffer->size, true); + addr += qbuffer->size; + + // Add normal + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->normals, + qbuffer->size, true); + + // Add colors + if (qbuffer->bag->color->single == nullptr) { + addr += qbuffer->size; + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors, + qbuffer->size, true); + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_tc_vu1.vclpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_tc_vu1.vclpp new file mode 100644 index 0000000..ab40535 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_tc_vu1.vclpp @@ -0,0 +1,140 @@ +; ______ ____ ___ +; | \/ ____| |___| +; | | | \ | | +;--------------------------------------------------------------- +; Copyright 2022, tyra - https://github.com/h4570/tyra +; Licenced under Apache License 2.0 +; Sandro Sobczyński +; +;--------------------------------------------------------------- +; Triangle list +; AsIs = NO TRANSFORM +; Texture, colors +;--------------------------------------------------------------- + +.syntax new +.name StdpipVU1As_Is_TC +.vu +.init_vf_all +.init_vi_all + +#include "src/renderer/core/paths/path1/programs/vcl_sml.i" +#include "src/renderer/core/paths/path1/programs/tyra_macros.i" +#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h" + +#define STQ_STORE_OFFSET 0 +#define RGBA_STORE_OFFSET 1 +#define XYZ2_STORE_OFFSET 2 + +--enter +--endenter + +#vuprog StdpipVU1AsIsTC + + LoadTyraStaticData{ gifSetTag } + LoadTyraSingleColor{ singleColor, singleColorEnabled, VU1_SINGLE_COLOR_ADDR, VU1_OPTIONS_ADDR } + LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR } + +begin: + xtop buffer + LoadTyraBufferTags{ scale, primTag, buffer } + + iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR + ilw.w vertexCount, 0(buffer) + iadd stqData, vertexData, vertexCount + iadd colorData, stqData, vertexCount + iblez singleColorEnabled, setDestAddrMultiColor + iadd kickAddress, stqData, vertexCount + b setDestAddr +setDestAddrMultiColor: + iadd kickAddress, colorData, vertexCount +setDestAddr: + iaddiu destAddress, kickAddress, 0 + + StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress } + + ;--- Loop + iadd vertexCounter, buffer, vertexCount +vertexLoop: + + iblez singleColorEnabled, multiColor + ;--- Load vertices single color + add color1, vf00, singleColor + add color2, vf00, singleColor + add color3, vf00, singleColor + b processing + +multiColor: + ;--- Load vertices colors + lq color1, (colorData) + lq color2, 1(colorData) + lq color3, 2(colorData) + +processing: + ;--- Load vertex1 + lq vertex1, (vertexData) + lq stq1, (stqData) + + ;--- Load vertex2 + lq vertex2, 1(vertexData) + lq stq2, 1(stqData) + + ;--- Load vertex3 + lq vertex3, 2(vertexData) + lq stq3, 2(stqData) + + ;--- Calculate vertex1 + ScaleVertexToGSFormat{ scale, vertex1 } + div q, vf00[w], vertex1[w] + PerformTexturePerspectiveCorrection{ outputStq1, stq1 } + FixColor{ color1 } + + ;--- Calculate vertex2 + ScaleVertexToGSFormat{ scale, vertex2 } + div q, vf00[w], vertex2[w] + PerformTexturePerspectiveCorrection{ outputStq2, stq2 } + FixColor{ color2 } + + ;--- Calculate vertex3 + ScaleVertexToGSFormat{ scale, vertex3 } + div q, vf00[w], vertex3[w] + PerformTexturePerspectiveCorrection{ outputStq3, stq3 } + FixColor{ color3 } + + ;--- Store vertex1 + sq outputStq1, STQ_STORE_OFFSET(destAddress) + sq color1, RGBA_STORE_OFFSET(destAddress) + sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress) + + ;--- Store vertex2 + sq outputStq2, STQ_STORE_OFFSET+3(destAddress) + sq color2, RGBA_STORE_OFFSET+3(destAddress) + sq.xyz vertex2, XYZ2_STORE_OFFSET+3(destAddress) + + ;--- Store vertex3 + sq outputStq3, STQ_STORE_OFFSET+6(destAddress) + sq color3, RGBA_STORE_OFFSET+6(destAddress) + sq.xyz vertex3, XYZ2_STORE_OFFSET+6(destAddress) + + ;------------------------------- + + iaddiu vertexData, vertexData, 3 + iaddiu stqData, stqData, 3 + iaddiu colorData, colorData, 3 + iaddiu destAddress, destAddress, 9 + + ;--- Fix loop + iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter + ibne vertexCounter, buffer, vertexLoop ; and repeat if needed + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it... +--cont + + b begin + +#endvuprog + +--exit +--endexit diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_tc_vu1_program.cpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_tc_vu1_program.cpp new file mode 100644 index 0000000..66b3261 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_tc_vu1_program.cpp @@ -0,0 +1,53 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include "renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_tc_vu1_program.hpp" + +extern u32 StdpipVU1As_Is_TC_CodeStart __attribute__((section(".vudata"))); +extern u32 StdpipVU1As_Is_TC_CodeEnd __attribute__((section(".vudata"))); + +namespace Tyra { + +StdpipAsIsTCVU1Program::StdpipAsIsTCVU1Program() + : StdpipVU1Program(StdpipAsIsTextureColor, &StdpipVU1As_Is_TC_CodeStart, + &StdpipVU1As_Is_TC_CodeEnd, + ((u64)GIF_REG_ST) << 0 | ((u64)GIF_REG_RGBAQ) << 4 | + ((u64)GIF_REG_XYZ2) << 8, + 3, 3) {} + +StdpipAsIsTCVU1Program::~StdpipAsIsTCVU1Program() {} + +std::string StdpipAsIsTCVU1Program::getStringName() const { + return std::string("As is - TC"); +} + +void StdpipAsIsTCVU1Program::addProgramQBufferDataToPacket( + packet2_t* packet, StdpipQBuffer* qbuffer) const { + u32 addr = VU1_VERT_DATA_ADDR; + + // Add vertices + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices, + qbuffer->size, true); + addr += qbuffer->size; + + // Add sts + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->sts, qbuffer->size, + true); + + // Add colors + if (qbuffer->bag->color->single == nullptr) { + addr += qbuffer->size; + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors, + qbuffer->size, true); + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_td_vu1.vclpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_td_vu1.vclpp new file mode 100644 index 0000000..966323f --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_td_vu1.vclpp @@ -0,0 +1,126 @@ +; ______ ____ ___ +; | \/ ____| |___| +; | | | \ | | +;--------------------------------------------------------------- +; Copyright 2022, tyra - https://github.com/h4570/tyra +; Licenced under Apache License 2.0 +; Sandro Sobczyński +; +;--------------------------------------------------------------- +; Triangle list +; AsIs = NO TRANSFORM +; Texture, directional lights +;--------------------------------------------------------------- + +.syntax new +.name StdpipVU1As_Is_TD +.vu +.init_vf_all +.init_vi_all + +#include "src/renderer/core/paths/path1/programs/vcl_sml.i" +#include "src/renderer/core/paths/path1/programs/tyra_macros.i" +#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h" + +#define STQ_STORE_OFFSET 0 +#define RGBA_STORE_OFFSET 1 +#define XYZ2_STORE_OFFSET 2 + +--enter +--endenter + +#vuprog StdpipVU1AsIsTD + + LoadTyraStaticData{ gifSetTag } + LoadTyraDirectionalLights{ lightMatrix, lightDirections, lightColors, ambientColor, VU1_LIGHTS_DIRS_ADDR, VU1_LIGHTS_COLORS_ADDR, VU1_LIGHTS_MATRIX_ADDR } + LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR } + +begin: + xtop buffer + LoadTyraBufferTags{ scale, primTag, buffer } + + iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR + ilw.w vertexCount, 0(buffer) + iadd stqData, vertexData, vertexCount + iadd normalData, stqData, vertexCount + iadd kickAddress, normalData, vertexCount + iadd destAddress, normalData, vertexCount + + StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress } + + ;--- Loop + iadd vertexCounter, buffer, vertexCount +vertexLoop: + ;--- Load vertex1 + lq vertex1, (vertexData) + lq stq1, (stqData) + lq.xyz normal1, (normalData) + + ;--- Load vertex2 + lq vertex2, 1(vertexData) + lq stq2, 1(stqData) + lq.xyz normal2, 1(normalData) + + ;--- Load vertex3 + lq vertex3, 2(vertexData) + lq stq3, 2(stqData) + lq.xyz normal3, 2(normalData) + + ;--- Calculate vertex1 + ScaleVertexToGSFormat{ scale, vertex1 } + div q, vf00[w], vertex1[w] + PerformTexturePerspectiveCorrection{ outputStq1, stq1 } + CalculateTyraDirectionalLights{ outputColor1, normal1, lightDirections, lightColors, lightMatrix, ambientColor } + FixColor{ outputColor1 } + + ;--- Calculate vertex2 + ScaleVertexToGSFormat{ scale, vertex2 } + div q, vf00[w], vertex2[w] + PerformTexturePerspectiveCorrection{ outputStq2, stq2 } + CalculateTyraDirectionalLights{ outputColor2, normal2, lightDirections, lightColors, lightMatrix, ambientColor } + FixColor{ outputColor2 } + + ;--- Calculate vertex3 + ScaleVertexToGSFormat{ scale, vertex3 } + div q, vf00[w], vertex3[w] + PerformTexturePerspectiveCorrection{ outputStq3, stq3 } + CalculateTyraDirectionalLights{ outputColor3, normal3, lightDirections, lightColors, lightMatrix, ambientColor } + FixColor{ outputColor3 } + + ;--- Store vertex1 + sq outputStq1, STQ_STORE_OFFSET(destAddress) + sq outputColor1, RGBA_STORE_OFFSET(destAddress) + sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress) + + ;--- Store vertex2 + sq outputStq2, STQ_STORE_OFFSET+3(destAddress) + sq outputColor2, RGBA_STORE_OFFSET+3(destAddress) + sq.xyz vertex2, XYZ2_STORE_OFFSET+3(destAddress) + + ;--- Store vertex3 + sq outputStq3, STQ_STORE_OFFSET+6(destAddress) + sq outputColor3, RGBA_STORE_OFFSET+6(destAddress) + sq.xyz vertex3, XYZ2_STORE_OFFSET+6(destAddress) + + ;------------------------------- + + iaddiu vertexData, vertexData, 3 + iaddiu stqData, stqData, 3 + iaddiu normalData, normalData, 3 + iaddiu destAddress, destAddress, 9 + + ;--- Fix loop + iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter + ibne vertexCounter, buffer, vertexLoop ; and repeat if needed + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it... +--cont + + b begin + +#endvuprog + +--exit +--endexit diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_td_vu1_program.cpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_td_vu1_program.cpp new file mode 100644 index 0000000..8132b3b --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_td_vu1_program.cpp @@ -0,0 +1,58 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include "renderer/3d/pipeline/std/core/path1/programs/as_is/stdpip_as_is_td_vu1_program.hpp" + +extern u32 StdpipVU1As_Is_TD_CodeStart __attribute__((section(".vudata"))); +extern u32 StdpipVU1As_Is_TD_CodeEnd __attribute__((section(".vudata"))); + +namespace Tyra { + +StdpipAsIsTDVU1Program::StdpipAsIsTDVU1Program() + : StdpipVU1Program(StdpipAsIsTextureDirLights, &StdpipVU1As_Is_TD_CodeStart, + &StdpipVU1As_Is_TD_CodeEnd, + ((u64)GIF_REG_ST) << 0 | ((u64)GIF_REG_RGBAQ) << 4 | + ((u64)GIF_REG_XYZ2) << 8, + 3, 4) {} + +StdpipAsIsTDVU1Program::~StdpipAsIsTDVU1Program() {} + +std::string StdpipAsIsTDVU1Program::getStringName() const { + return std::string("As is - LTC"); +} + +void StdpipAsIsTDVU1Program::addProgramQBufferDataToPacket( + packet2_t* packet, StdpipQBuffer* qbuffer) const { + u32 addr = VU1_VERT_DATA_ADDR; + + // Add vertices + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices, + qbuffer->size, true); + addr += qbuffer->size; + + // Add sts + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->sts, qbuffer->size, + true); + addr += qbuffer->size; + + // Add normal + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->normals, + qbuffer->size, true); + + // Add colors + if (qbuffer->bag->color->single == nullptr) { + addr += qbuffer->size; + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors, + qbuffer->size, true); + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_c_vu1.vclpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_c_vu1.vclpp new file mode 100644 index 0000000..8234ae3 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_c_vu1.vclpp @@ -0,0 +1,136 @@ +; ______ ____ ___ +; | \/ ____| |___| +; | | | \ | | +;--------------------------------------------------------------- +; Copyright 2022, tyra - https://github.com/h4570/tyra +; Licenced under Apache License 2.0 +; Sandro Sobczyński +; +;--------------------------------------------------------------- +; Triangle list +; Cull = Standard PS2 way. clipw polys are culled. +; Volors +;--------------------------------------------------------------- + +.syntax new +.name StdpipVU1Cull_C +.vu +.init_vf_all +.init_vi_all + +#include "src/renderer/core/paths/path1/programs/vcl_sml.i" +#include "src/renderer/core/paths/path1/programs/tyra_macros.i" +#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h" + +#define RGBA_STORE_OFFSET 0 +#define XYZ2_STORE_OFFSET 1 + +--enter +--endenter + +#vuprog StdpipVU1CullC + + ResetClipFlags{ } + LoadTyraStaticData{ gifSetTag } + MatrixLoad{ mvp, VU1_MVP_MATRIX_ADDR, vi00 } + LoadTyraSingleColor{ singleColor, singleColorEnabled, VU1_SINGLE_COLOR_ADDR, VU1_OPTIONS_ADDR } + LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR } + +begin: + xtop buffer + LoadTyraBufferTags{ scale, primTag, buffer } + + iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR + ilw.w vertexCount, 0(buffer) + iadd colorData, vertexData, vertexCount + iblez singleColorEnabled, setDestAddrMultiColor + iadd kickAddress, vertexData, vertexCount + b setDestAddr +setDestAddrMultiColor: + iadd kickAddress, colorData, vertexCount +setDestAddr: + iaddiu destAddress, kickAddress, 0 + + StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress } + + ;--- Loop + iadd vertexCounter, buffer, vertexCount +vertexLoop: + + iblez singleColorEnabled, multiColor + ;--- Load vertices single color + add color1, vf00, singleColor + add color2, vf00, singleColor + add color3, vf00, singleColor + b processing + +multiColor: + ;--- Load vertices colors + lq color1, (colorData) + lq color2, 1(colorData) + lq color3, 2(colorData) + +processing: + ;--- Load vertex1 + lq vertex1, (vertexData) + + ;--- Load vertex2 + lq vertex2, 1(vertexData) + + ;--- Load vertex3 + lq vertex3, 2(vertexData) + + ;--- Calculate vertex1 + MatrixMultiplyVertex{ vertex1, mvp, vertex1 } + PerformClipCheck{ vertex1, destAddress, XYZ2_STORE_OFFSET } + VertexPersCorr{ vertex1, vertex1 } + ScaleVertexToGSFormat{ scale, vertex1 } + FixColor{ color1 } + + ;--- Calculate vertex2 + MatrixMultiplyVertex{ vertex2, mvp, vertex2 } + PerformClipCheck{ vertex2, destAddress, XYZ2_STORE_OFFSET+2 } + VertexPersCorr{ vertex2, vertex2 } + ScaleVertexToGSFormat{ scale, vertex2 } + FixColor{ color2 } + + ;--- Calculate vertex3 + MatrixMultiplyVertex{ vertex3, mvp, vertex3 } + PerformClipCheck{ vertex3, destAddress, XYZ2_STORE_OFFSET+4 } + VertexPersCorr{ vertex3, vertex3 } + ScaleVertexToGSFormat{ scale, vertex3 } + FixColor{ color3 } + + ;--- Store vertex1 + sq color1, RGBA_STORE_OFFSET(destAddress) + sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress) + + ;--- Store vertex2 + sq color2, RGBA_STORE_OFFSET+2(destAddress) + sq.xyz vertex2, XYZ2_STORE_OFFSET+2(destAddress) + + ;--- Store vertex3 + sq color3, RGBA_STORE_OFFSET+4(destAddress) + sq.xyz vertex3, XYZ2_STORE_OFFSET+4(destAddress) + + ;------------------------------- + + iaddiu vertexData, vertexData, 3 + iaddiu colorData, colorData, 3 + iaddiu destAddress, destAddress, 6 + + ;--- Fix loop + iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter + ibne vertexCounter, buffer, vertexLoop ; and repeat if needed + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it... +--cont + + b begin + +#endvuprog + +--exit +--endexit diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_c_vu1_program.cpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_c_vu1_program.cpp new file mode 100644 index 0000000..6bd1cf4 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_c_vu1_program.cpp @@ -0,0 +1,46 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include "renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_c_vu1_program.hpp" + +extern u32 StdpipVU1Cull_C_CodeStart __attribute__((section(".vudata"))); +extern u32 StdpipVU1Cull_C_CodeEnd __attribute__((section(".vudata"))); + +namespace Tyra { + +StdpipCullCVU1Program::StdpipCullCVU1Program() + : StdpipVU1Program( + StdpipCullColor, &StdpipVU1Cull_C_CodeStart, &StdpipVU1Cull_C_CodeEnd, + ((u64)GIF_REG_RGBAQ) << 0 | ((u64)GIF_REG_XYZ2) << 4, 2, 2) {} + +StdpipCullCVU1Program::~StdpipCullCVU1Program() {} + +std::string StdpipCullCVU1Program::getStringName() const { + return std::string("Cull - C"); +} + +void StdpipCullCVU1Program::addProgramQBufferDataToPacket( + packet2_t* packet, StdpipQBuffer* qbuffer) const { + u32 addr = VU1_VERT_DATA_ADDR; + + // Add vertices + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices, + qbuffer->size, true); + + // Add colors + if (qbuffer->bag->color->single == nullptr) { + addr += qbuffer->size; + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors, + qbuffer->size, true); + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_d_vu1.vclpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_d_vu1.vclpp new file mode 100644 index 0000000..ffa857c --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_d_vu1.vclpp @@ -0,0 +1,122 @@ +; ______ ____ ___ +; | \/ ____| |___| +; | | | \ | | +;--------------------------------------------------------------- +; Copyright 2022, tyra - https://github.com/h4570/tyra +; Licenced under Apache License 2.0 +; Sandro Sobczyński +; +;--------------------------------------------------------------- +; Triangle list +; Cull = Standard PS2 way. clipw polys are culled. +; Directional lights +;--------------------------------------------------------------- + +.syntax new +.name StdpipVU1Cull_D +.vu +.init_vf_all +.init_vi_all + +#include "src/renderer/core/paths/path1/programs/vcl_sml.i" +#include "src/renderer/core/paths/path1/programs/tyra_macros.i" +#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h" + +#define RGBA_STORE_OFFSET 0 +#define XYZ2_STORE_OFFSET 1 + +--enter +--endenter + +#vuprog StdpipVU1CullD + + ResetClipFlags{ } + LoadTyraStaticData{ gifSetTag } + MatrixLoad{ mvp, VU1_MVP_MATRIX_ADDR, vi00 } + LoadTyraDirectionalLights{ lightMatrix, lightDirections, lightColors, ambientColor, VU1_LIGHTS_DIRS_ADDR, VU1_LIGHTS_COLORS_ADDR, VU1_LIGHTS_MATRIX_ADDR } + LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR } + +begin: + xtop buffer + LoadTyraBufferTags{ scale, primTag, buffer } + + iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR + ilw.w vertexCount, 0(buffer) + iadd normalData, vertexData, vertexCount + iadd kickAddress, normalData, vertexCount + iadd destAddress, normalData, vertexCount + + StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress } + + ;--- Loop + iadd vertexCounter, buffer, vertexCount +vertexLoop: + ;--- Load vertex1 + lq vertex1, (vertexData) + lq.xyz normal1, (normalData) + + ;--- Load vertex2 + lq vertex2, 1(vertexData) + lq.xyz normal2, 1(normalData) + + ;--- Load vertex3 + lq vertex3, 2(vertexData) + lq.xyz normal3, 2(normalData) + + ;--- Calculate vertex1 + MatrixMultiplyVertex{ vertex1, mvp, vertex1 } + PerformClipCheck{ vertex1, destAddress, XYZ2_STORE_OFFSET } + VertexPersCorr{ vertex1, vertex1 } + ScaleVertexToGSFormat{ scale, vertex1 } + CalculateTyraDirectionalLights{ outputColor1, normal1, lightDirections, lightColors, lightMatrix, ambientColor } + FixColor{ outputColor1 } + + ;--- Calculate vertex2 + MatrixMultiplyVertex{ vertex2, mvp, vertex2 } + PerformClipCheck{ vertex2, destAddress, XYZ2_STORE_OFFSET+2 } + VertexPersCorr{ vertex2, vertex2 } + ScaleVertexToGSFormat{ scale, vertex2 } + CalculateTyraDirectionalLights{ outputColor2, normal2, lightDirections, lightColors, lightMatrix, ambientColor } + FixColor{ outputColor2 } + + ;--- Calculate vertex3 + MatrixMultiplyVertex{ vertex3, mvp, vertex3 } + PerformClipCheck{ vertex3, destAddress, XYZ2_STORE_OFFSET+4 } + VertexPersCorr{ vertex3, vertex3 } + ScaleVertexToGSFormat{ scale, vertex3 } + CalculateTyraDirectionalLights{ outputColor3, normal3, lightDirections, lightColors, lightMatrix, ambientColor } + FixColor{ outputColor3 } + + ;--- Store vertex1 + sq outputColor1, RGBA_STORE_OFFSET(destAddress) + sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress) + + ;--- Store vertex2 + sq outputColor2, RGBA_STORE_OFFSET+2(destAddress) + sq.xyz vertex2, XYZ2_STORE_OFFSET+2(destAddress) + + ;--- Store vertex3 + sq outputColor3, RGBA_STORE_OFFSET+4(destAddress) + sq.xyz vertex3, XYZ2_STORE_OFFSET+4(destAddress) + + ;------------------------------- + + iaddiu vertexData, vertexData, 3 + iaddiu normalData, normalData, 3 + iaddiu destAddress, destAddress, 6 + + ;--- Fix loop + iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter + ibne vertexCounter, buffer, vertexLoop ; and repeat if needed + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it... +--cont + + b begin + +#endvuprog + +--exit +--endexit diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_d_vu1_program.cpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_d_vu1_program.cpp new file mode 100644 index 0000000..9ac111c --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_d_vu1_program.cpp @@ -0,0 +1,52 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include "renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_d_vu1_program.hpp" + +extern u32 StdpipVU1Cull_D_CodeStart __attribute__((section(".vudata"))); +extern u32 StdpipVU1Cull_D_CodeEnd __attribute__((section(".vudata"))); + +namespace Tyra { + +StdpipCullDVU1Program::StdpipCullDVU1Program() + : StdpipVU1Program(StdpipCullDirLights, &StdpipVU1Cull_D_CodeStart, + &StdpipVU1Cull_D_CodeEnd, + ((u64)GIF_REG_RGBAQ) << 0 | ((u64)GIF_REG_XYZ2) << 4, 2, + 3) {} + +StdpipCullDVU1Program::~StdpipCullDVU1Program() {} + +std::string StdpipCullDVU1Program::getStringName() const { + return std::string("Cull - LC"); +} + +void StdpipCullDVU1Program::addProgramQBufferDataToPacket( + packet2_t* packet, StdpipQBuffer* qbuffer) const { + u32 addr = VU1_VERT_DATA_ADDR; + + // Add vertices + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices, + qbuffer->size, true); + addr += qbuffer->size; + + // Add normal + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->normals, + qbuffer->size, true); + + // Add colors + if (qbuffer->bag->color->single == nullptr) { + addr += qbuffer->size; + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors, + qbuffer->size, true); + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_tc_vu1.vclpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_tc_vu1.vclpp new file mode 100644 index 0000000..cc2d0bb --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_tc_vu1.vclpp @@ -0,0 +1,148 @@ +; ______ ____ ___ +; | \/ ____| |___| +; | | | \ | | +;--------------------------------------------------------------- +; Copyright 2022, tyra - https://github.com/h4570/tyra +; Licenced under Apache License 2.0 +; Sandro Sobczyński +; +;--------------------------------------------------------------- +; Triangle list +; Cull = Standard PS2 way. clipw polys are culled. +; Lighting, texture, colors +;--------------------------------------------------------------- + +.syntax new +.name StdpipVU1Cull_TC +.vu +.init_vf_all +.init_vi_all + +#include "src/renderer/core/paths/path1/programs/vcl_sml.i" +#include "src/renderer/core/paths/path1/programs/tyra_macros.i" +#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h" + +#define STQ_STORE_OFFSET 0 +#define RGBA_STORE_OFFSET 1 +#define XYZ2_STORE_OFFSET 2 + +--enter +--endenter + +#vuprog StdpipVU1CullTC + + ResetClipFlags{ } + LoadTyraStaticData{ gifSetTag } + MatrixLoad{ mvp, VU1_MVP_MATRIX_ADDR, vi00 } + LoadTyraSingleColor{ singleColor, singleColorEnabled, VU1_SINGLE_COLOR_ADDR, VU1_OPTIONS_ADDR } + LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR } + +begin: + xtop buffer + LoadTyraBufferTags{ scale, primTag, buffer } + + iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR + ilw.w vertexCount, 0(buffer) + iadd stqData, vertexData, vertexCount + iadd colorData, stqData, vertexCount + iblez singleColorEnabled, setDestAddrMultiColor + iadd kickAddress, stqData, vertexCount + b setDestAddr +setDestAddrMultiColor: + iadd kickAddress, colorData, vertexCount +setDestAddr: + iaddiu destAddress, kickAddress, 0 + + StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress } + + ;--- Loop + iadd vertexCounter, buffer, vertexCount +vertexLoop: + + iblez singleColorEnabled, multiColor + ;--- Load vertices single color + add color1, vf00, singleColor + add color2, vf00, singleColor + add color3, vf00, singleColor + b processing + +multiColor: + ;--- Load vertices colors + lq color1, (colorData) + lq color2, 1(colorData) + lq color3, 2(colorData) + +processing: + ;--- Load vertex1 + lq vertex1, (vertexData) + lq stq1, (stqData) + + ;--- Load vertex2 + lq vertex2, 1(vertexData) + lq stq2, 1(stqData) + + ;--- Load vertex3 + lq vertex3, 2(vertexData) + lq stq3, 2(stqData) + + ;--- Calculate vertex1 + MatrixMultiplyVertex{ vertex1, mvp, vertex1 } + PerformClipCheck{ vertex1, destAddress, XYZ2_STORE_OFFSET } + VertexPersCorr{ vertex1, vertex1 } + ScaleVertexToGSFormat{ scale, vertex1 } + PerformTexturePerspectiveCorrection{ outputStq1, stq1 } + FixColor{ color1 } + + ;--- Calculate vertex2 + MatrixMultiplyVertex{ vertex2, mvp, vertex2 } + PerformClipCheck{ vertex2, destAddress, XYZ2_STORE_OFFSET+3 } + VertexPersCorr{ vertex2, vertex2 } + ScaleVertexToGSFormat{ scale, vertex2 } + PerformTexturePerspectiveCorrection{ outputStq2, stq2 } + FixColor{ color2 } + + ;--- Calculate vertex3 + MatrixMultiplyVertex{ vertex3, mvp, vertex3 } + PerformClipCheck{ vertex3, destAddress, XYZ2_STORE_OFFSET+6 } + VertexPersCorr{ vertex3, vertex3 } + ScaleVertexToGSFormat{ scale, vertex3 } + PerformTexturePerspectiveCorrection{ outputStq3, stq3 } + FixColor{ color3 } + + ;--- Store vertex1 + sq outputStq1, STQ_STORE_OFFSET(destAddress) + sq color1, RGBA_STORE_OFFSET(destAddress) + sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress) + + ;--- Store vertex2 + sq outputStq2, STQ_STORE_OFFSET+3(destAddress) + sq color2, RGBA_STORE_OFFSET+3(destAddress) + sq.xyz vertex2, XYZ2_STORE_OFFSET+3(destAddress) + + ;--- Store vertex3 + sq outputStq3, STQ_STORE_OFFSET+6(destAddress) + sq color3, RGBA_STORE_OFFSET+6(destAddress) + sq.xyz vertex3, XYZ2_STORE_OFFSET+6(destAddress) + + ;------------------------------- + + iaddiu vertexData, vertexData, 3 + iaddiu stqData, stqData, 3 + iaddiu colorData, colorData, 3 + iaddiu destAddress, destAddress, 9 + + ;--- Fix loop + iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter + ibne vertexCounter, buffer, vertexLoop ; and repeat if needed + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it... +--cont + + b begin + +#endvuprog + +--exit +--endexit diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_tc_vu1_program.cpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_tc_vu1_program.cpp new file mode 100644 index 0000000..6437241 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_tc_vu1_program.cpp @@ -0,0 +1,53 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include "renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_tc_vu1_program.hpp" + +extern u32 StdpipVU1Cull_TC_CodeStart __attribute__((section(".vudata"))); +extern u32 StdpipVU1Cull_TC_CodeEnd __attribute__((section(".vudata"))); + +namespace Tyra { + +StdpipCullTCVU1Program::StdpipCullTCVU1Program() + : StdpipVU1Program(StdpipCullTextureColor, &StdpipVU1Cull_TC_CodeStart, + &StdpipVU1Cull_TC_CodeEnd, + ((u64)GIF_REG_ST) << 0 | ((u64)GIF_REG_RGBAQ) << 4 | + ((u64)GIF_REG_XYZ2) << 8, + 3, 3) {} + +StdpipCullTCVU1Program::~StdpipCullTCVU1Program() {} + +std::string StdpipCullTCVU1Program::getStringName() const { + return std::string("Cull - TC"); +} + +void StdpipCullTCVU1Program::addProgramQBufferDataToPacket( + packet2_t* packet, StdpipQBuffer* qbuffer) const { + u32 addr = VU1_VERT_DATA_ADDR; + + // Add vertices + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices, + qbuffer->size, true); + addr += qbuffer->size; + + // Add sts + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->sts, qbuffer->size, + true); + + // Add colors + if (qbuffer->bag->color->single == nullptr) { + addr += qbuffer->size; + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors, + qbuffer->size, true); + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_td_vu1.vclpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_td_vu1.vclpp new file mode 100644 index 0000000..22877e8 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_td_vu1.vclpp @@ -0,0 +1,134 @@ +; ______ ____ ___ +; | \/ ____| |___| +; | | | \ | | +;--------------------------------------------------------------- +; Copyright 2022, tyra - https://github.com/h4570/tyra +; Licenced under Apache License 2.0 +; Sandro Sobczyński +; +;--------------------------------------------------------------- +; Triangle list +; Cull = Standard PS2 way. clipw polys are culled. +; Texture, directional lights +;--------------------------------------------------------------- + +.syntax new +.name StdpipVU1Cull_TD +.vu +.init_vf_all +.init_vi_all + +#include "src/renderer/core/paths/path1/programs/vcl_sml.i" +#include "src/renderer/core/paths/path1/programs/tyra_macros.i" +#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h" + +#define STQ_STORE_OFFSET 0 +#define RGBA_STORE_OFFSET 1 +#define XYZ2_STORE_OFFSET 2 + +--enter +--endenter + +#vuprog StdpipVU1CullTD + + ResetClipFlags{ } + LoadTyraStaticData{ gifSetTag } + MatrixLoad{ mvp, VU1_MVP_MATRIX_ADDR, vi00 } + LoadTyraDirectionalLights{ lightMatrix, lightDirections, lightColors, ambientColor, VU1_LIGHTS_DIRS_ADDR, VU1_LIGHTS_COLORS_ADDR, VU1_LIGHTS_MATRIX_ADDR } + LoadTyraTags{ lodGifTag, texBufferClutGifTag, VU1_LOD_ADDR, VU1_CLUT_ADDR } + +begin: + xtop buffer + LoadTyraBufferTags{ scale, primTag, buffer } + + iaddiu vertexData, buffer, VU1_VERT_DATA_ADDR + ilw.w vertexCount, 0(buffer) + iadd stqData, vertexData, vertexCount + iadd normalData, stqData, vertexCount + iadd kickAddress, normalData, vertexCount + iadd destAddress, normalData, vertexCount + + StoreTyraGifTags{ gifSetTag, lodGifTag, texBufferClutGifTag, primTag, destAddress } + + ;--- Loop + iadd vertexCounter, buffer, vertexCount +vertexLoop: + ;--- Load vertex1 + lq vertex1, (vertexData) + lq stq1, (stqData) + lq.xyz normal1, (normalData) + + ;--- Load vertex2 + lq vertex2, 1(vertexData) + lq stq2, 1(stqData) + lq.xyz normal2, 1(normalData) + + ;--- Load vertex3 + lq vertex3, 2(vertexData) + lq stq3, 2(stqData) + lq.xyz normal3, 2(normalData) + + ;--- Calculate vertex1 + MatrixMultiplyVertex{ vertex1, mvp, vertex1 } + PerformClipCheck{ vertex1, destAddress, XYZ2_STORE_OFFSET } + VertexPersCorr{ vertex1, vertex1 } + ScaleVertexToGSFormat{ scale, vertex1 } + PerformTexturePerspectiveCorrection{ outputStq1, stq1 } + CalculateTyraDirectionalLights{ outputColor1, normal1, lightDirections, lightColors, lightMatrix, ambientColor } + FixColor{ outputColor1 } + + ;--- Calculate vertex2 + MatrixMultiplyVertex{ vertex2, mvp, vertex2 } + PerformClipCheck{ vertex2, destAddress, XYZ2_STORE_OFFSET+3 } + VertexPersCorr{ vertex2, vertex2 } + ScaleVertexToGSFormat{ scale, vertex2 } + PerformTexturePerspectiveCorrection{ outputStq2, stq2 } + CalculateTyraDirectionalLights{ outputColor2, normal2, lightDirections, lightColors, lightMatrix, ambientColor } + FixColor{ outputColor2 } + + ;--- Calculate vertex3 + MatrixMultiplyVertex{ vertex3, mvp, vertex3 } + PerformClipCheck{ vertex3, destAddress, XYZ2_STORE_OFFSET+6 } + VertexPersCorr{ vertex3, vertex3 } + ScaleVertexToGSFormat{ scale, vertex3 } + PerformTexturePerspectiveCorrection{ outputStq3, stq3 } + CalculateTyraDirectionalLights{ outputColor3, normal3, lightDirections, lightColors, lightMatrix, ambientColor } + FixColor{ outputColor3 } + + ;--- Store vertex1 + sq outputStq1, STQ_STORE_OFFSET(destAddress) + sq outputColor1, RGBA_STORE_OFFSET(destAddress) + sq.xyz vertex1, XYZ2_STORE_OFFSET(destAddress) + + ;--- Store vertex2 + sq outputStq2, STQ_STORE_OFFSET+3(destAddress) + sq outputColor2, RGBA_STORE_OFFSET+3(destAddress) + sq.xyz vertex2, XYZ2_STORE_OFFSET+3(destAddress) + + ;--- Store vertex3 + sq outputStq3, STQ_STORE_OFFSET+6(destAddress) + sq outputColor3, RGBA_STORE_OFFSET+6(destAddress) + sq.xyz vertex3, XYZ2_STORE_OFFSET+6(destAddress) + + ;------------------------------- + + iaddiu vertexData, vertexData, 3 + iaddiu stqData, stqData, 3 + iaddiu normalData, normalData, 3 + iaddiu destAddress, destAddress, 9 + + ;--- Fix loop + iaddi vertexCounter, vertexCounter, -3 ; decrement the loop counter + ibne vertexCounter, buffer, vertexLoop ; and repeat if needed + + xgkick kickAddress ; dispatch to the GS rasterizer. + +--barrier ; Why the hell I must add barrier AFTER XGKICK? VCL does not adds E bit without it... +--cont + + b begin + +#endvuprog + +--exit +--endexit diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_td_vu1_program.cpp b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_td_vu1_program.cpp new file mode 100644 index 0000000..18c5ba4 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_td_vu1_program.cpp @@ -0,0 +1,58 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include "renderer/3d/pipeline/std/core/path1/programs/cull/stdpip_cull_td_vu1_program.hpp" + +extern u32 StdpipVU1Cull_TD_CodeStart __attribute__((section(".vudata"))); +extern u32 StdpipVU1Cull_TD_CodeEnd __attribute__((section(".vudata"))); + +namespace Tyra { + +StdpipCullTDVU1Program::StdpipCullTDVU1Program() + : StdpipVU1Program(StdpipCullTextureDirLights, &StdpipVU1Cull_TD_CodeStart, + &StdpipVU1Cull_TD_CodeEnd, + ((u64)GIF_REG_ST) << 0 | ((u64)GIF_REG_RGBAQ) << 4 | + ((u64)GIF_REG_XYZ2) << 8, + 3, 4) {} + +StdpipCullTDVU1Program::~StdpipCullTDVU1Program() {} + +std::string StdpipCullTDVU1Program::getStringName() const { + return std::string("Cull - LTC"); +} + +void StdpipCullTDVU1Program::addProgramQBufferDataToPacket( + packet2_t* packet, StdpipQBuffer* qbuffer) const { + u32 addr = VU1_VERT_DATA_ADDR; + + // Add vertices + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->vertices, + qbuffer->size, true); + addr += qbuffer->size; + + // Add sts + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->sts, qbuffer->size, + true); + addr += qbuffer->size; + + // Add normal + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->normals, + qbuffer->size, true); + + // Add colors + if (qbuffer->bag->color->single == nullptr) { + addr += qbuffer->size; + packet2_utils_vu_add_unpack_data(packet, addr, qbuffer->colors, + qbuffer->size, true); + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_clipper.cpp b/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_clipper.cpp new file mode 100644 index 0000000..42f3d05 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_clipper.cpp @@ -0,0 +1,87 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/std/core/path1/stdpip_clipper.hpp" + +namespace Tyra { + +StdpipClipper::StdpipClipper() {} +StdpipClipper::~StdpipClipper() {} + +void StdpipClipper::setMVP(M4x4* t_mvp) { mvp = t_mvp; } + +void StdpipClipper::init(const RendererSettings& settings) { + algorithm.init(settings); +} + +void StdpipClipper::setMaxVertCount(const u32& count) { maxVertCount = count; } + +void StdpipClipper::clip(StdpipQBuffer* buffer) { + TYRA_ASSERT(buffer->size <= maxVertCount / 3, "Buffer should have max ", + maxVertCount / 3, " verts if we want to clip it."); + + Path1EEClipAlgorithmSettings algoSettings = { + buffer->bag->lighting != nullptr, buffer->bag->texture != nullptr, + buffer->bag->color->many != nullptr}; + + std::vector clippedVertices; + + for (u32 i = 0; i < buffer->size / 3; i++) { + std::vector inputTriangle; + for (u8 j = 0; j < 3; j++) { + Path1ClipVertex vert = { + *mvp * buffer->vertices[i * 3 + j], + buffer->bag->lighting ? buffer->normals[i * 3 + j] : Vec4(), + buffer->bag->texture ? buffer->sts[i * 3 + j] : Vec4(), + buffer->bag->color->many ? buffer->colors[i * 3 + j] : Vec4()}; + + inputTriangle.push_back(vert); + } + + std::vector clippedTriangle; + algorithm.clip(&clippedTriangle, inputTriangle, algoSettings); + + if (clippedTriangle.size() == 0) continue; + + auto va = clippedTriangle.at(0); + for (u32 j = 1; j <= clippedTriangle.size() - 2; j++) { + auto vb = clippedTriangle.at(j); + auto vc = clippedTriangle.at((j + 1) % clippedTriangle.size()); + clippedVertices.push_back(va); + clippedVertices.push_back(vb); + clippedVertices.push_back(vc); + } + } + + perspectiveDivide(&clippedVertices); + moveDataToBuffer(clippedVertices, buffer); +} + +void StdpipClipper::perspectiveDivide(std::vector* vertices) { + for (u32 i = 0; i < vertices->size(); i++) { + (*vertices)[i].position /= (*vertices)[i].position.w; + } +} + +void StdpipClipper::moveDataToBuffer( + const std::vector& vertices, StdpipQBuffer* buffer) { + buffer->reallocateManually(vertices.size()); + + for (u32 i = 0; i < vertices.size(); i++) { + auto& vertex = vertices.at(i); + buffer->vertices[i] = vertex.position; + + if (buffer->bag->texture) buffer->sts[i] = vertex.st; + if (buffer->bag->color->many) buffer->colors[i] = vertex.color; + if (buffer->bag->lighting) buffer->normals[i] = vertex.normal; + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_programs_repository.cpp b/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_programs_repository.cpp new file mode 100644 index 0000000..b102150 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_programs_repository.cpp @@ -0,0 +1,48 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/std/core/path1/stdpip_programs_repository.hpp" + +namespace Tyra { + +StdpipProgramsRepository::StdpipProgramsRepository() {} + +StdpipProgramsRepository::~StdpipProgramsRepository() {} + +StdpipVU1Program* StdpipProgramsRepository::getProgram( + const StdpipProgramName& name) { + switch (name) { + case StdpipProgramName::StdpipAsIsColor: + return &asIsColor; + case StdpipProgramName::StdpipCullColor: + return &cullColor; + + case StdpipProgramName::StdpipAsIsDirLights: + return &asIsLightingColor; + case StdpipProgramName::StdpipCullDirLights: + return &cullLightingColor; + + case StdpipProgramName::StdpipAsIsTextureDirLights: + return &asIsLightingTextureColor; + case StdpipProgramName::StdpipCullTextureDirLights: + return &cullLightingTextureColor; + + case StdpipProgramName::StdpipAsIsTextureColor: + return &asIsTextureColor; + case StdpipProgramName::StdpipCullTextureColor: + return &cullTextureColor; + + default: + TYRA_TRAP("Unknown VU1 program name"); + return &cullLightingTextureColor; + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_qbuffer.cpp b/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_qbuffer.cpp new file mode 100644 index 0000000..c650943 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_qbuffer.cpp @@ -0,0 +1,265 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/std/core/path1/stdpip_qbuffer.hpp" + +namespace Tyra { + +StdpipQBuffer::StdpipQBuffer() { + size = 0; + _isDynamicallyAllocated = false; + _stAllocated = false; + _colorAllocated = false; + _normalAllocated = false; + + vertices = nullptr; + colors = nullptr; + sts = nullptr; + normals = nullptr; +} + +StdpipQBuffer::~StdpipQBuffer() { deallocateDynamicData(); } + +void StdpipQBuffer::setMaxVertCount(const u32& count) { maxVertCount = count; } + +void StdpipQBuffer::fillByPointer(const StdpipBagPackage& pkg) { + TYRA_ASSERT(pkg.size <= maxVertCount, "VU1 buffer supports only ", + maxVertCount, " verts. Provided: ", pkg.size); + + deallocateDynamicData(); + + vertices = pkg.vertices; + sts = pkg.sts; + colors = pkg.colors; + normals = pkg.normals; + size = pkg.size; + bag = pkg.bag; +} + +void StdpipQBuffer::fillByCopyMax(const StdpipBagPackage& pkg1, + const StdpipBagPackage& pkg2, + const StdpipBagPackage& pkg3) { + TYRA_ASSERT(pkg1.size <= maxVertCount / 3, + "Wrong package size (1). Provided: ", pkg1.size); + TYRA_ASSERT(pkg2.size <= maxVertCount / 3, + "Wrong package size (2). Provided: ", pkg2.size); + TYRA_ASSERT(pkg3.size <= maxVertCount / 3, + "Wrong package size (3). Provided: ", pkg3.size); + + deallocateDynamicData(); + size = pkg1.size + pkg2.size + pkg3.size; + allocateDynamicData(size, pkg1.bag); + + for (u16 i = 0; i < pkg1.size; i++) { + vertices[i].set(pkg1.vertices[i]); + + if (pkg1.bag->texture) sts[i].set(pkg1.sts[i]); + + if (pkg1.bag->color->many) + colors[i].set(reinterpret_cast(pkg1.colors[i])); + + if (pkg1.bag->lighting) normals[i].set(pkg1.normals[i]); + } + + for (u16 i = 0; i < pkg2.size; i++) { + vertices[i + pkg1.size].set(pkg2.vertices[i]); + + if (pkg1.bag->texture) sts[i + pkg1.size].set(pkg2.sts[i]); + + if (pkg1.bag->color->many) + colors[i + pkg1.size].set(reinterpret_cast(pkg2.colors[i])); + + if (pkg1.bag->lighting) normals[i + pkg1.size].set(pkg2.normals[i]); + } + + for (u16 i = 0; i < pkg3.size; i++) { + vertices[i + pkg1.size + pkg2.size].set(pkg3.vertices[i]); + + if (pkg1.bag->texture) sts[i + pkg1.size + pkg2.size].set(pkg3.sts[i]); + + if (pkg1.bag->color->many) + colors[i + pkg1.size + pkg2.size].set( + reinterpret_cast(pkg3.colors[i])); + + if (pkg1.bag->lighting) + normals[i + pkg1.size + pkg2.size].set(pkg3.normals[i]); + } + + bag = pkg1.bag; +} + +void StdpipQBuffer::fillByCopy1By2(const StdpipBagPackage& pkg1, + const StdpipBagPackage& pkg2) { + TYRA_ASSERT(pkg1.size <= maxVertCount / 3, + "Wrong package size (1). Provided: ", pkg1.size); + TYRA_ASSERT(pkg2.size <= maxVertCount / 3, + "Wrong package size (2). Provided: ", pkg2.size); + + deallocateDynamicData(); + size = pkg1.size + pkg2.size; + allocateDynamicData(size, pkg1.bag); + + for (u16 i = 0; i < pkg1.size; i++) { + vertices[i].set(pkg1.vertices[i]); + + if (pkg1.bag->texture) sts[i].set(pkg1.sts[i]); + + if (pkg1.bag->color->many) + colors[i].set(reinterpret_cast(pkg1.colors[i])); + + if (pkg1.bag->lighting) normals[i].set(pkg1.normals[i]); + } + + for (u16 i = 0; i < pkg2.size; i++) { + vertices[i + pkg1.size].set(pkg2.vertices[i]); + + if (pkg1.bag->texture) sts[i + pkg1.size].set(pkg2.sts[i]); + + if (pkg1.bag->color->many) + colors[i + pkg1.size].set(reinterpret_cast(pkg2.colors[i])); + + if (pkg1.bag->lighting) normals[i + pkg1.size].set(pkg2.normals[i]); + } + + bag = pkg1.bag; +} + +void StdpipQBuffer::fillByCopy1By3(const StdpipBagPackage& pkg) { + TYRA_ASSERT(pkg.size <= maxVertCount / 3, + "Wrong package size (1). Provided: ", pkg.size); + + deallocateDynamicData(); + size = pkg.size; + allocateDynamicData(size, pkg.bag); + + for (u16 i = 0; i < pkg.size; i++) { + vertices[i].set(pkg.vertices[i]); + + if (pkg.bag->texture) sts[i].set(pkg.sts[i]); + + if (pkg.bag->color->many) + colors[i].set(reinterpret_cast(pkg.colors[i])); + + if (pkg.bag->lighting) normals[i].set(pkg.normals[i]); + } + + bag = pkg.bag; +} + +void StdpipQBuffer::reallocateManually(const u16& t_size) { + deallocateDynamicData(); + allocateDynamicData(t_size, bag); + size = t_size; +} + +void StdpipQBuffer::deallocateDynamicData() { + if (_isDynamicallyAllocated) { + delete[] vertices; + + if (_stAllocated) { + delete[] sts; + _stAllocated = false; + } + + if (_colorAllocated) { + delete[] colors; + _colorAllocated = false; + } + + if (_normalAllocated) { + delete[] normals; + _normalAllocated = false; + } + + _isDynamicallyAllocated = false; + } +} + +/** When we not receive maxVertCount vertices, we must align it by ourself. + * Too bad - not efficient. */ +void StdpipQBuffer::allocateDynamicData(u16 size, StdpipBag* bag) { + TYRA_ASSERT(size <= maxVertCount, "Wrong size. Max buffer size in VU1 is ", + maxVertCount, ". Provided: ", size); + TYRA_ASSERT(!_isDynamicallyAllocated, "Buffer is already allocated"); + + vertices = new (std::align_val_t(sizeof(VECTOR))) Vec4[size]; + + if (bag->texture != nullptr) { + sts = new (std::align_val_t(sizeof(VECTOR))) Vec4[size]; + _stAllocated = true; + } + + if (bag->color->many != nullptr) { + colors = new (std::align_val_t(sizeof(VECTOR))) Vec4[size]; + _colorAllocated = true; + } + + if (bag->lighting != nullptr) { + normals = new (std::align_val_t(sizeof(VECTOR))) Vec4[size]; + _normalAllocated = true; + } + + _isDynamicallyAllocated = true; +} + +bool StdpipQBuffer::any() const { return size > 0; } + +void StdpipQBuffer::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void StdpipQBuffer::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string StdpipQBuffer::getPrint(const char* name) const { + std::stringstream res; + if (name) { + res << name << "("; + } else { + res << "Path1Buffer("; + } + res << std::fixed << std::setprecision(2); + res << std::endl; + res << "Size: " << static_cast(size) << std::endl; + + res << "Vectors: " << std::endl; + for (u32 i = 0; i < size; i++) + res << i << ": " << vertices[i].getPrint() << std::endl; + + if (bag->texture != nullptr) { + res << "STs: " << std::endl; + for (u32 i = 0; i < size; i++) + res << i << ": " << sts[i].getPrint() << std::endl; + } + + if (bag->color->many != nullptr) { + res << "Colors: " << std::endl; + for (u32 i = 0; i < size; i++) + res << i << ": " << colors[i].getPrint() << std::endl; + } + + if (bag->lighting != nullptr) { + res << "Normals: " << std::endl; + for (u32 i = 0; i < size; i++) { + res << i << ": " << normals[i].getPrint(); + if (i < size - 1) { + res << std::endl; + } + } + } + + res << ")"; + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_qbuffer_renderer.cpp b/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_qbuffer_renderer.cpp new file mode 100644 index 0000000..b9df310 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_qbuffer_renderer.cpp @@ -0,0 +1,320 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/std/core/path1/stdpip_qbuffer_renderer.hpp" +#include "renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h" + +namespace Tyra { + +/** + * VU1 = 1000 vert + * + * Quadbuffering: + * 2 main buffers = 1000 / 2 = 500 vert + * 2 kick buffers = 500 / 2 = 250 vert + * + * Vert data: + * Pos + Normal + ST + Color = 4 + * = 4 * 48 = 192 + * + * Other data: + * mvp matrix, light matrix, tags = 14 + * 20 light vectors, light intesities = 25 + * = 14 + 25 = 39 + * + * All data: + * = 192 + 39 = 231 + * + */ + +StdpipQBufferRenderer::StdpipQBufferRenderer() { + context = 0; + lastProgramName = StdipUndefinedProgram; + staticDataPacket = packet2_create(3, P2_TYPE_NORMAL, P2_MODE_CHAIN, true); + objectDataPacket = packet2_create(16, P2_TYPE_NORMAL, P2_MODE_CHAIN, true); + programsPacket = nullptr; +} +StdpipQBufferRenderer::~StdpipQBufferRenderer() { + packet2_free(packets[0]); + packet2_free(packets[1]); + packet2_free(staticDataPacket); + packet2_free(objectDataPacket); + + if (programsPacket) packet2_free(programsPacket); +} + +void StdpipQBufferRenderer::init(RendererCore* t_core) { + path1 = t_core->getPath1(); + clipper.init(t_core->getSettings()); + rendererCore = t_core; + + dma_channel_initialize(DMA_CHANNEL_VIF1, NULL, 0); + dma_channel_fast_waits(DMA_CHANNEL_VIF1); + + const u32 VU1_PACKET_SIZE = 16; + + packets[0] = + packet2_create(VU1_PACKET_SIZE, P2_TYPE_NORMAL, P2_MODE_CHAIN, true); + + packets[1] = + packet2_create(VU1_PACKET_SIZE, P2_TYPE_NORMAL, P2_MODE_CHAIN, true); + + setProgramsCache(); + + reinitVU1(); + + TYRA_LOG("Renderer3DQBufferRenderer initialized"); +} + +void StdpipQBufferRenderer::reinitVU1() { + sendStaticData(); + uploadPrograms(); + setDoubleBuffer(); +} + +StdpipQBuffer* StdpipQBufferRenderer::getBuffer() { return &buffers[context]; } + +void StdpipQBufferRenderer::sendObjectData( + StdpipBag* bag, M4x4* mvp, RendererCoreTextureBuffers* texBuffers) const { + packet2_reset(objectDataPacket, false); + packet2_utils_vu_add_unpack_data(objectDataPacket, VU1_MVP_MATRIX_ADDR, + mvp->data, 4, false); + + if (bag->lighting) { + packet2_utils_vu_add_unpack_data(objectDataPacket, VU1_LIGHTS_MATRIX_ADDR, + bag->lighting->lightMatrix, 3, false); + + packet2_utils_vu_add_unpack_data(objectDataPacket, VU1_LIGHTS_DIRS_ADDR, + bag->lighting->getLightDirections(), 3, + false); + + packet2_utils_vu_add_unpack_data(objectDataPacket, VU1_LIGHTS_COLORS_ADDR, + bag->lighting->getLightColors(), 4, false); + } + + u8 singleColorEnabled = bag->color->single != nullptr; + + if (singleColorEnabled) // Color is placed in 4th slot of + // VU1_LIGHTS_MATRIX_ADDR + packet2_utils_vu_add_unpack_data(objectDataPacket, VU1_SINGLE_COLOR_ADDR, + bag->color->single->rgba, 1, false); + + packet2_utils_vu_open_unpack(objectDataPacket, VU1_OPTIONS_ADDR, false); + { + packet2_add_u32(objectDataPacket, + singleColorEnabled); // Single color enabled. + packet2_add_u32(objectDataPacket, 0); // not used, padding + packet2_add_u32(objectDataPacket, 0); // not used, padding + packet2_add_u32(objectDataPacket, 0); // not used, padding + + packet2_utils_gs_add_lod(objectDataPacket, &rendererCore->gs.lod); + + if (texBuffers != nullptr) { + packet2_utils_gs_add_texbuff_clut(objectDataPacket, texBuffers->core, + &rendererCore->texture.clut); + + rendererCore->texture.updateClutBuffer(texBuffers->clut); + } + } + packet2_utils_vu_close_unpack(objectDataPacket); + + packet2_utils_vu_add_end_tag(objectDataPacket); + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + dma_channel_send_packet2(objectDataPacket, DMA_CHANNEL_VIF1, true); +} + +void StdpipQBufferRenderer::setInfo(StdpipInfoBag* bag) { + rendererCore->gs.prim.antialiasing = bag->antiAliasingEnabled; + rendererCore->gs.prim.blending = bag->blendingEnabled; + rendererCore->gs.prim.shading = bag->shadingType; +} + +void StdpipQBufferRenderer::sendStaticData() const { + packet2_reset(staticDataPacket, false); + packet2_utils_vu_open_unpack(staticDataPacket, VU1_SET_GIFTAG_ADDR, false); + { packet2_utils_gif_add_set(staticDataPacket, 1); } + packet2_utils_vu_close_unpack(staticDataPacket); + + packet2_utils_vu_add_end_tag(staticDataPacket); + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + dma_channel_send_packet2(staticDataPacket, DMA_CHANNEL_VIF1, true); +} + +void StdpipQBufferRenderer::setProgramsCache() { + VU1Program** programs = new VU1Program*[8]; + programs[0] = repository.getProgram(StdpipCullColor); + programs[1] = repository.getProgram(StdpipAsIsColor); + programs[2] = repository.getProgram(StdpipCullDirLights); + programs[3] = repository.getProgram(StdpipAsIsDirLights); + programs[4] = repository.getProgram(StdpipCullTextureDirLights); + programs[5] = repository.getProgram(StdpipAsIsTextureDirLights); + programs[6] = repository.getProgram(StdpipCullTextureColor); + programs[7] = repository.getProgram(StdpipAsIsTextureColor); + programsPacket = path1->createProgramsCache(programs, 8, 0); + delete[] programs; +} + +void StdpipQBufferRenderer::uploadPrograms() { + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + dma_channel_send_packet2(programsPacket, DMA_CHANNEL_VIF1, true); + dma_channel_wait(DMA_CHANNEL_VIF1, 0); +} + +void StdpipQBufferRenderer::setDoubleBuffer() { + u16 startingAddr = VU1_LAST_ITEM_ADDR + 1; + const u16 bufferMaxSize = 1000; + bufferSize = (bufferMaxSize - startingAddr) / 2; + + path1->setDoubleBuffer(startingAddr, bufferSize); + + bufferSize -= 1; // Because we don't want to upload anything from first + // buffer, to first addr of second buffer +} + +void StdpipQBufferRenderer::cull(StdpipQBuffer* buffer) { + if (buffer->size == 0) { + return; + } + + auto program = getCullProgramByBag(buffer->bag); + addBufferDataToPacket(program, buffer); + sendPacket(); +} + +// void StdpipQBufferRenderer::sendFinishTag() { +// StdpipQBufferRenderer way proposition +// auto program = path1->getProgramByName(Draw_Finish); +// addBufferDataToPacket(program, nullptr); +// sendPacket(); + +// packet2_t* packet2 = packet2_create(8, P2_TYPE_NORMAL, P2_MODE_CHAIN, +// true); auto program = +// static_cast(getProgramByName(Draw_Finish)); + +// program->addTag(packet2, prim); +// packet2_utils_vu_add_start_program(packet2, +// program->getDestinationAddress()); packet2_utils_vu_add_end_tag(packet2); +// dma_channel_wait(DMA_CHANNEL_VIF1, 0); +// dma_channel_send_packet2(packet2, DMA_CHANNEL_VIF1, true); +// packet2_free(packet2); +// } + +void StdpipQBufferRenderer::clip(StdpipQBuffer* buffer) { + if (buffer->size == 0) { + return; + } + + auto program = getAsIsProgramByBag(buffer->bag); + clipper.clip(buffer); + + if (buffer->any()) { + if (buffer) addBufferDataToPacket(program, buffer); + sendPacket(); + } +} + +void StdpipQBufferRenderer::clearLastProgramName() { + lastProgramName = StdipUndefinedProgram; +} + +void StdpipQBufferRenderer::addBufferDataToPacket(StdpipVU1Program* program, + StdpipQBuffer* buffer) { + currentPacket = packets[context]; + packet2_reset(currentPacket, false); + + program->addBufferDataToPacket(currentPacket, buffer, &rendererCore->gs.prim); + + if (lastProgramName != program->getName()) { + packet2_utils_vu_add_start_program(currentPacket, + program->getDestinationAddress()); + lastProgramName = program->getName(); + } else { + packet2_utils_vu_add_continue_program(currentPacket); + } + packet2_utils_vu_add_end_tag(currentPacket); +} + +void StdpipQBufferRenderer::sendPacket() { + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + dma_channel_send_packet2(currentPacket, DMA_CHANNEL_VIF1, true); + + // Switch packet, so we can proceed during DMA transfer + context = !context; +} + +void StdpipQBufferRenderer::setMaxVertCount(const u32& count) { + buffers[0].setMaxVertCount(count); + buffers[1].setMaxVertCount(count); + clipper.setMaxVertCount(count); +} + +StdpipVU1Program* StdpipQBufferRenderer::getAsIsProgramByBag( + const StdpipBag* bag) { + auto programType = getDrawProgramTypeByBag(bag); + + if (programType == StdpipVU1TextureDirLights) + return getProgramByName(StdpipAsIsTextureDirLights); + else if (programType == StdpipVU1DirLights) + return getProgramByName(StdpipAsIsDirLights); + else if (programType == StdpipVU1TextureColor) + return getProgramByName(StdpipAsIsTextureColor); + else + return getProgramByName(StdpipAsIsColor); +} + +StdpipVU1Program* StdpipQBufferRenderer::getCullProgramByBag( + const StdpipBag* bag) { + auto programType = getDrawProgramTypeByBag(bag); + return getCullProgramByType(programType); +} + +StdpipVU1Program* StdpipQBufferRenderer::getProgramByName( + const StdpipProgramName& name) { + return repository.getProgram(name); +} + +StdpipVU1Program* StdpipQBufferRenderer::getCullProgramByParams( + const bool& isLightingEnabled, const bool& isTextureEnabled) { + auto type = getDrawProgramTypeByParams(isLightingEnabled, isTextureEnabled); + return getCullProgramByType(type); +} + +StdpipVU1Program* StdpipQBufferRenderer::getCullProgramByType( + const StdpipProgramType& programType) { + if (programType == StdpipVU1TextureDirLights) + return getProgramByName(StdpipCullTextureDirLights); + else if (programType == StdpipVU1DirLights) + return getProgramByName(StdpipCullDirLights); + else if (programType == StdpipVU1TextureColor) + return getProgramByName(StdpipCullTextureColor); + else + return getProgramByName(StdpipCullColor); +} + +StdpipProgramType StdpipQBufferRenderer::getDrawProgramTypeByBag( + const StdpipBag* bag) const { + auto isLightingEnabled = bag->lighting != nullptr; + auto isTextureEnabled = bag->texture != nullptr; + return getDrawProgramTypeByParams(isLightingEnabled, isTextureEnabled); +} + +StdpipProgramType StdpipQBufferRenderer::getDrawProgramTypeByParams( + const bool& isLightingEnabled, const bool& isTextureEnabled) const { + if (isLightingEnabled && isTextureEnabled) + return StdpipVU1TextureDirLights; + else if (isLightingEnabled) + return StdpipVU1DirLights; + else if (isTextureEnabled) + return StdpipVU1TextureColor; + else + return StdpipVU1Color; +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_vu1_program.cpp b/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_vu1_program.cpp new file mode 100644 index 0000000..9725637 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/path1/stdpip_vu1_program.cpp @@ -0,0 +1,83 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/std/core/path1/stdpip_vu1_program.hpp" + +namespace Tyra { + +StdpipVU1Program::StdpipVU1Program(const StdpipProgramName& t_name, + u32* t_start, u32* t_end, + const u32& t_reglist, + const u8& t_reglistCount, + const u8& t_elementsPerVertex) + : VU1Program(t_start, t_end), + name(t_name), + reglistCount(t_reglistCount), + elementsPerVertex(t_elementsPerVertex), + reglist(t_reglist) { + packetSize = packet2_utils_get_packet_size_for_program(start, end); + programSize = calculateProgramSize(); +} + +StdpipVU1Program::~StdpipVU1Program() {} + +const StdpipProgramName& StdpipVU1Program::getName() const { return name; } + +u32& StdpipVU1Program::getReglist() { return reglist; } + +void StdpipVU1Program::addBufferDataToPacket(packet2_t* packet, + StdpipQBuffer* buffer, + prim_t* prim) { + addStandardBufferDataToPacket(packet, buffer, prim); + addProgramQBufferDataToPacket(packet, buffer); +} + +void StdpipVU1Program::addStandardBufferDataToPacket(packet2_t* packet, + StdpipQBuffer* buffer, + prim_t* prim) { + if (buffer->bag->texture) + prim->mapping = 1; + else + prim->mapping = 0; + + packet2_utils_vu_open_unpack(packet, 0, true); + { + packet2_add_float(packet, 2048.0F); // scale + packet2_add_float(packet, 2048.0F); // scale + packet2_add_float(packet, + static_cast(0xFFFFFF) / 32.0F); // scale + packet2_add_u32(packet, buffer->size); // vertex count + + packet2_utils_gs_add_prim_giftag(packet, prim, buffer->size, reglist, + reglistCount, 0); + } + packet2_utils_vu_close_unpack(packet); +} + +u16 StdpipVU1Program::getMaxVertCount(const bool& singleColorEnabled, + const u16& bufferSize) const { + u16 res = bufferSize - 4; + u8 colorElementsPerVertex = + singleColorEnabled ? (elementsPerVertex - 1) : elementsPerVertex; + res /= (colorElementsPerVertex + reglistCount); + + // Buffer size = VU1 double buffer size (xtop) + // res = qbuffer size (directly inside VU1) + + // Must be dividable by 3 and the result also dividable by 3. Why? + // 1st dividable reason - triangle, and packaging system in 3d rendering + // 2nd dividable reason - subpackaging system. We are splitting packages into + // 3 subpackages in 3d renderer. + res = res / 3 / 3; + res = res * 3 * 3; + return res; +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/core/std_pipeline_core.cpp b/engine/src/renderer/3d/pipeline/std/core/std_pipeline_core.cpp new file mode 100644 index 0000000..3444b94 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/core/std_pipeline_core.cpp @@ -0,0 +1,227 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/std/core/std_pipeline_core.hpp" +#include "renderer/core/renderer_core.hpp" +#include "thread/threading.hpp" + +// #define TYRA_RENDERER_VERBOSE_LOG 1 + +#ifdef TYRA_RENDERER_VERBOSE_LOG +#define Verbose(...) Debug::writeLines("VRB: ", ##__VA_ARGS__, "\n") +#else +#define Verbose(...) ((void)0) +#endif + +namespace Tyra { + +StdPipelineCore::StdPipelineCore() { maxVertCount = 0; } + +StdPipelineCore::~StdPipelineCore() {} + +void StdPipelineCore::init(RendererCore* t_core) { + rendererCore = t_core; + qbufferRenderer.init(t_core); + packager.init(&rendererCore->renderer3D.frustumPlanes); +} + +void StdPipelineCore::reinitStandardVU1Programs() { + qbufferRenderer.reinitVU1(); +} + +u32 StdPipelineCore::getMaxVertCountByBag(const StdpipBag* bag) { + return qbufferRenderer.getCullProgramByBag(bag)->getMaxVertCount( + bag->color->many == nullptr, qbufferRenderer.getBufferSize()); +} + +u32 StdPipelineCore::getMaxVertCountByParams(const bool& isSingleColor, + const bool& isLightingEnabled, + const bool& isTextureEnabled) { + return qbufferRenderer + .getCullProgramByParams(isLightingEnabled, isTextureEnabled) + ->getMaxVertCount(isSingleColor, qbufferRenderer.getBufferSize()); +} + +void StdPipelineCore::render(StdpipBag* bag, StdpipBagPackagesBBox* bbox) { + if (bag->count <= 0) return; + + TYRA_ASSERT(bag->vertices != nullptr, + "Vertices are required in 3D render bag!"); + TYRA_ASSERT(bag->info != nullptr, "Info bag is required in 3D render bag!"); + TYRA_ASSERT(bag->info->model != nullptr, + "Info bag's model pointer is empty!"); + TYRA_ASSERT(bag->color != nullptr, "Color bag is required in 3D render bag!"); + TYRA_ASSERT(bag->color->single || bag->color->many, + "At least one color is required in 3D render bag!"); + TYRA_ASSERT((!bag->color->many && !bag->lighting) || + (bag->color->many && !bag->lighting) || + (!bag->color->many && bag->lighting), + "Multicolor is not supported with lighting, please choose one!"); + TYRA_ASSERT( + !bag->lighting || (bag->lighting->lightMatrix && bag->lighting->normals), + "If you want lighting, please provide light matrix and normals!"); + TYRA_ASSERT( + !bag->texture || (bag->texture->texture && bag->texture->coordinates), + "If you want texture, please provide texture and coordinates!"); + + StdpipBagPackagesBBox* renderBbox; + + u32 maxVertCount = getMaxVertCountByBag(bag); + + setMaxVertCount(maxVertCount); + + if (!bbox) + renderBbox = + new StdpipBagPackagesBBox(bag->vertices, bag->count, maxVertCount); + else + renderBbox = bbox; + + auto frustumCheck = renderBbox->getMainBBox()->clipIsInFrustum( + rendererCore->renderer3D.frustumPlanes.getAll(), *bag->info->model); + auto mvp = rendererCore->renderer3D.getViewProj() * *bag->info->model; + + if (frustumCheck == OUTSIDE_FRUSTUM) return; + + RendererCoreTextureBuffers* texBuffers = nullptr; + if (bag->texture) { + auto temp = rendererCore->texture.useTexture(bag->texture->texture); + texBuffers = new RendererCoreTextureBuffers{temp.id, temp.core, temp.clut}; + } + + qbufferRenderer.clearLastProgramName(); + + qbufferRenderer.sendObjectData(bag, &mvp, texBuffers); + + packager.setRenderBBox(renderBbox); + + qbufferRenderer.setClipperMVP(&mvp); + + qbufferRenderer.setInfo(bag->info); + + if (frustumCheck == IN_FRUSTUM || + (frustumCheck == PARTIALLY_IN_FRUSTUM && bag->info->noClipChecks)) { + u16 packagesCount = 0; + auto biggerPkgs = packager.create(&packagesCount, bag, maxVertCount); + Verbose("Material - in frustum. Pkgs: ", packagesCount, + " size: ", static_cast(biggerPkgs[0].size)); + for (u16 i = 0; i < packagesCount; i++) { + Verbose(i, " package - cull by data pointer"); + auto buffer = qbufferRenderer.getBuffer(); + buffer->fillByPointer(biggerPkgs[i]); + qbufferRenderer.cull(buffer); + } + delete[] biggerPkgs; + } else if (frustumCheck == PARTIALLY_IN_FRUSTUM) { + u16 packagesCount = 0; + if (bag->count >= maxVertCount * 2) { + auto packages = packager.create(&packagesCount, bag, maxVertCount); + Verbose("Material - partial. Packages: ", packagesCount); + renderPkgs(packages, packagesCount); + delete[] packages; + } else { + auto subpkgs = packager.create(&packagesCount, bag, maxVertCount / 3); + Verbose("Material - partial. Subpackages: ", packagesCount); + renderSubpkgs(subpkgs, packagesCount); + delete[] subpkgs; + } + } + + if (!bbox) delete renderBbox; + if (texBuffers) delete texBuffers; + + Threading::switchThread(); + + Verbose("Render finished"); +} + +void StdPipelineCore::renderPkgs(StdpipBagPackage* packages, u16 count) { + for (u16 i = 0; i < count; i++) { + if (packages[i].isInFrustum == IN_FRUSTUM) { + Verbose(i, " - package in frustum -> cull"); + auto buffer = qbufferRenderer.getBuffer(); + buffer->fillByPointer(packages[i]); + qbufferRenderer.cull(buffer); + } else if (packages[i].isInFrustum == PARTIALLY_IN_FRUSTUM) { + u16 subpkgsSize = 0; + auto packages1By3 = + packager.create(&subpkgsSize, packages[i], maxVertCount / 3); + Verbose(i, " - partial package. Created subpkgs: ", subpkgsSize); + + renderSubpkgs(packages1By3, subpkgsSize); + delete[] packages1By3; + } + Verbose(i, " - package skipped (outside)"); + } +} + +void StdPipelineCore::renderSubpkgs(StdpipBagPackage* subpkgs, u16 count) { + std::vector doneIndexes; + std::vector loadedIndexes; + + // Check if some subpkgs are full in frustum + for (u16 i = 0; i < count; i++) { + if (subpkgs[i].isInFrustum == IN_FRUSTUM) { + if (loadedIndexes.size() <= 1) { + Verbose(i, " - subpackage in frustum -> load"); + loadedIndexes.push_back(i); + } else { // Hmm, this will never happen? + Verbose(i, " - subpackage in frustum, cull all 3 subpkgs"); + auto buffer = qbufferRenderer.getBuffer(); + buffer->fillByCopyMax(subpkgs[loadedIndexes[0]], + subpkgs[loadedIndexes[1]], subpkgs[i]); + qbufferRenderer.cull(buffer); + doneIndexes.push_back(loadedIndexes[0]); + doneIndexes.push_back(loadedIndexes[1]); + doneIndexes.push_back(i); + loadedIndexes.clear(); + } + } + } + + if (loadedIndexes.size() == 2) { + Verbose("2 in frustum subpkgs left -> cull them"); + auto buffer = qbufferRenderer.getBuffer(); + buffer->fillByCopy1By2(subpkgs[loadedIndexes[0]], + subpkgs[loadedIndexes[1]]); + qbufferRenderer.cull(buffer); + doneIndexes.push_back(loadedIndexes[0]); + doneIndexes.push_back(loadedIndexes[1]); + } else if (loadedIndexes.size() == 1) { + Verbose("1 in frustum subpkg left -> cull it"); + auto buffer = qbufferRenderer.getBuffer(); + buffer->fillByPointer(subpkgs[loadedIndexes[0]]); + qbufferRenderer.cull(buffer); + doneIndexes.push_back(loadedIndexes[0]); + } + + for (u16 i = 0; i < count; i++) { + bool isSkip = subpkgs[i].isInFrustum == OUTSIDE_FRUSTUM || + std::find(doneIndexes.begin(), doneIndexes.end(), i) != + doneIndexes.end(); + + if (isSkip) { + Verbose(i, " - subpkg skipped, already rendered/outside"); + continue; + } + + auto buffer = qbufferRenderer.getBuffer(); + buffer->fillByCopy1By3(subpkgs[i]); + Verbose(i, " - subpkg out/partial -> send to clipper"); + qbufferRenderer.clip(buffer); + } +} + +void StdPipelineCore::setMaxVertCount(const u32& count) { + maxVertCount = count; + packager.setMaxVertCount(count); + qbufferRenderer.setMaxVertCount(count); +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/pipeline/std/std_pipeline.cpp b/engine/src/renderer/3d/pipeline/std/std_pipeline.cpp new file mode 100644 index 0000000..00982b4 --- /dev/null +++ b/engine/src/renderer/3d/pipeline/std/std_pipeline.cpp @@ -0,0 +1,221 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/pipeline/std/std_pipeline.hpp" + +namespace Tyra { + +StdPipeline::StdPipeline() { colorsCache = new Vec4[4]; } + +StdPipeline::~StdPipeline() { delete[] colorsCache; } + +void StdPipeline::init(RendererCore* t_core) { + rendererCore = t_core; + core.init(t_core); +} + +void StdPipeline::onUse() { core.reinitStandardVU1Programs(); } + +void StdPipeline::render(Mesh* mesh, const StdpipOptions* options) { + auto model = mesh->getModelMatrix(); + + MeshFrame* frameFrom = mesh->getFramesCount() > 0 + ? mesh->getFrame(mesh->getCurrentAnimationFrame()) + : mesh->getFrame(0); + + MeshFrame* frameTo = mesh->getFramesCount() > 0 + ? mesh->getFrame(mesh->getNextAnimationFrame()) + : nullptr; + + auto* infoBag = getInfoBag(mesh, options, &model); + + if (options->lighting) setLightingColorsCache(options->lighting); + + for (u32 i = 0; i < mesh->getMaterialsCount(); i++) { + auto* material = mesh->getMaterial(i); + + // 2x bufory[maxVertCount*2] -> pętla po mniejszych częściach i czestsze + // rendery + + // TODO: Double buffering in future + // auto maxVertCount = core->renderer3D.getMaxVertCountByParams( + // material->isSingleColorActivated(), material->getNormalFaces(), + // material->getTextureCoordFaces()); + + StdpipBag bag; + addVertices(mesh, material, &bag, frameFrom, frameTo); + bag.info = infoBag; + bag.color = getColorBag(mesh, material, frameFrom, frameTo); + bag.texture = getTextureBag(mesh, material, frameFrom, frameTo); + bag.lighting = + getLightingBag(mesh, material, &model, frameFrom, frameTo, options); + + core.render(&bag); + + deallocDrawBags(&bag, material); + } + + delete infoBag; +} + +void StdPipeline::addVertices(Mesh* mesh, MeshMaterial* material, + StdpipBag* bag, MeshFrame* frameFrom, + MeshFrame* frameTo) const { + bag->count = material->getFacesCount(); + bag->vertices = new Vec4[bag->count]; + + for (u32 i = 0; i < bag->count; i++) { + auto& face = material->getVertexFaces()[i]; + if (frameTo == nullptr) { + bag->vertices[i] = frameFrom->getVertices()[face]; + } else { + Vec4::setLerp(&bag->vertices[i], frameFrom->getVertices()[face], + frameTo->getVertices()[face], + mesh->getAnimState().interpolation); + } + } +} + +StdpipInfoBag* StdPipeline::getInfoBag(Mesh* mesh, const StdpipOptions* options, + M4x4* model) const { + auto* result = new StdpipInfoBag(); + + if (options) { + result->antiAliasingEnabled = options->antiAliasingEnabled; + result->blendingEnabled = options->blendingEnabled; + result->shadingType = options->shadingType; + result->noClipChecks = options->noClipChecks; + } else { + result->antiAliasingEnabled = false; + result->blendingEnabled = true; + result->shadingType = StdpipShadingFlat; + result->noClipChecks = true; + } + + result->model = model; + + return result; +} + +StdpipColorBag* StdPipeline::getColorBag(Mesh* mesh, MeshMaterial* material, + MeshFrame* frameFrom, + MeshFrame* frameTo) const { + auto* result = new StdpipColorBag(); + + if (material->isSingleColorActivated()) { + result->single = &material->singleColor; + } else { + result->many = new Color[material->getFacesCount()]; + + for (u32 i = 0; i < material->getFacesCount(); i++) { + auto& face = material->getColorFaces()[i]; + if (frameTo == nullptr) { + result->many[i] = frameFrom->getColors()[face]; + } else { + Vec4::setLerp( + reinterpret_cast(&result->many[i]), + reinterpret_cast(frameFrom->getColors()[face]), + reinterpret_cast(frameTo->getColors()[face]), + mesh->getAnimState().interpolation); + } + } + } + + return result; +} + +StdpipTextureBag* StdPipeline::getTextureBag(Mesh* mesh, MeshMaterial* material, + MeshFrame* frameFrom, + MeshFrame* frameTo) { + if (!material->getTextureCoordFaces()) return nullptr; + + auto* result = new StdpipTextureBag(); + + result->texture = + rendererCore->texture.repository.getBySpriteOrMesh(material->getId()); + TYRA_ASSERT(result->texture, "Texture for material id: ", material->getId(), + " was not found in texture repository!"); + + result->coordinates = new Vec4[material->getFacesCount()]; + + for (u32 i = 0; i < material->getFacesCount(); i++) { + auto& face = material->getTextureCoordFaces()[i]; + if (frameTo == nullptr) { + result->coordinates[i] = frameFrom->getTextureCoords()[face]; + } else { + Vec4::setLerp(&result->coordinates[i], + frameFrom->getTextureCoords()[face], + frameTo->getTextureCoords()[face], + mesh->getAnimState().interpolation); + } + } + + return result; +} + +StdpipLightingBag* StdPipeline::getLightingBag( + Mesh* mesh, MeshMaterial* material, M4x4* model, MeshFrame* frameFrom, + MeshFrame* frameTo, const StdpipOptions* options) const { + if (!material->getNormalFaces() || options == nullptr || + options->lighting == nullptr) + return nullptr; + + auto* result = new StdpipLightingBag(true); + result->lightMatrix = model; + + result->setLightsManually(colorsCache, + options->lighting->directionalDirections); + + result->normals = new Vec4[material->getFacesCount()]; + + for (u32 i = 0; i < material->getFacesCount(); i++) { + auto& face = material->getNormalFaces()[i]; + if (frameTo == nullptr) { + result->normals[i] = frameFrom->getNormals()[face]; + } else { + Vec4::setLerp(&result->normals[i], frameFrom->getNormals()[face], + frameTo->getNormals()[face], + mesh->getAnimState().interpolation); + } + } + + return result; +} + +void StdPipeline::setLightingColorsCache( + StdpipLightingOptions* lightingOptions) { + for (int i = 0; i < 3; i++) { + colorsCache[i] = + reinterpret_cast(lightingOptions->directionalColors[i]); + } + colorsCache[3] = reinterpret_cast(*lightingOptions->ambientColor); +} + +void StdPipeline::deallocDrawBags(StdpipBag* bag, + MeshMaterial* material) const { + if (bag->color->many) { + delete[] bag->color->many; + } + + if (bag->texture) { + delete[] bag->texture->coordinates; + delete bag->texture; + } + + if (bag->lighting) { + delete[] bag->lighting->normals; + delete bag->lighting; + } + + delete[] bag->vertices; + delete bag->color; +} + +} // namespace Tyra diff --git a/engine/src/renderer/3d/renderer_3d.cpp b/engine/src/renderer/3d/renderer_3d.cpp new file mode 100644 index 0000000..f9352f7 --- /dev/null +++ b/engine/src/renderer/3d/renderer_3d.cpp @@ -0,0 +1,27 @@ + +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/3d/renderer_3d.hpp" + +namespace Tyra { + +Renderer3D::Renderer3D() { currentPipeline = nullptr; } + +Renderer3D::~Renderer3D() {} + +void Renderer3D::usePipeline(Renderer3DPipeline* pipeline) { + if (currentPipeline != pipeline) { + currentPipeline = pipeline; + currentPipeline->onUse(); + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/2d/renderer_core_2d.cpp b/engine/src/renderer/core/2d/renderer_core_2d.cpp new file mode 100644 index 0000000..11c6aab --- /dev/null +++ b/engine/src/renderer/core/2d/renderer_core_2d.cpp @@ -0,0 +1,104 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/2d/renderer_core_2d.hpp" +#include +#include + +namespace Tyra { + +RendererCore2D::RendererCore2D() { + context = 0; + packets[0] = packet2_create(16, P2_TYPE_NORMAL, P2_MODE_NORMAL, 0); + packets[1] = packet2_create(16, P2_TYPE_NORMAL, P2_MODE_NORMAL, 0); + rects[0] = new texrect_t; + rects[1] = new texrect_t; +} + +RendererCore2D::~RendererCore2D() { + packet2_free(packets[0]); + packet2_free(packets[1]); + delete rects[0]; + delete rects[1]; +} + +const float RendererCore2D::GS_CENTER = 4096.0F; +const float RendererCore2D::SCREEN_CENTER = 4096.0F / 2.0F; + +void RendererCore2D::init(RendererSettings* t_settings, + clutbuffer_t* t_clutBuffer) { + settings = t_settings; + clutBuffer = t_clutBuffer; +} + +void RendererCore2D::render(Sprite* sprite, + const RendererCoreTextureBuffers& texBuffers, + Texture* texture) { + auto* rect = rects[context]; + float sizeX, sizeY; + + if (sprite->getMode() == MODE_REPEAT) { + sizeX = sprite->size.x; + sizeY = sprite->size.y; + } else { + sizeX = static_cast(texture->getWidth()); + sizeY = static_cast(texture->getHeight()); + } + + float texS, texT; + float texMax = texT = texS = sizeX > sizeY ? sizeX : sizeY; + + if (sizeX > sizeY) + texT = texMax / (sizeX / sizeY); + else if (sizeY > sizeX) + texS = texMax / (sizeY / sizeX); + + rect->t0.s = sprite->isFlippedHorizontally() ? texS : 0.0F; + rect->t0.t = sprite->isFlippedVertically() ? texT : 0.0F; + rect->t1.s = sprite->isFlippedHorizontally() ? 0.0F : texS; + rect->t1.t = sprite->isFlippedVertically() ? 0.0F : texT; + + rect->color.r = sprite->color.r; + rect->color.g = sprite->color.g; + rect->color.b = sprite->color.b; + rect->color.a = sprite->color.a; + rect->color.q = 0; + + rect->v0.x = sprite->position.x; + rect->v0.y = sprite->position.y; + rect->v0.z = (u32)-1; + + rect->v1.x = (sprite->size.x * sprite->scale) + sprite->position.x; + rect->v1.y = (sprite->size.y * sprite->scale) + sprite->position.y; + rect->v1.z = (u32)-1; + + auto* packet = packets[context]; + + packet2_reset(packet, false); + packet2_update(packet, draw_primitive_xyoffset(packet->base, 0, SCREEN_CENTER, + SCREEN_CENTER)); + packet2_utils_gif_add_set(packet, 1); + packet2_utils_gs_add_texbuff_clut(packet, texBuffers.core, clutBuffer); + draw_enable_blending(); + packet2_update(packet, draw_rect_textured(packet->next, 0, rect)); + packet2_update(packet, draw_primitive_xyoffset( + packet->next, 0, + SCREEN_CENTER - (settings->getWidth() / 2.0F), + SCREEN_CENTER - (settings->getHeight() / 2.0F))); + draw_disable_blending(); + packet2_update(packet, draw_finish(packet->next)); + + dma_channel_wait(DMA_CHANNEL_GIF, 0); + dma_channel_send_packet2(packet, DMA_CHANNEL_GIF, true); + + context = !context; +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/2d/sprite/sprite.cpp b/engine/src/renderer/core/2d/sprite/sprite.cpp new file mode 100644 index 0000000..34ec840 --- /dev/null +++ b/engine/src/renderer/core/2d/sprite/sprite.cpp @@ -0,0 +1,39 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/2d/sprite/sprite.hpp" + +namespace Tyra { + +Sprite::Sprite() { + id = rand() % 1000000; + _flipH = false; + _flipV = false; + size.set(32.0F, 32.0F); + position.set(100.0F, 100.0F); + scale = 1.0F; + mode = MODE_REPEAT; + setDefaultColor(); +} + +Sprite::~Sprite() {} + +// ---- +// Methods +// ---- + +void Sprite::setDefaultColor() { + color.r = 128; + color.g = 128; + color.b = 128; + color.a = 128; +} + +} // namespace Tyra \ No newline at end of file diff --git a/engine/src/renderer/core/3d/bbox/core_bbox.cpp b/engine/src/renderer/core/3d/bbox/core_bbox.cpp new file mode 100644 index 0000000..7341784 --- /dev/null +++ b/engine/src/renderer/core/3d/bbox/core_bbox.cpp @@ -0,0 +1,169 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include +#include "renderer/core/3d/bbox/core_bbox.hpp" + +namespace Tyra { + +CoreBBox::CoreBBox(CoreBBox** t_bboxes, const u32& count) { + float lowX = t_bboxes[0]->_vertices[0].x; + float lowY = t_bboxes[0]->_vertices[0].y; + float lowZ = t_bboxes[0]->_vertices[0].z; + + float hiX = t_bboxes[0]->_vertices[7].x; + float hiY = t_bboxes[0]->_vertices[7].y; + float hiZ = t_bboxes[0]->_vertices[7].z; + + for (u32 i = 0; i < count; i++) { + if (lowX > t_bboxes[i]->_vertices[0].x) lowX = t_bboxes[i]->_vertices[0].x; + if (hiX < t_bboxes[i]->_vertices[7].x) hiX = t_bboxes[i]->_vertices[7].x; + + if (lowY > t_bboxes[i]->_vertices[0].y) lowY = t_bboxes[i]->_vertices[0].y; + if (hiY < t_bboxes[i]->_vertices[7].y) hiY = t_bboxes[i]->_vertices[7].y; + + if (lowZ > t_bboxes[i]->_vertices[0].z) lowZ = t_bboxes[i]->_vertices[0].z; + if (hiZ < t_bboxes[i]->_vertices[7].z) hiZ = t_bboxes[i]->_vertices[7].z; + } + + _vertices[0].set(lowX, lowY, lowZ); + _vertices[1].set(lowX, lowY, hiZ); + _vertices[2].set(lowX, hiY, lowZ); + _vertices[3].set(lowX, hiY, hiZ); + + _vertices[4].set(hiX, lowY, lowZ); + _vertices[5].set(hiX, lowY, hiZ); + _vertices[6].set(hiX, hiY, lowZ); + _vertices[7].set(hiX, hiY, hiZ); +} + +CoreBBox::CoreBBox(const std::vector& t_bboxes, const u32& startIndex, + const u32& stopIndex) { + float lowX = t_bboxes[startIndex]._vertices[0].x; + float lowY = t_bboxes[startIndex]._vertices[0].y; + float lowZ = t_bboxes[startIndex]._vertices[0].z; + + float hiX = t_bboxes[startIndex]._vertices[7].x; + float hiY = t_bboxes[startIndex]._vertices[7].y; + float hiZ = t_bboxes[startIndex]._vertices[7].z; + + for (u32 i = startIndex; i < stopIndex; i++) { + if (lowX > t_bboxes[i]._vertices[0].x) lowX = t_bboxes[i]._vertices[0].x; + if (hiX < t_bboxes[i]._vertices[7].x) hiX = t_bboxes[i]._vertices[7].x; + + if (lowY > t_bboxes[i]._vertices[0].y) lowY = t_bboxes[i]._vertices[0].y; + if (hiY < t_bboxes[i]._vertices[7].y) hiY = t_bboxes[i]._vertices[7].y; + + if (lowZ > t_bboxes[i]._vertices[0].z) lowZ = t_bboxes[i]._vertices[0].z; + if (hiZ < t_bboxes[i]._vertices[7].z) hiZ = t_bboxes[i]._vertices[7].z; + } + + _vertices[0].set(lowX, lowY, lowZ); + _vertices[1].set(lowX, lowY, hiZ); + _vertices[2].set(lowX, hiY, lowZ); + _vertices[3].set(lowX, hiY, hiZ); + + _vertices[4].set(hiX, lowY, lowZ); + _vertices[5].set(hiX, lowY, hiZ); + _vertices[6].set(hiX, hiY, lowZ); + _vertices[7].set(hiX, hiY, hiZ); +} + +CoreBBox::CoreBBox(Vec4* t_vertices, u32* faces, u32 count) { + float lowX, lowY, lowZ, hiX, hiY, hiZ; + lowX = hiX = t_vertices[faces[0]].x; + lowY = hiY = t_vertices[faces[0]].y; + lowZ = hiZ = t_vertices[faces[0]].z; + for (u32 i = 0; i < count; i++) { + if (lowX > t_vertices[faces[i]].x) lowX = t_vertices[faces[i]].x; + if (hiX < t_vertices[faces[i]].x) hiX = t_vertices[faces[i]].x; + + if (lowY > t_vertices[faces[i]].y) lowY = t_vertices[faces[i]].y; + if (hiY < t_vertices[faces[i]].y) hiY = t_vertices[faces[i]].y; + + if (lowZ > t_vertices[faces[i]].z) lowZ = t_vertices[faces[i]].z; + if (hiZ < t_vertices[faces[i]].z) hiZ = t_vertices[faces[i]].z; + } + + _vertices[0].set(lowX, lowY, lowZ); + _vertices[1].set(lowX, lowY, hiZ); + _vertices[2].set(lowX, hiY, lowZ); + _vertices[3].set(lowX, hiY, hiZ); + + _vertices[4].set(hiX, lowY, lowZ); + _vertices[5].set(hiX, lowY, hiZ); + _vertices[6].set(hiX, hiY, lowZ); + _vertices[7].set(hiX, hiY, hiZ); +} + +CoreBBox::CoreBBox(Vec4* t_vertices, u32 count) { + float lowX, lowY, lowZ, hiX, hiY, hiZ; + lowX = hiX = t_vertices[0].x; + lowY = hiY = t_vertices[0].y; + lowZ = hiZ = t_vertices[0].z; + for (u32 i = 0; i < count; i++) { + if (lowX > t_vertices[i].x) lowX = t_vertices[i].x; + if (hiX < t_vertices[i].x) hiX = t_vertices[i].x; + + if (lowY > t_vertices[i].y) lowY = t_vertices[i].y; + if (hiY < t_vertices[i].y) hiY = t_vertices[i].y; + + if (lowZ > t_vertices[i].z) lowZ = t_vertices[i].z; + if (hiZ < t_vertices[i].z) hiZ = t_vertices[i].z; + } + + _vertices[0].set(lowX, lowY, lowZ); + _vertices[1].set(lowX, lowY, hiZ); + _vertices[2].set(lowX, hiY, lowZ); + _vertices[3].set(lowX, hiY, hiZ); + + _vertices[4].set(hiX, lowY, lowZ); + _vertices[5].set(hiX, lowY, hiZ); + _vertices[6].set(hiX, hiY, lowZ); + _vertices[7].set(hiX, hiY, hiZ); +} + +CoreBBox::CoreBBox(Vec4* t_vertices) { + for (auto i = 0; i < 8; i++) Vec4::copy(&_vertices[i], t_vertices[i].xyzw); +} + +void CoreBBox::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void CoreBBox::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string CoreBBox::getPrint(const char* name) const { + std::stringstream res; + if (name) { + res << name << "("; + } else { + res << "CoreBBox("; + } + res << std::fixed << std::setprecision(4); + res << std::endl; + for (auto i = 0; i < 8; i++) { + res << i << ": " << _vertices[i].x << ", " << _vertices[i].y << ", " + << _vertices[i].z << ", " << _vertices[i].w; + + if (i != 7) + res << std::endl; + else + res << ")"; + } + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/3d/bbox/render_bbox.cpp b/engine/src/renderer/core/3d/bbox/render_bbox.cpp new file mode 100644 index 0000000..dc1465b --- /dev/null +++ b/engine/src/renderer/core/3d/bbox/render_bbox.cpp @@ -0,0 +1,94 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include +#include "renderer/core/3d/bbox/render_bbox.hpp" + +namespace Tyra { + +RenderBBox::RenderBBox(CoreBBox** t_bboxes, const u32& count) + : CoreBBox(t_bboxes, count) {} + +RenderBBox::RenderBBox(const std::vector& t_bboxes, + const u32& startIndex, const u32& stopIndex) + : CoreBBox(t_bboxes, startIndex, stopIndex) {} + +RenderBBox::RenderBBox(Vec4* t_vertices, u32* faces, u32 count) + : CoreBBox(t_vertices, faces, count) {} + +RenderBBox::RenderBBox(Vec4* t_vertices, u32 count) + : CoreBBox(t_vertices, count) {} + +RenderBBox::RenderBBox(Vec4* t_vertices) : CoreBBox(t_vertices) {} + +/** + * @brief Frustum checker for renderer. + * Background: We want to really put as low as possible polys to clipper. + * So we are doing magic trick. If BBox is partially inside frustum (clipper), + * we are adding some margins, and checking again if it really needs clipping, + * because "Cull" renderer can handle easy clip cases and its faster. + */ +CoreBBoxFrustum RenderBBox::clipIsInFrustum(const Plane* frustumPlanes, + const M4x4& model) const { + auto result = isInFrustum(frustumPlanes, model); + + if (result != PARTIALLY_IN_FRUSTUM) { + return result; + } + + // Oh no, it probably needs clipping + + float margins[6]; // This probably needs more calibration + + margins[0] = -15.0F; // Top + margins[1] = -10.0F; // BOTTOM + margins[2] = -25.0F; // LEFT + margins[3] = -25.0F; // RIGHT + margins[4] = -10.0F; // NEAR + margins[5] = -10.0F; // FAR + + return isInFrustum(frustumPlanes, model, margins); // Let's check it again +} + +CoreBBoxFrustum RenderBBox::isInFrustum(const Plane* frustumPlanes, + const M4x4& model, + const float* margins) const { + CoreBBoxFrustum result = IN_FRUSTUM; + Vec4 boxCalcTemp; + u8 boxIn = 0, boxOut = 0; + + for (u8 i = 0; i < 6; i++) { + const auto margin = margins == nullptr ? 0.0F : margins[i]; + boxOut = 0; + boxIn = 0; + // for each corner of the box do ... + // get out of the cycle as soon as a box as corners + // both inside and out of the frustum + for (u8 y = 0; y < 8 && (boxIn == 0 || boxOut == 0); y++) { + boxCalcTemp = model * _vertices[y]; + + auto isOut = frustumPlanes[i].distanceTo(boxCalcTemp) < margin; + + if (isOut) + boxOut++; + else + boxIn++; + } + // if all corners are out + if (!boxIn) + return OUTSIDE_FRUSTUM; + else if (boxOut) + result = PARTIALLY_IN_FRUSTUM; + } + return result; +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/3d/camera_info_3d.cpp b/engine/src/renderer/core/3d/camera_info_3d.cpp new file mode 100644 index 0000000..be58dfa --- /dev/null +++ b/engine/src/renderer/core/3d/camera_info_3d.cpp @@ -0,0 +1,30 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/3d/camera_info_3d.hpp" + +namespace Tyra { + +Vec4 CameraInfo3D::defaultCameraUp = Vec4(0.0F, 1.0F, 0.0F); + +CameraInfo3D::CameraInfo3D(Vec4* t_cameraPosition, Vec4* t_cameraLooksAt, + Vec4* t_cameraUp) { + position = t_cameraPosition; + looksAt = t_cameraLooksAt; + + if (t_cameraUp == nullptr) { + up = &defaultCameraUp; + } else { + up = t_cameraUp; + } +} +CameraInfo3D::~CameraInfo3D() {} + +} // namespace Tyra diff --git a/engine/src/renderer/core/3d/renderer_3d_frustum_planes.cpp b/engine/src/renderer/core/3d/renderer_3d_frustum_planes.cpp new file mode 100644 index 0000000..cb6185f --- /dev/null +++ b/engine/src/renderer/core/3d/renderer_3d_frustum_planes.cpp @@ -0,0 +1,107 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include "debug/debug.hpp" +#include "renderer/core/3d/renderer_3d_frustum_planes.hpp" + +namespace Tyra { + +Renderer3DFrustumPlanes::Renderer3DFrustumPlanes() { lastFov = 0.0F; } +Renderer3DFrustumPlanes::~Renderer3DFrustumPlanes() {} + +void Renderer3DFrustumPlanes::init(RendererSettings* t_settings, + const float& fov) { + settings = t_settings; + computeStaticData(fov); + + TYRA_LOG("Frustum planes initialized!"); +} + +void Renderer3DFrustumPlanes::update(const CameraInfo3D& cameraInfo, + const float& fov) { + computeStaticData(fov); + + // compute the Z axis of camera + Z = *cameraInfo.position - *cameraInfo.looksAt; + Z.normalize(); + + // X axis of camera of given "up" vector and Z axis + X = cameraInfo.up->cross(Z); + X.normalize(); + + // the real "up" vector is the cross product of Z and X + Y = Z.cross(X); + + // compute the center of the near and far planes + nearCenter = *cameraInfo.position - Z * settings->getNear(); + farCenter = *cameraInfo.position - Z * settings->getFar(); + + // compute the 8 corners of the frustum + ntl = nearCenter + Y * nearHeight - X * nearWidth; + ntr = nearCenter + Y * nearHeight + X * nearWidth; + nbl = nearCenter - Y * nearHeight - X * nearWidth; + nbr = nearCenter - Y * nearHeight + X * nearWidth; + + ftl = farCenter + Y * farHeight - X * farWidth; + fbr = farCenter - Y * farHeight + X * farWidth; + ftr = farCenter + Y * farHeight + X * farWidth; + fbl = farCenter - Y * farHeight - X * farWidth; + + frustumPlanes[0].update(ntr, ntl, ftl); // Top + frustumPlanes[1].update(nbl, nbr, fbr); // BOTTOM + frustumPlanes[2].update(ntl, nbl, fbl); // LEFT + frustumPlanes[3].update(nbr, ntr, fbr); // RIGHT + frustumPlanes[4].update(ntl, ntr, nbr); // NEAR + frustumPlanes[5].update(ftr, ftl, fbl); // FAR +} + +void Renderer3DFrustumPlanes::computeStaticData(const float& fov) { + if (fabs(fov - lastFov) < 0.00001F) return; + + lastFov = fov; + float tang = tanf(fov * Math::HALF_ANG2RAD); + nearHeight = tang * settings->getNear(); + nearWidth = nearHeight * settings->getAspectRatio(); + farHeight = tang * settings->getFar(); + farWidth = farHeight * settings->getAspectRatio(); +} + +void Renderer3DFrustumPlanes::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void Renderer3DFrustumPlanes::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string Renderer3DFrustumPlanes::getPrint(const char* name) const { + std::stringstream res; + if (name) { + res << name << "("; + } else { + res << "Renderer3DFrustumPlanes("; + } + res << std::fixed << std::setprecision(2); + res << std::endl; + + res << frustumPlanes[0].getPrint("Top") << std::endl; + res << frustumPlanes[1].getPrint("Bottom") << std::endl; + res << frustumPlanes[2].getPrint("Left") << std::endl; + res << frustumPlanes[3].getPrint("Right") << std::endl; + res << frustumPlanes[4].getPrint("Near") << std::endl; + res << frustumPlanes[5].getPrint("Far") << ")"; + + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/3d/renderer_core_3d.cpp b/engine/src/renderer/core/3d/renderer_core_3d.cpp new file mode 100644 index 0000000..189d400 --- /dev/null +++ b/engine/src/renderer/core/3d/renderer_core_3d.cpp @@ -0,0 +1,54 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/3d/renderer_core_3d.hpp" + +namespace Tyra { + +RendererCore3D::RendererCore3D() { fov = 60.0F; } +RendererCore3D::~RendererCore3D() {} + +void RendererCore3D::update(const CameraInfo3D& cameraInfo) { + frustumPlanes.update(cameraInfo, fov); + M4x4::lookAt(&view, *cameraInfo.position, *cameraInfo.looksAt); + viewProj = projection * view; +} + +void RendererCore3D::init(RendererSettings* t_settings, Path1* t_path1) { + settings = t_settings; + path1 = t_path1; + frustumPlanes.init(settings, fov); + setProjection(); + TYRA_LOG("RendererCore3D initialized!"); +} + +// TODO, usunac albo private core? przesunac set/get fov? +void RendererCore3D::setFov(const float& t_fov) { + fov = t_fov; + setProjection(); +} + +void RendererCore3D::setProjection() { + projection = M4x4::perspective( + fov, settings->getWidth(), settings->getHeight(), + settings->getProjectionScale(), settings->getAspectRatio(), + settings->getNear(), settings->getFar()); +} + +u32 RendererCore3D::uploadVU1Program(VU1Program* program, const u32& address) { + return path1->uploadProgram(program, address); +} + +void RendererCore3D::setVU1DoubleBuffers(const u16& startingAddress, + const u16& bufferSize) { + path1->setDoubleBuffer(startingAddress, bufferSize); +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/gs/renderer_core_gs.cpp b/engine/src/renderer/core/gs/renderer_core_gs.cpp new file mode 100644 index 0000000..2b6ac67 --- /dev/null +++ b/engine/src/renderer/core/gs/renderer_core_gs.cpp @@ -0,0 +1,147 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "debug/debug.hpp" +#include "renderer/core/gs/renderer_core_gs.hpp" + +namespace Tyra { + +RendererCoreGS::RendererCoreGS() { + context = 0; + spaceOccupiedByFrameBuffers = 0; + spaceOccupiedByZBuffer = 0; +} +RendererCoreGS::~RendererCoreGS() { + if (flipPacket) { + packet2_free(flipPacket); + } +} + +void RendererCoreGS::init(RendererSettings* t_settings) { + settings = t_settings; + + initChannels(); + flipPacket = packet2_create(4, P2_TYPE_UNCACHED_ACCL, P2_MODE_NORMAL, 0); + allocateBuffers(); + initDrawingEnvironment(); + setPrim(); + setLod(); + + TYRA_LOG("Renderer core initialized!"); +} + +void RendererCoreGS::initChannels() { + dma_channel_initialize(DMA_CHANNEL_GIF, NULL, 0); +} + +void RendererCoreGS::allocateBuffers() { + const u16 psm = 24; + + frameBuffers[0].width = static_cast(settings->getWidth()); + frameBuffers[0].height = static_cast(settings->getHeight()); + frameBuffers[0].mask = 0; + frameBuffers[0].psm = GS_PSM_24; + frameBuffers[0].address = + graph_vram_allocate(frameBuffers[0].width, frameBuffers[0].height, + frameBuffers[0].psm, GRAPH_ALIGN_PAGE); + + frameBuffers[1].width = frameBuffers[0].width; + frameBuffers[1].height = frameBuffers[0].height; + frameBuffers[1].mask = frameBuffers[0].mask; + frameBuffers[1].psm = frameBuffers[0].psm; + frameBuffers[1].address = + graph_vram_allocate(frameBuffers[1].width, frameBuffers[1].height, + frameBuffers[1].psm, GRAPH_ALIGN_PAGE); + + zBuffer.enable = DRAW_ENABLE; + zBuffer.mask = 0; + zBuffer.method = ZTEST_METHOD_GREATER_EQUAL; + zBuffer.zsm = GS_ZBUF_24; + zBuffer.address = + graph_vram_allocate(frameBuffers[0].width, frameBuffers[0].height, + zBuffer.zsm, GRAPH_ALIGN_PAGE); + TYRA_LOG("Framebuffers, zBuffer set and allocated!"); + + // Initialize the screen and tie the first framebuffer to the read circuits. + graph_initialize(frameBuffers[1].address, frameBuffers[1].width, + frameBuffers[1].height, frameBuffers[1].psm, 0, 0); + + spaceOccupiedByFrameBuffers = + ((frameBuffers[0].width / 100.0F) * (frameBuffers[0].height / 100.0F) * + (psm / 100.0F)) / + 8.0F; + + spaceOccupiedByZBuffer = spaceOccupiedByFrameBuffers; + spaceOccupiedByFrameBuffers *= 2; +} + +void RendererCoreGS::initDrawingEnvironment() { + packet2_t* packet2 = packet2_create(20, P2_TYPE_NORMAL, P2_MODE_NORMAL, 0); + packet2_update(packet2, draw_setup_environment(packet2->base, 0, frameBuffers, + &zBuffer)); + packet2_update(packet2, draw_primitive_xyoffset( + packet2->next, 0, + screenCenter - (settings->getWidth() / 2.0F), + screenCenter - (settings->getHeight() / 2.0F))); + packet2_update(packet2, draw_finish(packet2->next)); + dma_channel_send_packet2(packet2, DMA_CHANNEL_GIF, true); + dma_channel_wait(DMA_CHANNEL_GIF, 0); + packet2_free(packet2); + TYRA_LOG("Drawing environment initialized!"); +} + +void RendererCoreGS::setPrim() { + prim.type = PRIM_TRIANGLE; + prim.shading = PRIM_SHADE_GOURAUD; + prim.mapping = DRAW_ENABLE; + prim.fogging = DRAW_DISABLE; + prim.blending = DRAW_ENABLE; + prim.antialiasing = DRAW_DISABLE; + prim.mapping_type = PRIM_MAP_ST; + prim.colorfix = PRIM_UNFIXED; + TYRA_LOG("Prim set!"); +} + +void RendererCoreGS::setLod() { + lod.calculation = LOD_USE_K; + lod.max_level = 0; + lod.mag_filter = LOD_MAG_NEAREST; + lod.min_filter = LOD_MIN_NEAREST; + lod.l = 0; + lod.k = 0.0F; + TYRA_LOG("Lod set!"); +} + +void RendererCoreGS::flipBuffers() { + graph_set_framebuffer_filtered(frameBuffers[context].address, + frameBuffers[context].width, + frameBuffers[context].psm, 0, 0); + + context ^= 1; + + packet2_update(flipPacket, + draw_framebuffer(flipPacket->base, 0, &frameBuffers[context])); + packet2_update(flipPacket, draw_finish(flipPacket->next)); + dma_channel_wait(DMA_CHANNEL_GIF, 0); + dma_channel_send_packet2(flipPacket, DMA_CHANNEL_GIF, true); + draw_wait_finish(); +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/paths/path1/clipper/path1_ee_clip_algorithm.cpp b/engine/src/renderer/core/paths/path1/clipper/path1_ee_clip_algorithm.cpp new file mode 100644 index 0000000..c398efd --- /dev/null +++ b/engine/src/renderer/core/paths/path1/clipper/path1_ee_clip_algorithm.cpp @@ -0,0 +1,114 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/paths/path1/clipper/path1_ee_clip_algorithm.hpp" + +namespace Tyra { + +Path1EEClipAlgorithm::Path1EEClipAlgorithm() {} + +Path1EEClipAlgorithm::~Path1EEClipAlgorithm() {} + +float Path1EEClipAlgorithm::clipMargin = -10.0F; + +void Path1EEClipAlgorithm::init(const RendererSettings& settings) { + halfWidth = settings.getWidth() / 2; + halfHeight = settings.getHeight() / 2; + near = settings.getNear() - (-clipMargin); + far = -settings.getFar(); +} + +void Path1EEClipAlgorithm::clip(std::vector* o_vertices, + const std::vector& vertices, + const Path1EEClipAlgorithmSettings& settings) { + tempVertices.clear(); + + for (u32 i = 0; i < vertices.size(); i++) { + o_vertices->push_back(vertices.at(i)); + } + + clipAgainstPlane(*o_vertices, &tempVertices, 1, halfWidth, settings); + clipAgainstPlane(tempVertices, o_vertices, 1, -halfWidth, settings); + clipAgainstPlane(*o_vertices, &tempVertices, 2, halfHeight, settings); + clipAgainstPlane(tempVertices, o_vertices, 2, -halfHeight, settings); + clipAgainstPlane(*o_vertices, &tempVertices, 3, near, settings); + clipAgainstPlane(tempVertices, o_vertices, 4, far, settings); +} + +float Path1EEClipAlgorithm::getValueByPlane(const Path1ClipVertex& v, + const int& plane) { + switch (plane) { + case 1: + return v.position.x; // x plane + case 2: + return v.position.y; // y plane + case 3: // z near + case 4: + return v.position.z; // z far + default: + return 0; + } +} + +bool Path1EEClipAlgorithm::isInside(const int& plane, const float& v, + const float& w, + const float& planeLimitValue) { + switch (plane) { + case 3: + return v <= planeLimitValue; // near z plane + case 4: + return v >= planeLimitValue; // far z plane + default: + return (planeLimitValue < 0) ? (v >= planeLimitValue * w) + : (v <= planeLimitValue * w); + } +} + +void Path1EEClipAlgorithm::clipAgainstPlane( + const std::vector& original, + std::vector* clipped, const int& plane, + const float& planeLimitValue, + const Path1EEClipAlgorithmSettings& settings) { + clipped->clear(); + + for (u32 i = 0; i < original.size(); i++) { + auto a = original.at(i); + auto b = original.at((i + 1) % original.size()); + auto apx = getValueByPlane(a, plane); + auto bpx = getValueByPlane(b, plane); + auto aIsInside = isInside(plane, apx, a.position.w, planeLimitValue); + auto bIsInside = isInside(plane, bpx, b.position.w, planeLimitValue); + + if (aIsInside) { + clipped->push_back(a); + } + + if (aIsInside != bIsInside) { + auto p = + (plane >= 3) + ? (planeLimitValue - a.position.z) / (b.position.z - a.position.z) + : (-a.position.w * planeLimitValue + apx) / + ((b.position.w - a.position.w) * planeLimitValue - + (bpx - apx)); + + Path1ClipVertex nb = { + Vec4::getByLerp(a.position, b.position, p), + settings.lerpNormals ? Vec4::getByLerp(a.normal, b.normal, p) + : Vec4(), + settings.lerpTexCoords ? Vec4::getByLerp(a.st, b.st, p) : Vec4(), + settings.lerpColors ? Vec4::getByLerp(a.color, b.color, p) : Vec4(), + }; + + clipped->push_back(nb); + } + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/paths/path1/path1.cpp b/engine/src/renderer/core/paths/path1/path1.cpp new file mode 100644 index 0000000..072c322 --- /dev/null +++ b/engine/src/renderer/core/paths/path1/path1.cpp @@ -0,0 +1,82 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/paths/path1/path1.hpp" + +namespace Tyra { + +Path1::Path1() { + doubleBufferPacket = packet2_create(2, P2_TYPE_NORMAL, P2_MODE_CHAIN, true); +} +Path1::~Path1() { packet2_free(doubleBufferPacket); } + +u32 Path1::uploadProgram(VU1Program* program, const u32& address) { + // TYRA_LOG("Uploading VU1 program. Size: ", program->getProgramSize(), + // ", name: ", program->getStringName(), ", address:", address); + + auto packetSize = program->getPacketSize() + 1; // + end tag + + packet2_t* packet2 = + packet2_create(packetSize, P2_TYPE_NORMAL, P2_MODE_CHAIN, 1); + + packet2_vif_add_micro_program(packet2, address, program->getStart(), + program->getEnd()); + + packet2_utils_vu_add_end_tag(packet2); + + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + dma_channel_send_packet2(packet2, DMA_CHANNEL_VIF1, true); + dma_channel_wait(DMA_CHANNEL_VIF1, 0); + + program->setDestinationAddress(address); + + packet2_free(packet2); + + return address + program->getProgramSize(); +} + +packet2_t* Path1::createProgramsCache(VU1Program** programs, const u32& count, + const u32& address) { + u32 packetSize = 1; // + end tag + for (u32 i = 0; i < count; i++) { + packetSize += programs[i]->getPacketSize(); + } + + packet2_t* packet2 = + packet2_create(packetSize, P2_TYPE_NORMAL, P2_MODE_CHAIN, 1); + + u32 currentAddr = address; + for (u32 i = 0; i < count; i++) { + programs[i]->setDestinationAddress(currentAddr); + packet2_vif_add_micro_program(packet2, currentAddr, programs[i]->getStart(), + programs[i]->getEnd()); + currentAddr += programs[i]->getProgramSize() + 1; + } + + packet2_utils_vu_add_end_tag(packet2); + + return packet2; +} + +/** Set double buffer settings */ +void Path1::setDoubleBuffer(const u16& startingAddress, const u16& bufferSize) { + packet2_reset(doubleBufferPacket, false); + packet2_utils_vu_add_double_buffer(doubleBufferPacket, startingAddress, + bufferSize); + + // TYRA_LOG("VU1 double buffer: starting addr: ", startingAddress, + // ", offset: ", startingAddress + bufferSize, ", size: ", + // bufferSize); + + packet2_utils_vu_add_end_tag(doubleBufferPacket); + dma_channel_send_packet2(doubleBufferPacket, DMA_CHANNEL_VIF1, true); +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/paths/path1/programs/draw_finish/draw_finish.vclpp b/engine/src/renderer/core/paths/path1/programs/draw_finish/draw_finish.vclpp new file mode 100644 index 0000000..95e687f --- /dev/null +++ b/engine/src/renderer/core/paths/path1/programs/draw_finish/draw_finish.vclpp @@ -0,0 +1,45 @@ +; ______ ____ ___ +; | \/ ____| |___| +; | | | \ | | +;--------------------------------------------------------------- +; Copyright 2022, tyra - https://github.com/h4570/tyra +; Licenced under Apache License 2.0 +; Sandro Sobczyński +; +;--------------------------------------------------------------- +; Needed for synchronization with draw_wait_finish() +;--------------------------------------------------------------- + +.syntax new +.name VU1DrawFinish +.vu +.init_vf_all +.init_vi_all + +#include "src/renderer/core/paths/path1/programs/vcl_sml.i" +#include "src/renderer/core/paths/path1/programs/tyra_macros.i" +#include "inc/renderer/3d/pipeline/std/core/path1/programs/stdpip_vu1_shared_defines.h" + +--enter +--endenter + +#vuprog VU1DrawFinish + + LoadTyraStaticData{ gifSetTag } + + xtop buffer + lq drawFinishTag, 0(buffer) + lq primTag, 1(buffer) + + iaddiu kickAddress, buffer, 2 + + sq gifSetTag, 0(kickAddress) + sq drawFinishTag, 1(kickAddress) + sq primTag, 2(kickAddress) + + xgkick kickAddress + +#endvuprog + +--exit +--endexit diff --git a/engine/src/renderer/core/paths/path1/programs/draw_finish/vu1_draw_finish.cpp b/engine/src/renderer/core/paths/path1/programs/draw_finish/vu1_draw_finish.cpp new file mode 100644 index 0000000..76cd2e0 --- /dev/null +++ b/engine/src/renderer/core/paths/path1/programs/draw_finish/vu1_draw_finish.cpp @@ -0,0 +1,37 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "debug/debug.hpp" +#include "renderer/core/paths/path1/programs/draw_finish/vu1_draw_finish.hpp" + +extern u32 VU1DrawFinish_CodeStart __attribute__((section(".vudata"))); +extern u32 VU1DrawFinish_CodeEnd __attribute__((section(".vudata"))); + +namespace Tyra { + +VU1DrawFinish::VU1DrawFinish() + : VU1Program(&VU1DrawFinish_CodeStart, &VU1DrawFinish_CodeEnd) {} + +VU1DrawFinish::~VU1DrawFinish() {} + +std::string VU1DrawFinish::getStringName() const { + return std::string("Draw finish"); +} + +void VU1DrawFinish::addTag(packet2_t* packet, prim_t* prim) const { + packet2_utils_vu_open_unpack(packet, 0, true); + packet2_utils_gs_add_draw_finish_giftag(packet); + packet2_utils_gs_add_prim_giftag( + packet, prim, 1, ((u64)GIF_REG_RGBAQ) << 0 | ((u64)GIF_REG_XYZ2) << 4, 2, + 0); + packet2_utils_vu_close_unpack(packet); +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/paths/path1/programs/tyra_macros.i b/engine/src/renderer/core/paths/path1/programs/tyra_macros.i new file mode 100644 index 0000000..4b4214c --- /dev/null +++ b/engine/src/renderer/core/paths/path1/programs/tyra_macros.i @@ -0,0 +1,99 @@ +;//-------------------------------------------------------------------------------- +;// Tyra's standard macros library +;//-------------------------------------------------------------------------------- + +;//--------------------------------------------------------- +;// LoadTyraStaticData - Load "Set" gif tag +;//--------------------------------------------------------- +#macro LoadTyraStaticData: t_gifSetTagName + lq t_gifSetTagName, VU1_SET_GIFTAG_ADDR(vi00) +#endmacro + +;//--------------------------------------------------------- +;// LoadTyraLightMatrix - Loads single color. Color is placed in 4th slot of Lights matrix +;//--------------------------------------------------------- +#macro LoadTyraSingleColor: t_singleColor, t_singleColorEnabled, t_singleColorAddr, t_optionsAddr + lq t_singleColor, t_singleColorAddr(vi00) + ilw.x t_singleColorEnabled, t_optionsAddr(vi00) +#endmacro + +;//--------------------------------------------------------- +;// LoadTyraTags - Load lod, texture buffer and clut +;// 1 - GIF tag - texture LOD +;// 2 - GIF tag - texture buffer & CLUT +;//--------------------------------------------------------- +#macro LoadTyraTags: t_lodGifTag, t_texBufferClutGifTag, t_lodAddr, t_ClutAddr + lq t_lodGifTag, t_lodAddr(vi00) + lq t_texBufferClutGifTag, t_ClutAddr(vi00) +#endmacro + +;//--------------------------------------------------------- +;// LoadTyraBufferTags - Load scales and prim tag +;// 1 - float : X, Y, Z - scale vector that we will use to scale the verts after projecting them, float : W - vert count. +;// 2 - GIF tag - tell GS how many data we will send +;//--------------------------------------------------------- +#macro LoadTyraBufferTags: t_scale, t_primTag, t_buffer + lq.xyz t_scale, 0(t_buffer) + lq t_primTag, 1(t_buffer) +#endmacro + +;//--------------------------------------------------------- +;// LoadTyraDirectionalLights - Load directions and colors. All are always present +;// - 3 directional lights directions +;// - 3 directional lights colors + ambient color +;//--------------------------------------------------------- +#macro LoadTyraDirectionalLights: t_lightMatrix, t_lightDirections, t_lightsColors, t_ambientColor, t_dirOffset, t_colorOffset, t_matrixOffset + lq.xyz t_lightMatrix[0], t_matrixOffset+0(vi00) + lq.xyz t_lightMatrix[1], t_matrixOffset+1(vi00) + lq.xyz t_lightMatrix[2], t_matrixOffset+2(vi00) + lq.xyz t_lightDirections[0], t_dirOffset(vi00) + lq.xyz t_lightDirections[1], t_dirOffset+1(vi00) + lq.xyz t_lightDirections[2], t_dirOffset+2(vi00) + lq.xyz t_lightsColors[0], t_colorOffset(vi00) + lq.xyz t_lightsColors[1], t_colorOffset+1(vi00) + lq.xyz t_lightsColors[2], t_colorOffset+2(vi00) + lq.xyz t_ambientColor, t_colorOffset+3(vi00) +#endmacro + +;//--------------------------------------------------------- +;// StoreTyraGifTags - Store gif tags. +;// Not using sqi instruction, because VCL cannot optimize it. +;// Primtag contains information about how many polys we will send +;//--------------------------------------------------------- +#macro StoreTyraGifTags: t_gifSetTag, t_lodGifTag, t_texBufferClutGifTag, t_primTag, t_destAddress + sq t_gifSetTag, 0(t_destAddress) + sq t_lodGifTag, 1(t_destAddress) + sq t_gifSetTag, 2(t_destAddress) + sq t_texBufferClutGifTag, 3(t_destAddress) + sq t_primTag, 4(t_destAddress) + iaddiu t_destAddress, t_destAddress, 5 +#endmacro + +;//--------------------------------------------------------- +;// CalculateLights - Based on Dr Fortuna's work +;// +;// 1. Transform by the rotation part of the world matrix +;// 2. "Transform" the normal by the light direction matrix +;// 3. Four intensities, one for each light. +;// 4. Clamp the intensity to 0..1 +;// 5. Transform the intensities by the light colour matrix +;// 6. Load 128 and put it into the alpha value +;// 7. Clamp result to 0-128 values +;// 8. And write to the output buffer +;//--------------------------------------------------------- +#macro CalculateTyraDirectionalLights: t_outputColor, t_normal, t_lightDirections, t_lightColors, t_lightMatrix, t_ambientColor + mul.xyz acc, t_lightMatrix[0], t_normal[x] + madd.xyz acc, t_lightMatrix[1], t_normal[y] + madd.xyz t_normal, t_lightMatrix[2], t_normal[z] + mula.xyz acc, t_lightDirections[0], t_normal[x] + madd.xyz acc, t_lightDirections[1], t_normal[y] + madd.xyz t_outputColor, t_lightDirections[2], t_normal[z] + mini.xyz t_outputColor, t_outputColor, vf00[w] + max.xyz t_outputColor, t_outputColor, vf00[x] + mula.xyz acc, t_lightColors[0], t_outputColor[x] + madda.xyz acc, t_lightColors[1], t_outputColor[y] + madda.xyz acc, t_lightColors[2], t_outputColor[z] + madd.xyz t_outputColor, t_ambientColor, vf00[w] + loi 128 + addi.w t_outputColor, vf00, i +#endmacro diff --git a/engine/src/renderer/core/paths/path1/programs/vcl_sml.i b/engine/src/renderer/core/paths/path1/programs/vcl_sml.i new file mode 100644 index 0000000..d1bb727 --- /dev/null +++ b/engine/src/renderer/core/paths/path1/programs/vcl_sml.i @@ -0,0 +1,1432 @@ +;//-------------------------------------------------------------------------------- +;// VCLSML - VCL Standard Macros Library +;// Version 1.4 +;// +;// Geoff Audy, January 17th 2002 Initial macro set +;// Geoff Audy, March 26th 2002 Fixed some macros that were broken +;// Geoff Audy, March 27th 2002 Added some macros, most from Colin Hughes (SCEE) +;// Geoff Audy, April 2002 Added some macros, from Dave Etherton (Angel +;// Studios) +;// Geoff Audy, May 16th 2002 Added the macro "MatrixMultiplyVertexW1", from +;// Mike Healey (Rebellion) +;// Geoff Audy, June 2002 Added "MatrixInverse" from Adrian Stephen +;// (Luxoflux, via newsgroup) +;// Geoff Audy, July 2002 Added macros "Arccos", "QuaternionSlerp" and +;// "DistanceVertexToPlane" +;// Geoff Audy, October 7th 2002 Added macros "VertexLightDir3W1" and +;// "VertexLightDir3AmbW1" +;// Geoff Audy, March 19th 2004 Added a better accuracy "Arccos", from +;// Morten Mikkelsen (IO Interactive) +;// Geoff Audy, March 19th 2004 Added macro "Arcsin", from Morten Mikkelsen +;// (IO Interactive) +;// Geoff Audy, June 10th 2004 Fixed a bug in the macro "VectorDotProductACC". +;// "[z]" was missing at the end of the last line +;// Sandro S., June 18th 2022 Thank you guys for your work... +;// +;// Copyright (C) 2002-2004, Sony Computer Entertainment America Inc. +;// All rights reserved. +;// +;// Note: Some macros generate the following temporary variables: +;// vclsmlftemp: Temporary float register +;// vclsmlitemp: Temporary integer register +;// Some macros generate more temporary variables +;//-------------------------------------------------------------------------------- + +;//-------------------------------------------------------------------- +;// MatrixLoad - Load "matrix" from VU mem location "vumemlocation" + +;// "offset" +;//-------------------------------------------------------------------- +#macro MatrixLoad: matrix, offset, vumemlocation + lq matrix[0], offset+0(vumemlocation) + lq matrix[1], offset+1(vumemlocation) + lq matrix[2], offset+2(vumemlocation) + lq matrix[3], offset+3(vumemlocation) +#endmacro + +;//-------------------------------------------------------------------- +;// MatrixSave - Save "matrix" to VU mem location "vumemlocation" + +;// "offset" +;//-------------------------------------------------------------------- +#macro MatrixSave: matrix,offset,vumemlocation + sq matrix[0], offset+0(vumemlocation) + sq matrix[1], offset+1(vumemlocation) + sq matrix[2], offset+2(vumemlocation) + sq matrix[3], offset+3(vumemlocation) +#endmacro + +;//-------------------------------------------------------------------- +;// MatrixIdentity - Set "matrix" to be an identity matrix +;// Thanks to Colin Hughes (SCEE) for that one +;//-------------------------------------------------------------------- +#macro MatrixIdentity: matrix + add.x matrix[0], vf00, vf00[w] + mfir.yzw matrix[0], vi00 + + mfir.xzw matrix[1], vi00 + add.y matrix[1], vf00, vf00[w] + + mr32 matrix[2], vf00 + + max matrix[3], vf00, vf00 +#endmacro + +;//-------------------------------------------------------------------- +;// MatrixCopy - Copy "matrixsrc" to "matrixdest" +;// Thanks to Colin Hughes (SCEE) for that one +;//-------------------------------------------------------------------- +#macro MatrixCopy: matrixdest, matrixsrc + max matrixdest[0], matrixsrc[0], matrixsrc[0] + move matrixdest[1], matrixsrc[1] + max matrixdest[2], matrixsrc[2], matrixsrc[2] + move matrixdest[3], matrixsrc[3] +#endmacro + +;//-------------------------------------------------------------------- +;// MatrixSwap - Swap the content of "matrix1" and "matrix2" +;// The implementation seems lame, but VCL will convert moves to maxes +;// if it sees fit +;//-------------------------------------------------------------------- +#macro MatrixSwap: matrix1, matrix2 + move vclsmlftemp, matrix1[0] + move matrix1[0], matrix2[0] + move matrix2[0], vclsmlftemp + + move vclsmlftemp, matrix1[1] + move matrix1[1], matrix2[1] + move matrix2[1], vclsmlftemp + + move vclsmlftemp, matrix1[2] + move matrix1[2], matrix2[2] + move matrix2[2], vclsmlftemp + + move vclsmlftemp, matrix1[3] + move matrix1[3], matrix2[3] + move matrix2[3], vclsmlftemp +#endmacro + +;//-------------------------------------------------------------------- +;// MatrixTranspose - Transpose "matrixsrc" to "matresult". It is safe +;// for "matrixsrc" and "matresult" to be the same. +;// Thanks to Colin Hughes (SCEE) for that one +;// Had to modify it though, it was too... smart. +;//-------------------------------------------------------------------- +#macro MatrixTranspose: matresult, matrixsrc + mr32.y vclsmlftemp, matrixsrc[1] + add.z matresult[1], vf00, matrixsrc[2][y] + move.y matresult[2], vclsmlftemp + mr32.y vclsmlftemp, matrixsrc[0] + add.z matresult[0], vf00, matrixsrc[2][x] + mr32.z vclsmlftemp, matrixsrc[1] + mul.w matresult[1], vf00, matrixsrc[3][y] + mr32.x vclsmlftemp, matrixsrc[0] + add.y matresult[0], vf00, matrixsrc[1][x] + move.x matresult[1], vclsmlftemp + mul.w vclsmlftemp, vf00, matrixsrc[3][z] + mr32.z matresult[3], matrixsrc[2] + move.w matresult[2], vclsmlftemp + mr32.w vclsmlftemp, matrixsrc[3] + add.x matresult[3], vf00, matrixsrc[0][w] + move.w matresult[0], vclsmlftemp + mr32.y matresult[3], vclsmlftemp + add.x matresult[2], vf00, vclsmlftemp[y] + + move.x matresult[0], matrixsrc[0] ;// These 4 instructions will be + move.y matresult[1], matrixsrc[1] ;// removed if "matrixsrc" and + move.z matresult[2], matrixsrc[2] ;// "matresult" are the same + move.w matresult[3], matrixsrc[3] ;// +#endmacro + +;//-------------------------------------------------------------------- +;// MatrixMultiply - Multiply 2 matrices, "matleft" and "matright", and +;// output the result in "matresult". Dont forget matrix multipli- +;// cations arent commutative, i.e. left X right wont give you the +;// same result as right X left. +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- +#macro MatrixMultiply: matresult, matleft, matright + mul acc, matright[0], matleft[0][x] + madd acc, matright[1], matleft[0][y] + madd acc, matright[2], matleft[0][z] + madd matresult[0], matright[3], matleft[0][w] + mul acc, matright[0], matleft[1][x] + madd acc, matright[1], matleft[1][y] + madd acc, matright[2], matleft[1][z] + madd matresult[1], matright[3], matleft[1][w] + mul acc, matright[0], matleft[2][x] + madd acc, matright[1], matleft[2][y] + madd acc, matright[2], matleft[2][z] + madd matresult[2], matright[3], matleft[2][w] + mul acc, matright[0], matleft[3][x] + madd acc, matright[1], matleft[3][y] + madd acc, matright[2], matleft[3][z] + madd matresult[3], matright[3], matleft[3][w] +#endmacro + +;//---------------------------------------------------- +;// MatrixInverse - Does a full-fledge matrix inversion +;// of "matsrc", and output the result to "matdest". +;// Thanks to Adrian Stephen (Luxoflux) for that one +;// (via newsgroup) +;// +;// Note: ACC and Q registers are modified +;//---------------------------------------------------- +#macro MatrixInverse: matdest, matsrc + opmula.xyz acc, matsrc[2], matsrc[3] ;c0 = src.z.xyz() ^ src.w.xyz(); + opmsub.xyz c0, matsrc[3], matsrc[2] + mul.xyz acc, matsrc[2], matsrc[3][w] ;c1 = src.z.xyz() * src.w.w() - src.w.xyz() * src.z.w(); + msub.xyz c1, matsrc[3], matsrc[2][w] + + mul.xyz t0, c0, matsrc[1] ;dst.x = Vector((src.y.xyz() ^ c1) + c0 * src.y.w()).setw(-DotProd(c0,src.y.xyz())); + + opmula.xyz acc, matsrc[1], c1 + madd.xyz acc, c0, matsrc[1][w] + opmsub.xyz matdest[0], c1, matsrc[1] + + mul.w acc, vf00, vf00[x] + msub.w acc, vf00, t0[x] + msub.w acc, vf00, t0[y] + msub.w matdest[0], vf00, t0[z] + + mul.xyz t0, c0, matsrc[0] ;dst.y = Vector((c1 ^ src.x.xyz()) - c0 * src.x.w()).setw( DotProd(c0,src.x.xyz())); + + opmula.xyz acc, c1, matsrc[0] + msub.xyz acc, c0, matsrc[0][w] + opmsub.xyz matdest[1], matsrc[0], c1 + + mul det, matsrc[0], matdest[0] ; for determinant calc + + mul.w acc, vf00, t0[x] + madd.w acc, vf00, t0[y] + madd.w matdest[1], vf00, t0[z] + + add.w acc, det, det[x] + madd.w acc, vf00, det[y] + madd.w det, vf00, det[z] + + opmula.xyz acc, matsrc[0], matsrc[1] ;c0 = src.x.xyz() ^ src.y.xyz(); + opmsub.xyz c0, matsrc[1], matsrc[0] + mul.xyz acc, matsrc[0], matsrc[1][w] ;c1 = src.x.xyz() * src.y.w() - src.y.xyz() * src.x.w(); + msub.xyz c1, matsrc[1], matsrc[0][w] + + div Q, vf00[w], det[w] + + mul.xyz t0, c0, matsrc[3] ;dst.z = Vector((src.w.xyz() ^ c1) + c0 * src.w.w()).setw(-DotProd(c0,src.w.xyz())); + + opmula.xyz acc, matsrc[3], c1 + madd.xyz acc, c0, matsrc[3][w] + opmsub.xyz matdest[2], c1, matsrc[3] + + mul.w acc, vf00, vf00[x] + msub.w acc, vf00, t0[x] + msub.w acc, vf00, t0[y] + msub.w matdest[2], vf00, t0[z] + + mul.xyz t0, c0, matsrc[2] ;dst.w = Vector((c1 ^ src.z.xyz()) - c0 * src.z.w()).setw( DotProd(c0,src.z.xyz())); + + opmula.xyz acc, c1, matsrc[2] + msub.xyz acc, c0, matsrc[2][w] + opmsub.xyz matdest[3], matsrc[2], c1 + + mul.w acc, vf00, t0[x] + madd.w acc, vf00, t0[y] + madd.w matdest[3], vf00, t0[z] + + mul matdest[0], matdest[0], Q + mul matdest[1], matdest[1], Q + mul matdest[2], matdest[2], Q + mul matdest[3], matdest[3], Q +#endmacro + +;//-------------------------------------------------------------------- +;// LocalizeLightMatrix - Transform the light matrix "lightmatrix" into +;// local space, as described by "matrix", and output the result in +;// "locallightmatrix" +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- +#macro LocalizeLightMatrix: locallightmatrix, matrix, lightmatrix + mul acc, lightmatrix[0], matrix[0][x] + madd acc, lightmatrix[1], matrix[0][y] + madd acc, lightmatrix[2], matrix[0][z] + madd locallightmatrix[0], lightmatrix[3], matrix[0][w] + + mul acc, lightmatrix[0], matrix[1][x] + madd acc, lightmatrix[1], matrix[1][y] + madd acc, lightmatrix[2], matrix[1][z] + madd locallightmatrix[1], lightmatrix[3], matrix[1][w] + + mul acc, lightmatrix[0], matrix[2][x] + madd acc, lightmatrix[1], matrix[2][y] + madd acc, lightmatrix[2], matrix[2][z] + madd locallightmatrix[2], lightmatrix[3], matrix[2][w] + + move locallightmatrix[3], lightmatrix[3] +#endmacro + +;//-------------------------------------------------------------------- +;// MatrixMultiplyVertex - Multiply "matrix" by "vertex", and output +;// the result in "vertexresult" +;// +;// Note: Apply rotation, scale and translation +;// Note: ACC register is modified +;//-------------------------------------------------------------------- +#macro MatrixMultiplyVertex: vertexresult, matrix, vertex + mul acc, matrix[0], vertex[x] + madd acc, matrix[1], vertex[y] + madd acc, matrix[2], vertex[z] + madd vertexresult, matrix[3], vertex[w] +#endmacro + +;//-------------------------------------------------------------------- +;// MatrixMultiplyVertexW1 - Multiply "matrix" by "vertex", and output +;// the result in "vertexresult". Assumes 1.f for W field, which will +;// allow for better optimization in most cases, and leaves W free for +;// other uses (ADC flags, scale, etc). +;// +;// Note: Apply rotation, scale and translation +;// Note: ACC register is modified +;//-------------------------------------------------------------------- +#macro MatrixMultiplyVertexW1: vertexresult, matrix, vertex + mul acc, matrix[3], vf00[w] + madd acc, matrix[0], vertex[x] + madd acc, matrix[1], vertex[y] + madd vertexresult, matrix[2], vertex[z] +#endmacro + +;//--------------------------------------------------------------------- +;// MatrixMultiplyVertexXYZ1 - Multiply "matrix" by "vertex", and output +;// the result in "vertexresult" +;// +;// Note: Apply rotation, scale and translation +;// Note: ACC register is modified +;//--------------------------------------------------------------------- +#macro MatrixMultiplyVertexXYZ1: vertexresult, matrix, vertex + mul acc, matrix[0], vertex[x] + madd acc, matrix[1], vertex[y] + madd acc, matrix[2], vertex[z] + madd vertexresult, matrix[3], vf00[w] +#endmacro + +;//-------------------------------------------------------------------- +;// MatrixMultiplyVector - Multiply "matrix" by "vector", and output +;// the result in "vectorresult" +;// +;// Note: Apply rotation and scale, but no translation +;// Note: ACC register is modified +;//-------------------------------------------------------------------- +#macro MatrixMultiplyVector: vectorresult, matrix, vector + mul acc, matrix[0], vector[x] + madd acc, matrix[1], vector[y] + madd vectorresult, matrix[2], vector[z] +#endmacro + +;//-------------------------------------------------------------------- +;// VectorLoad - Load "vector" from VU mem location "vumemlocation" + +;// "offset" +;//-------------------------------------------------------------------- +#macro VectorLoad: vector, offset, vumemlocation + lq vector, offset(vumemlocation) +#endmacro + +;//-------------------------------------------------------------------- +;// VectorSave - Save "vector" to VU mem location "vumemlocation" + +;// "offset" +;//-------------------------------------------------------------------- +#macro VectorSave: vector, offset, vumemlocation + sq vector, offset(vumemlocation) +#endmacro + +;//-------------------------------------------------------------------- +;// VectorAdd - Add 2 vectors, "vector1" and "vector2" and output the +;// result in "vectorresult" +;//-------------------------------------------------------------------- +#macro VectorAdd: vectorresult, vector1, vector2 + add vectorresult, vector1, vector2 +#endmacro + +;//-------------------------------------------------------------------- +;// VectorSub - Subtract "vector2" from "vector1", and output the +;// result in "vectorresult" +;//-------------------------------------------------------------------- +#macro VectorSub: vectorresult, vector1, vector2 + sub vectorresult, vector1, vector2 +#endmacro + +;//-------------------------------------------------------------------- +;// VertexLoad - Load "vertex" from VU mem location "vumemlocation" + +;// "offset" +;//-------------------------------------------------------------------- +#macro VertexLoad: vertex, offset, vumemlocation + lq vertex, offset(vumemlocation) +#endmacro + +;//-------------------------------------------------------------------- +;// VertexSave - Save "vertex" to VU mem location "vumemlocation" + +;// "offset" +;//-------------------------------------------------------------------- +#macro VertexSave: vertex, offset, vumemlocation + sq vertex, offset(vumemlocation) +#endmacro + +;//-------------------------------------------------------------------- +;// VertexPersCorr - Apply perspective correction onto "vertex" and +;// output the result in "vertexoutput" +;// +;// Note: Q register is modified +;//-------------------------------------------------------------------- +#macro VertexPersCorr: vertexoutput, vertex + div q, vf00[w], vertex[w] + mul.xyz vertexoutput, vertex, q +; mul vertexoutput, vertex, q +#endmacro + +;//-------------------------------------------------------------------- +;// VertexPersCorrST - Apply perspective correction onto "vertex" and +;// "st", and output the result in "vertexoutput" and "stoutput" +;// +;// Note: Q register is modified +;//-------------------------------------------------------------------- +#macro VertexPersCorrST: vertexoutput, stoutput, vertex, st + div q, vf00[w], vertex[w] + mul.xyz vertexoutput, vertex, q + move.w vertexoutput, vertex +; mul vertexoutput, vertex, q + mul stoutput, st, q +#endmacro + +;//-------------------------------------------------------------------- +;// VertexFPtoGsXYZ2 - Convert an XYZW, floating-point vertex to GS +;// XYZ2 format (ADC bit isnt set) +;//-------------------------------------------------------------------- +#macro VertexFpToGsXYZ2: outputxyz, vertex + ftoi4.xy outputxyz, vertex + ftoi0.z outputxyz, vertex + mfir.w outputxyz, vi00 +#endmacro + +;//-------------------------------------------------------------------- +;// VertexFPtoGsXYZ2Adc - Convert an XYZW, floating-point vertex to GS +;// XYZ2 format (ADC bit is set) +;//-------------------------------------------------------------------- +#macro VertexFpToGsXYZ2Adc: outputxyz, vertex + ftoi4.xy outputxyz, vertex + ftoi0.z outputxyz, vertex + ftoi15.w outputxyz, vf00 +#endmacro + +;//-------------------------------------------------------------------- +;// VertexFpToGsXYZF2 - Convert an XYZF, floating-point vertex to GS +;// XYZF2 format (ADC bit isnt set) +;//-------------------------------------------------------------------- +#macro VertexFpToGsXYZF2: outputxyz, vertex + ftoi4 outputxyz, vertex +#endmacro + +;//-------------------------------------------------------------------- +;// VertexFpToGsXYZF2Adc - Convert an XYZF, floating-point vertex to GS +;// XYZF2 format (ADC bit is set) +;//-------------------------------------------------------------------- +#macro VertexFpToGsXYZF2Adc: outputxyz, vertex + ftoi4 outputxyz, vertex + mtir vclsmlitemp, outputxyz[w] + iaddiu vclsmlitemp, 0x7FFF + iaddi vclsmlitemp, 1 + mfir.w outputxyz, vclsmlitemp +#endmacro + +;//-------------------------------------------------------------------- +;// ColorFPtoGsRGBAQ - Convert an RGBA, floating-point color to GS +;// RGBAQ format +;//-------------------------------------------------------------------- +#macro ColorFPtoGsRGBAQ: outputrgba, color + ftoi0 outputrgba, color +#endmacro + +;//-------------------------------------------------------------------- +;// ColorGsRGBAQtoFP - Convert an RGBA, GS RGBAQ format to floating- +;// point color +;//-------------------------------------------------------------------- +#macro ColorGsRGBAQtoFP: outputrgba, color + itof0 outputrgba, color +#endmacro + +;//-------------------------------------------------------------------- +;// CreateGsPRIM - Create a GS-packed-format PRIM command, according to +;// a specified immediate value "prim" +;// +;// Note: Meant more for debugging purposes than for a final solution +;//-------------------------------------------------------------------- +#macro CreateGsPRIM: outputprim, prim + iaddiu vclsmlitemp, vi00, prim + mfir outputprim, vclsmlitemp +#endmacro + +;//-------------------------------------------------------------------- +;// CreateGsRGBA - Create a GS-packed-format RGBA command, according to +;// specified immediate values "r", "g", "b" and "a" (integer 0-255) +;// +;// Note: Meant more for debugging purposes than for a final solution +;//-------------------------------------------------------------------- +#macro CreateGsRGBA: outputrgba, r, g, b, a + iaddiu vclsmlitemp, vi00, r + mfir.x outputrgba, vclsmlitemp + iaddiu vclsmlitemp, vi00, g + mfir.y outputrgba, vclsmlitemp + iaddiu vclsmlitemp, vi00, b + mfir.z outputrgba, vclsmlitemp + iaddiu vclsmlitemp, vi00, a + mfir.w outputrgba, vclsmlitemp +#endmacro + +;//-------------------------------------------------------------------- +;// CreateGsSTQ - Create a GS-packed-format STQ command, according to +;// specified immediate values "s", "t" and "q" (floats) +;// +;// Note: I register is modified +;// Note: Meant more for debugging purposes than for a final solution +;//-------------------------------------------------------------------- +#macro CreateGsSTQ: outputstq, s, t, q + loi s + add.x outputstq, vf00, i + loi t + add.y outputstq, vf00, i + loi q + add.z outputstq, vf00, i +#endmacro + +;//-------------------------------------------------------------------- +;// CreateGsUV - Create a GS-packed-format VU command, according to +;// specified immediate values "u" and "v" (integer -32768 - 32768, +;// with 4 LSB as precision) +;// +;// Note: Meant more for debugging purposes than for a final solution +;//-------------------------------------------------------------------- +#macro CreateGsUV: outputuv, u, v + iaddiu vclsmlitemp, vi00, u + mfir.x outputuv, vclsmlitemp + iaddiu vclsmlitemp, vi00, v + mfir.y outputuv, vclsmlitemp +#endmacro + +;//-------------------------------------------------------------------- +;// CreateGsRGBA - Create a GS-packed-format RGBA command, according to +;// a specified immediate value "fog" (integer 0-255) +;// +;// Note: Meant more for debugging purposes than for a final solution +;//-------------------------------------------------------------------- +#macro CreateGsFOG: outputfog, fog + iaddiu vclsmlitemp, vi00, fog * 16 + mfir.w outputfog, vclsmlitemp +#endmacro + +;//-------------------------------------------------------------------- +;// CreateGifTag - Create a packed-mode giftag, according to specified +;// immediate values. Currently only support up to 4 registers. +;// +;// Note: I register is modified +;// Note: Definitely meant for debugging purposes, NOT for a final +;// solution +;//-------------------------------------------------------------------- +;// MIGHT NOT BE IMPLEMENTABLE AFTER ALL (AT LEAST NOT UNTIL VCL EVALUATES CONSTANTS!) +;// THAT WOULD HAVE BEEN KINDA COOL... DAMN. --GEOFF +;//#macro CreateGifTag outputgiftag,nloop,prim,nreg,reg1,reg2,reg3,reg4 +;// iaddiu vclsmlitemp, vi00, nloop + 0x8000 +;// mfir.x outputgiftag, vclsmlitemp +;// loi 0x00004000 + (prim * 0x8000) + (nreg * 0x10000000) +;// add.y outputgiftag, vf00, i +;// iaddiu vclsmlitemp, vi00, reg1 + (reg2 * 16) + (reg3 * 256) + (reg4 * 4096) +;// mfir.z outputgiftag, vclsmlitemp +;//#endmacro + +;//-------------------------------------------------------------------- +;// VectorDotProduct - Calculate the dot product of "vector1" and +;// "vector2", and output to "dotproduct"[x] +;//-------------------------------------------------------------------- +#macro VectorDotProduct: dotproduct, vector1, vector2 + mul.xyz dotproduct, vector1, vector2 + add.x dotproduct, dotproduct, dotproduct[y] + add.x dotproduct, dotproduct, dotproduct[z] +#endmacro + +;//-------------------------------------------------------------------- +;// VectorDotProductACC - Calculate the dot product of "vector1" and +;// "vector2", and output to "dotproduct"[x]. This one does it using +;// the ACC register which, depending on the case, might turn out to be +;// faster or slower. +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- +#macro VectorDotProductACC: dotproduct, vector1, vector2 + max Vector1111, vf00, vf00[w] + mul vclsmlftemp, vector1, vector2 + add.x acc, vclsmlftemp, vclsmlftemp[y] + madd.x dotproduct, Vector1111, vclsmlftemp[z] +#endmacro + +;//-------------------------------------------------------------------- +;// VectorCrossProduct - Calculate the cross product of "vector1" and +;// "vector2", and output to "vectoroutput" +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- +#macro VectorCrossProduct: vectoroutput, vector1, vector2 + opmula.xyz ACC, vector1, vector2 + opmsub.xyz vectoroutput, vector2, vector1 + sub.w vectoroutput, vf00, vf00 +#endmacro + +;//-------------------------------------------------------------------- +;// VectorNormalize - Bring the length of "vector" to 1.f, and output +;// it to "vectoroutput" +;// +;// Note: Q register is modified +;//-------------------------------------------------------------------- +#macro VectorNormalize: vecoutput, vector + mul.xyz vclsmlftemp, vector, vector + add.x vclsmlftemp, vclsmlftemp, vclsmlftemp[y] + add.x vclsmlftemp, vclsmlftemp, vclsmlftemp[z] + rsqrt q, vf00[w], vclsmlftemp[x] + sub.w vecoutput, vf00, vf00 + mul.xyz vecoutput, vector, q +#endmacro + +;//-------------------------------------------------------------------- +;// VectorNormalizeXYZ - Bring the length of "vector" to 1.f, and out- +;// put it to "vectoroutput". The "w" field isn't transfered. +;// +;// Note: Q register is modified +;//-------------------------------------------------------------------- +#macro VectorNormalizeXYZ: vecoutput, vector + mul.xyz vclsmlftemp, vector, vector + add.x vclsmlftemp, vclsmlftemp, vclsmlftemp[y] + add.x vclsmlftemp, vclsmlftemp, vclsmlftemp[z] + rsqrt q, vf00[w], vclsmlftemp[x] + mul.xyz vecoutput, vector, q +#endmacro + +;//-------------------------------------------------------------------- +;// VertexLightAmb - Apply ambient lighting "ambientrgba" to a vertex +;// of color "vertexrgba", and output the result in "outputrgba" +;//-------------------------------------------------------------------- +#macro VertexLightAmb: rgbaout,vertexrgba,ambientrgba + mul rgbaout, vertexrgba, ambientrgba +#endmacro + +;//-------------------------------------------------------------------- +;// VertexLightDir3 - Apply up to 3 directional lights contained in a +;// light matrix "lightmatrix" to a vertex of color "vertexrgba" and +;// having a normal "vertexnormal", and output the result in +;// "outputrgba" +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- +#macro VertexLightDir3: rgbaout, vertexrgba, vertexnormal, lightcolors, lightnormals + mul acc, lightnormals[0], vertexnormal[x] + madd acc, lightnormals[1], vertexnormal[y] + madd acc, lightnormals[2], vertexnormal[z] + madd rgbaout, lightnormals[3], vertexnormal[w] ;// Here "rgbaout" is the dot product for the 3 lights + max rgbaout, rgbaout, vf00[x] ;// Here "rgbaout" is the dot product for the 3 lights + mul acc, lightcolors[0], rgbaout[x] + madd acc, lightcolors[1], rgbaout[y] + madd rgbaout, lightcolors[2], rgbaout[z] ;// Here "rgbaout" is the light applied on the vertex + mul rgbaout, vertexrgba, rgbaout ;// Here "rgbaout" is the amount of light reflected by the vertex +#endmacro + +;//-------------------------------------------------------------------- +;// VertexLightDir3W1 - Apply up to 3 directional lights contained in a +;// light matrix "lightmatrix" to a vertex of color "vertexrgba" and +;// having a normal "vertexnormal", and output the result in +;// "outputrgba". Assumes 1.0 for the normal's W field +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- +#macro VertexLightDir3W1: rgbaout, vertexrgba, vertexnormal, lightcolors, lightnormals + mul acc, lightnormals[0], vertexnormal[x] + madd acc, lightnormals[1], vertexnormal[y] + madd acc, lightnormals[2], vertexnormal[z] + madd rgbaout, lightnormals[3], vf00[w] ;// Here "rgbaout" is the dot product for the 3 lights + max rgbaout, rgbaout, vf00[x] ;// Here "rgbaout" is the dot product for the 3 lights + mul acc, lightcolors[0], rgbaout[x] + madd acc, lightcolors[1], rgbaout[y] + madd rgbaout, lightcolors[2], rgbaout[z] ;// Here "rgbaout" is the light applied on the vertex + mul rgbaout, vertexrgba, rgbaout ;// Here "rgbaout" is the amount of light reflected by the vertex +#endmacro + +;//-------------------------------------------------------------------- +;// VertexLightDir3Amb - Apply up to 3 directional lights, plus an +;// ambient light contained in a light matrix "lightmatrix" to a vertex +;// of color "vertexrgba" and having a normal "vertexnormal", and +;// output the result in "outputrgba" +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- +#macro VertexLightDir3Amb: rgbaout, vertexrgba, vertexnormal, lightcolors, lightnormals + mul acc, lightnormals[0], vertexnormal[x] + madd acc, lightnormals[1], vertexnormal[y] + madd acc, lightnormals[2], vertexnormal[z] + madd rgbaout, lightnormals[3], vertexnormal[w] ;// Here "rgbaout" is the dot product for the 3 lights + max rgbaout, rgbaout, vf00[x] ;// Here "rgbaout" is the dot product for the 3 lights + mul acc, lightcolors[0], rgbaout[x] + madd acc, lightcolors[1], rgbaout[y] + madd acc, lightcolors[2], rgbaout[z] + madd rgbaout, lightcolors[3], rgbaout[w] ;// Here "rgbaout" is the light applied on the vertex + mul.xyz rgbaout, vertexrgba, rgbaout ;// Here "rgbaout" is the amount of light reflected by the vertex +#endmacro + +;//-------------------------------------------------------------------- +;// VertexLightDir3AmbW1 - Apply up to 3 directional lights, plus an +;// ambient light contained in a light matrix "lightmatrix" to a vertex +;// of color "vertexrgba" and having a normal "vertexnormal", and +;// output the result in "outputrgba". Assumes 1.0 for the normal's +;// W field. +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- +#macro VertexLightDir3AmbW1: rgbaout, vertexrgba, vertexnormal, lightcolors, lightnormals + mul acc, lightnormals[0], vertexnormal[x] + madd acc, lightnormals[1], vertexnormal[y] + madd acc, lightnormals[2], vertexnormal[z] + madd rgbaout, lightnormals[3], vf00[w] ;// Here "rgbaout" is the dot product for the 3 lights + max rgbaout, rgbaout, vf00[x] ;// Here "rgbaout" is the dot product for the 3 lights + mul acc, lightcolors[0], rgbaout[x] + madd acc, lightcolors[1], rgbaout[y] + madd acc, lightcolors[2], rgbaout[z] + madd rgbaout, lightcolors[3], rgbaout[w] ;// Here "rgbaout" is the light applied on the vertex + mul.xyz rgbaout, vertexrgba, rgbaout ;// Here "rgbaout" is the amount of light reflected by the vertex +#endmacro + +;//-------------------------------------------------------------------- +;// FogSetup - Set up fog "fogparams", by specifying "nearfog" and +;// "farfog". "fogparams" will afterward be ready to be used by fog- +;// related macros, like "VertexFogLinear" for example. +;// +;// Note: I register is modified +;//-------------------------------------------------------------------- +#macro FogSetup: fogparams, nearfogz, farfogz + sub fogparams, vf00, vf00 ;// Set XYZW to 0 + loi farfogz ;// + add.w fogparams, fogparams, i ;// fogparam[w] is farfogz + loi nearfogz + add.z fogparams, fogparams, fogparams[w] + sub.z fogparams, fogparams, i + loi 255.0 + add.xy fogparams, fogparams, i ;// fogparam[y] is 255.0 + sub.x fogparams, fogparams, vf00[w] ;// fogparam[x] is 254.0 + div q, fogparams[y], fogparams[z] + sub.z fogparams, fogparams, fogparams + add.z fogparams, fogparams, q ;// fogparam[z] is 255.f / (farfogz - nearfogz) +#endmacro + +;//-------------------------------------------------------------------- +;// VertexFogLinear - Apply fog "fogparams" to a vertex "xyzw", and +;// output the result in "xyzfoutput". "xyzw" [w] is assumed to be +;// the distance from the camera. "fogparams" must contain farfogz in +;// [w], and (255.f / (farfogz - nearfogz)) in [z]. "xyzfoutputf" [w] +;// will contain a float value between 0.0 and 255.0, inclusively. +;//-------------------------------------------------------------------- +#macro VertexFogLinear: xyzfoutput, xyzw, fogparams + move.xyz xyzfoutput, xyzw ;// XYZ part won't be modified + sub.w xyzfoutput, fogparams, xyzw[w] ;// fog = (farfogz - z) * 255.0 / + mul.w xyzfoutput, xyzfoutput, fogparams[z] ;// (farfogz - nearfogz) + max.w xyzfoutput, xyzfoutput, vf00[x] ;// Clamp fog values outside the range 0.0-255.0 + mini.w xyzfoutput, xyzfoutput, fogparams[y] ;// +#endmacro + +;//-------------------------------------------------------------------- +;// VertexFogRemove - Remove any effect of fog to "xyzf". "fogparams" +;// [ x ] must be set to 254.0. "xyzf" will be modified directly. +;//-------------------------------------------------------------------- +#macro VertexFogRemove: xyzf, fogparams + add.w xyzf, vf00, fogparams[x] ;// xyzw[w] = 1.0 + 254.0 = 255.0 = no fog +#endmacro + +;//-------------------------------------------------------------------- +;// PushInteger1 - Push "integer1" on "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PushInteger1: stackptr, integer1 + isubiu stackptr, stackptr, 1 + iswr.x integer1, (stackptr):VCLSML_STACK +#endmacro + +;//-------------------------------------------------------------------- +;// PushInteger2 - Push "integer1" and "integer2" on "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PushInteger2: stackptr, integer1, integer2 + isubiu stackptr, stackptr, 1 + iswr.x integer1, (stackptr):VCLSML_STACK + iswr.y integer2, (stackptr):VCLSML_STACK +#endmacro + +;//-------------------------------------------------------------------- +;// PushInteger3 - Push "integer1", "integer2" and "integer3" on +;// "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PushInteger3: stackptr, integer1, integer2, integer3 + isubiu stackptr, stackptr, 1 + iswr.x integer1, (stackptr):VCLSML_STACK + iswr.y integer2, (stackptr):VCLSML_STACK + iswr.z integer3, (stackptr):VCLSML_STACK +#endmacro + +;//-------------------------------------------------------------------- +;// PushInteger4 - Push "integer1", "integer2", "integer3" and +;// "integer4" on "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PushInteger4: stackptr, integer1, integer2, integer3, integer4 + isubiu stackptr, stackptr, 1 + iswr.x integer1, (stackptr):VCLSML_STACK + iswr.y integer2, (stackptr):VCLSML_STACK + iswr.z integer3, (stackptr):VCLSML_STACK + iswr.w integer4, (stackptr):VCLSML_STACK +#endmacro + +;//-------------------------------------------------------------------- +;// PopInteger1 - Pop "integer1" on "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PopInteger1: stackptr, integer1 + ilwr.x integer1, (stackptr):VCLSML_STACK + iaddiu stackptr, stackptr, 1 +#endmacro + +;//-------------------------------------------------------------------- +;// PopInteger2 - Pop "integer1" and "integer2" on "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PopInteger2: stackptr, integer1, integer2 + ilwr.y integer2, (stackptr):VCLSML_STACK + ilwr.x integer1, (stackptr):VCLSML_STACK + iaddiu stackptr, stackptr, 1 +#endmacro + +;//-------------------------------------------------------------------- +;// PopInteger3 - Pop "integer1", "integer2" and "integer3" on +;// "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PopInteger3: stackptr, integer1, integer2, integer3 + ilwr.z integer3, (stackptr):VCLSML_STACK + ilwr.y integer2, (stackptr):VCLSML_STACK + ilwr.x integer1, (stackptr):VCLSML_STACK + iaddiu stackptr, stackptr, 1 +#endmacro + +;//-------------------------------------------------------------------- +;// PopInteger4 - Pop "integer1", "integer2", "integer3" and +;// "integer4" on "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PopInteger4: stackptr, integer1, integer2, integer3, integer4 + ilwr.w integer4, (stackptr):VCLSML_STACK + ilwr.z integer3, (stackptr):VCLSML_STACK + ilwr.y integer2, (stackptr):VCLSML_STACK + ilwr.x integer1, (stackptr):VCLSML_STACK + iaddiu stackptr, stackptr, 1 +#endmacro + +;//-------------------------------------------------------------------- +;// PushMatrix - Push "matrix" onto the "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PushMatrix: stackptr, matrix + sq matrix[0], -1(stackptr):VCLSML_STACK + sq matrix[1], -2(stackptr):VCLSML_STACK + sq matrix[2], -3(stackptr):VCLSML_STACK + sq matrix[3], -4(stackptr):VCLSML_STACK + iaddi stackptr, stackptr, -4 +#endmacro + +;//-------------------------------------------------------------------- +;// PopMatrix - Pop "matrix" out of the "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PopMatrix: stackptr, matrix + lq matrix[0], 0(stackptr):VCLSML_STACK + lq matrix[1], 1(stackptr):VCLSML_STACK + lq matrix[2], 2(stackptr):VCLSML_STACK + lq matrix[3], 3(stackptr):VCLSML_STACK + iaddi stackptr, stackptr, 4 +#endmacro + +;//-------------------------------------------------------------------- +;// PushVector - Push "vector" onto the "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PushVector: stackptr, vector + sqd vector, (--stackptr):VCLSML_STACK +#endmacro + +;//-------------------------------------------------------------------- +;// PopVector - Pop "vector" out of the "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PopVector: stackptr, vector + lqi vector, (stackptr++):VCLSML_STACK +#endmacro + +;//-------------------------------------------------------------------- +;// PushVertex - Push "vector" onto the "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PushVertex: stackptr, vertex + sqd vertex, (--stackptr):VCLSML_STACK +#endmacro + +;//-------------------------------------------------------------------- +;// PopVertex - Pop "vertex" out of the "stackptr" +;// +;// Note: "stackptr" is updated +;//-------------------------------------------------------------------- +#macro PopVertex: stackptr, vertex + lqi vertex, (stackptr++):VCLSML_STACK +#endmacro + +;//-------------------------------------------------------------------- +;// AngleSinCos - Returns the sin and cos of up to 2 angles, which must +;// be contained in the X and Z elements of "angle". The sin/cos pair +;// will be contained in the X/Y elements of "sincos" for the first +;// angle, and Z/W for the second one. +;// Thanks to Colin Hughes (SCEE) for that one +;// +;// Note: ACC and I registers are modified, and a bunch of temporary +;// variables are created... Maybe bad for VCL register pressure +;//-------------------------------------------------------------------- +#macro AngleSinCos: angle, sincos + move.xz sincos, angle ; To avoid modifying the original angles... + + mul.w sincos, vf00, sincos[z] ; Copy angle from z to w + add.y sincos, vf00, sincos[x] ; Copy angle from x to y + + loi 1.570796 ; Phase difference for sin as cos ( PI/2 ) + sub.xz sincos, sincos, I ; + + abs sincos, sincos ; Mirror cos around zero + + max Vector1111, vf00, vf00[w] ; Initialise all 1s + + loi -0.159155 ; Scale so single cycle is range 0 to -1 ( *-1/2PI ) + mul ACC, sincos, I ; + + loi 12582912.0 ; Apply bias to remove fractional part + msub ACC, Vector1111, I ; + madd ACC, Vector1111, I ; Remove bias to leave original int part + + loi -0.159155 ; Apply original number to leave fraction range only + msub ACC, sincos, I ; + + loi 0.5 ; Ajust range: -0.5 to +0.5 + msub sincos, Vector1111, I ; + + abs sincos, sincos ; Clamp: 0 to +0.5 + + loi 0.25 ; Ajust range: -0.25 to +0.25 + sub sincos, sincos, I ; + + mul anglepower2, sincos, sincos ; a^2 + + loi -76.574959 ; + mul k4angle, sincos, I ; k4 a + + loi -41.341675 ; + mul k2angle, sincos, I ; k2 a + + loi 81.602226 ; + mul k3angle, sincos, I ; k3 a + + mul anglepower4, anglepower2, anglepower2 ; a^4 + mul k4angle, k4angle, anglepower2 ; k4 a^3 + mul ACC, k2angle, anglepower2 ; + k2 a^3 + + loi 39.710659 ; k5 a + mul k2angle, sincos, I ; + + mul anglepower8, anglepower4, anglepower4 ; a^8 + madd ACC, k4angle, anglepower4 ; + k4 a^7 + madd ACC, k3angle, anglepower4 ; + k3 a^5 + loi 6.283185 ; + madd ACC, sincos, I ; + k1 a + madd sincos, k2angle, anglepower8 ; + k5 a^9 +#endmacro + +;//-------------------------------------------------------------------- +;// ArcsinAccurate - Returns an accurate arcsin angle of a value, which +;// must be contained in the X element of "value". X element of "asin" +;// will contain, on return, the angle corresponding to the value. +;// Thanks to Morten Mikkelsen (IO Interactive) for that one +;// +;// Note: ACC, I registers are modified, and a bunch of temporary +;// variables are created... Maybe bad for VCL register pressure +;//-------------------------------------------------------------------- +#macro ArcsinAccurate: value, asin + + add.x acc, vf00, vf00[w] + msub.x Xc, value, value + sqrt Q, Xc[x] ; Xc = sqrt (1 - value * value) + add.x Xc, vf00, q + + mr32.w bflip, value ; Sign in w field of bflip + abs.x Xs, value ; Xs = fabs (value) + + loi 0x3f3504f3 + subi.x bflip, Xs, i ; bflip = (Xs - sqrt (0.5f)) * I + loi 0z7F800000 ; I = big float + mul.xw bflip, bflip, i ; + mini.xw bflip, bflip, vf00[w] ; Minf (&bflip, bflip, 1), Minf (&Sign, Sign, 1) sign should only be zeroif bflip is, so offs should not be affected by this case + + loi -1 ; I = -1.0 + max.xw bflip, bflip, i ; Maxf (&bflip, bflip, -1), Maxf (&Sign, Sign, -1) if x is already zero, then scaling by zero will not matter either when sign is applied + + add.x acc, vf00, bflip[w] + madd.x offs, bflip, bflip[w] ; offs = Sign + Sign * bflip + mul.x bflip, bflip, bflip[w] ; bflip *= Sign + + mini.x temp, Xc, Xs ; temp = fmin (Xc, Xs) + mul.x temp, temp, bflip ; temp *= bflip + + loi 0x3f490fdb ; + mul.x acc, offs, i ; MULA (offs, piquat) + + loi -1.0 ; s1 = -1 (0xbf800000) + madd.x acc, temp, i ; MADDA (s1, temp) + + mul.x x2, temp, temp ; x2 = temp * temp + loi 0xbe2aaaab ; s2 = -1 / 6 + mul.x S2, temp, i ; S2 = s2 * temp + + madd.x acc, S2, x2 ; MADDA (S2, x2) + + mul.x x3, temp, x2 ; x3 = temp * x2 + loi 0xbd36db6e ; s4 = -5 / 112 + mul.x S4, x3, i ; S4 = s4 * x3 + mul.x x5, x2, x3 ; x5 = x2 * x3 + mul.x x4, x2, x2 ; x4 = x2 * x2 + + loi 0xbcf8e38e ; s5 = -35 / 1152 + mul.x S5, x4, i ; S5 = s5 * x4 + + loi 0xbd99999a ; s3 = -3 / 40 + mul.x S3, x2, i ; S3 = s3 * x2 + + madd.x acc, S3, x3 ; MADDA (S3, x3) + madd.x acc, S4, x4 ; MADDA (S4, x4) + madd.x asin, S5, x5 ; MADD (&r, S5, x5) + +#endmacro + +;//-------------------------------------------------------------------- +;// ArccosAccurate - Returns an accurate arccos angle of a value, which +;// must be contained in the X element of "value". X element of "acos" +;// will contain, on return, the angle corresponding to the value. +;// Thanks to Morten Mikkelsen (IO Interactive) for that one +;// +;// Note: ACC, I registers are modified, and a bunch of temporary +;// variables are created... Maybe bad for VCL register pressure +;//-------------------------------------------------------------------- +#macro ArccosAccurate: value, acos + + add.x acc, vf00, vf00[w] + msub.x Xc, value, value + sqrt Q, Xc[x] ; Xc = sqrt (1 - value * value) + add.x Xc, vf00, q ; + + mr32.w bflip, value ; Sign in w field of bflip + abs.x Xs, value ; Xs = fabs (value) + + loi 0x3f3504f3 + subi.x bflip, Xs, i ; bflip = (Xs - sqrt (0.5f)) * I + loi 0z7F800000 ; I = big float + mul.xw bflip, bflip, i ; + mini.xw bflip, bflip, vf00[w] ; Minf (&bflip, bflip, 1), Minf (&Sign, Sign, 1) sign should only be zeroif bflip is, so offs should not be affected by this case + + loi -1 ; I = -1.0 + max.xw bflip, bflip, i ; Maxf (&bflip, bflip, -1), Maxf (&Sign, Sign, -1) if x is already zero, then scaling by zero will not matter either when sign is applied + + mul.x bflip, bflip, bflip[w] ; bflip *= sign + loi 2.0 + sub.w bflip, bflip, i ; (Sign - 2) + add.x offs, bflip, bflip[w] ; offs = (Sign - 2) + Sign * bflip + + mini.x temp, Xc, Xs ; temp = fmin (Xc, Xs) + mul.x temp, temp, bflip ; temp *= bflip + + loi 0xbf490fdb + mul.x acc, offs, i ; MULA (offs, -piquat) + + loi 1.0 ; s1 = 1 (0x3f800000) + madd.x acc, temp, i ; MADDA (s1, temp) + + mul.x x2, temp, temp ; x2 = temp * temp + loi 0x3e2aaaab ; s2 = 1 / 6 + mul.x S2, temp, i ; S2 = s2 * temp + + madd.x acc, S2, x2 ; MADDA (S2, x2) + + mul.x x3, temp, x2 ; x3 = temp * x2 + loi 0x3d36db6e ; s4 = 5 / 112 + mul.x S4, x3, i ; S4 = s4 * x3 + mul.x x5, x2, x3 ; x5 = x2 * x3 + mul.x x4, x2, x2 ; x4 = x2 * x2 + + loi 0x3cf8e38e ; s5 = 35 / 1152 + mul.x S5, x4, i ; S5 = s5 * x4 + + loi 0x3d99999a ; s3 = 3 / 40 + mul.x S3, x2, i ; S3 = s3 * x2 + + madd.x acc, S3, x3 ; MADDA (S3, x3) + madd.x acc, S4, x4 ; MADDA (S4, x4) + madd.x acos, S5, x5 ; MADD (&r, S5, x5) + +#endmacro + +;//-------------------------------------------------------------------- +;// Arccos - Returns the arccos angle of up to 2 values, which must be +;// contained in the X and Z elements of "value". X and Z elements of +;// "acos" will contain, on return, the angles corresponding to the 2 +;// values. +;// +;// ARCCOS(value) may be approximated with the following: +;// mirror = value * -HALFPI + HALFPI; +;// ARCCOS(value) = 2 * mirror - +;// (sin (value * HALFPI) * -HALFPI + HALFPI); +;// +;// Note: ACC and I registers are modified, and a bunch of temporary +;// variables are created... Maybe bad for VCL register pressure +;//-------------------------------------------------------------------- +#macro Arccos: value, acos + loi 1.570796326 ; value * HALFPI + mul.xz acos, value, i ; + + move.xz mirror, value ; Important to do it here, or else it would be invalid for "value" & "acos" to be the same + + ;//// This part is really just a copy of AngleSinCos + loi 1.570796326 ; Phase difference for sin as cos ( PI/2 ) + sub.xz acos, acos, I ; + + abs.xz acos, acos ; Mirror cos around zero + + max.xz Vector1111, vf00, vf00[w] ; Initialise all 1s + + loi -0.159155 ; Scale so single cycle is range 0 to -1 ( *-1/2PI ) + mul.xz ACC, acos, I ; + + loi 12582912.0 ; Apply bias to remove fractional part + msub.xz ACC, Vector1111, I ; + madd.xz ACC, Vector1111, I ; Remove bias to leave original int part + + loi -0.159155 ; Apply original number to leave fraction range only + msub.xz ACC, acos, I ; + + loi 0.5 ; Ajust range: -0.5 to +0.5 + msub.xz acos, Vector1111, I ; + + abs.xz acos, acos ; Clamp: 0 to +0.5 + + loi 0.25 ; Ajust range: -0.25 to +0.25 + sub.xz acos, acos, I ; + + mul.xz anglepower2, acos, acos ; a^2 + + loi -76.574959 ; + mul.xz k4angle, acos, I ; k4 a + + loi -41.341675 ; + mul.xz k2angle, acos, I ; k2 a + + loi 81.602226 ; + mul.xz k3angle, acos, I ; k3 a + + mul.xz anglepower4, anglepower2, anglepower2 ; a^4 + mul.xz k4angle, k4angle, anglepower2 ; k4 a^3 + mul.xz ACC, k2angle, anglepower2 ; + k2 a^3 + + loi 39.710659 ; k5 a + mul.xz k2angle, acos, I ; + + mul.xz anglepower8, anglepower4, anglepower4 ; a^8 + madd.xz ACC, k4angle, anglepower4 ; + k4 a^7 + madd.xz ACC, k3angle, anglepower4 ; + k3 a^5 + loi 6.283185 ; + madd.xz ACC, acos, I ; + k1 a + madd.xz acos, k2angle, anglepower8 ; + k5 a^9 + ;//// This part was really just a copy of AngleSinCos + + loi -1.570796326 ; mirror = value * -HALFPI + HALFPI + mul.xz mirror, mirror, i ; + sub.xz mirror, mirror, i ; + + mul.xz acos, acos, i ; + sub.xz acos, acos, i ; + + add.xz mirror, mirror, mirror ; acos = 2 * mirror - (sin (value * HALFPI) * -HALFPI + HALFPI) + sub.xz acos, mirror, acos ; Final result in "x" +#endmacro + +;//------------------------------------------------------------------------------ +;// QuaternionSlerp - Spherical interpolation between 2 quaternions. "quatfrom" +;// corresponds to time 0.0, and "quatto" corresponds to time 1.0. The X element +;// of "qtime" must contain the desired time value (0.0 - 1.0). The result will +;// be returned in "quatdest". +;// +;// Note: ACC, I and Q registers are modified +;//------------------------------------------------------------------------------ +#macro QuaternionSlerp: quatdest, quatfrom, quatto, qtime + mul vclsmlftemp, quatfrom, quatto ; vclsmlftemp.x = dotproduct4 (quatfrom, quatto) + add.x vclsmlftemp, vclsmlftemp, vclsmlftemp[y] ; + add.x vclsmlftemp, vclsmlftemp, vclsmlftemp[z] ; + add.x vclsmlftemp, vclsmlftemp, vclsmlftemp[w] ; + + loi -1.0 ; + + move QSLquatto1, quatto ; We want to keep the angle between the 2 vectors <= PI + iaddiu vclsmlitemp, vi00, 0x80 ; (vclsmlftemp >= 0.0) + fmand vclsmlitemp, vclsmlitemp ; + ibeq vclsmlitemp, vi00, QSL1@ ; + + mul.x vclsmlftemp, vclsmlftemp, i ; Inverse the "quatto" vector, so the angle between the + mul QSLquatto1, QSLquatto1, i ; 2 vectors will become < PI + +QSL1@: + loi 0.999 ; If the angle is too small, we gotta do a linear + sub.x vf00, vclsmlftemp, i ; interpolation, because the slerp will fail + iaddiu vclsmlitemp, vi00, 0x80 ; (if (0.999 - vclsmlftemp.x < 0.0) it is too small) + fmand vclsmlitemp, vclsmlitemp ; + ibne vclsmlitemp, vi00, QSL2@ ; + + add.x vclsmlftemp, vf00, qtime ; vclsmlftemp.x = qtime.x + add.z vclsmlftemp, vf00, vf00[w] ; vclsmlftemp.z = 1.0 - qtime.x + sub.z vclsmlftemp, vclsmlftemp, qtime[x] ; + + b QSL3@ ; Go straight to the weighting part + +QSL2@: + Arccos vclsmlftemp, QSLangle ; QSLangle.x = acos (vclsmlftemp.x) + AngleSinCos QSLangle, QSLsinom ; QSLsinom.x = sin (QSLangle.x) + + add.z qtime, vf00, vf00[w] ; qtime.z = 1.0 - qtime.x + sub.z qtime, qtime, qtime[x] ; + + mul.xz vclsmlftemp, qtime, QSLangle[x] ; vclsmlftemp.x = sin (time * QSLangle) + AngleSinCos vclsmlftemp, vclsmlftemp ; vclsmlftemp.z = sin ((1.0 - time) * QSLangle) + + div q, vf00[w], QSLsinom[x] ; vclsmlftemp.x = vclsmlftemp.x / QSLsinom.x + mul.xz vclsmlftemp, vclsmlftemp, q ; vclsmlftemp.z = vclsmlftemp.z / QSLsinom.x + +QSL3@: + mul acc, quatfrom, vclsmlftemp[z] ; Now that we have the src and dest scale (0.0 - 1.0), get the final result + madd quatdest, QSLquatto1, vclsmlftemp[x] ; + +#endmacro + +;//-------------------------------------------------------------------- +;// QuaternionToMatrix - Converts a quaternion rotation to a matrix +;// Thanks to Colin Hughes (SCEE) for that one +;// +;// Note: ACC and I registers are modified +;//-------------------------------------------------------------------- +#macro QuaternionToMatrix: matresult, quaternion + mula.xyz ACC, quaternion, quaternion ; xx yy zz + + loi 1.414213562 + muli vclsmlftemp, quaternion, I ; x sqrt2 y sqrt2 z sqrt2 w sqrt2 + + mr32.w matresult[0], vf00 ; Set rhs matrix line 0 to 0 + mr32.w matresult[1], vf00 ; + mr32.w matresult[2], vf00 ; Set rhs matrix + move matresult[3], vf00 ; Set bottom line to 0 0 0 1 + + madd.xyz vcl_2qq, quaternion, quaternion ; 2xx 2yy 2zz + addw.xyz Vector111, vf00, vf00 ; 1 1 1 - + + opmula.xyz ACC, vclsmlftemp, vclsmlftemp ; 2yz 2xz 2xy - + msubw.xyz vclsmlftemp2, vclsmlftemp, vclsmlftemp ; 2yz-2xw 2xz-2yz 2xy-2zw - + maddw.xyz vclsmlftemp3, vclsmlftemp, vclsmlftemp ; 2yz+2xw 2xz+2yz 2xy+2zw - + addaw.xyz ACC, vf00, vf00 ; 1 1 1 - + msubax.yz ACC, Vector111, vcl_2qq ; 1 1-2xx 1-2xx + + msuby.z matresult[2], Vector111, vcl_2qq ; - - 1-2xx-2yy - + msubay.x ACC, Vector111, vcl_2qq ; 1-2yy 1-2xx 1-2xx-2yy - + msubz.y matresult[1], Vector111, vcl_2qq ; - 1-2xx-2zz - - + mr32.y matresult[0], vclsmlftemp2 + msubz.x matresult[0], Vector111, vcl_2qq ; 1-2yy-2zz - - - + mr32.x matresult[2], vclsmlftemp2 + addy.z matresult[0], vf00, vclsmlftemp3 + mr32.w vclsmlftemp, vclsmlftemp2 + mr32.z matresult[1], vclsmlftemp + addx.y matresult[2], vf00, vclsmlftemp3 + mr32.y vclsmlftemp3, vclsmlftemp3 + mr32.x matresult[1], vclsmlftemp3 + +#endmacro + +;//-------------------------------------------------------------------- +;// QuaternionMultiply - Multiplies "quaternion1" and "quaternion2", +;// and puts the result in "quatresult". +;// Thanks to Colin Hughes (SCEE) for that one +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- +#macro QuaternionMultiply: quatresult, quaternion1, quaternion2 + mul vclsmlftemp, quaternion1, quaternion2 ; xx yy zz ww + + opmula.xyz ACC, quaternion1, quaternion2 ; Start Outerproduct + madd.xyz ACC, quaternion1, quaternion2[w]; Add w2.xyz1 + madd.xyz ACC, quaternion2, quaternion1[w]; Add w1.xyz2 + opmsub.xyz quatresult, quaternion2, quaternion1 ; Finish Outerproduct + + sub.w ACC, vclsmlftemp, vclsmlftemp[z] ; ww - zz + msub.w ACC, vf00, vclsmlftemp[y] ; ww - zz - yy + msub.w quatresult, vf00, vclsmlftemp[x] ; ww - zz - yy - xx +#endmacro + +;//-------------------------------------------------------------------- +;// TriangleWinding - Compute winding of triangle relative to "eyepos" +;// result is nonzero if winding is CW (actually depends on your +;// coordinate system) +;// Thanks to David Etherton (Angel Studios) for that one +;// +;// Note: ACC register is modified +;//-------------------------------------------------------------------- +#macro TriangleWinding: result, vert1, vert2, vert3, eyepos + sub.xyz tw_vert12, vert2, vert1 + sub.xyz tw_vert13, vert3, vert1 + + opmula.xyz ACC, tw_vert12, tw_vert13 + opmsub.xyz tw_normal, tw_vert13, tw_vert12 + + sub.xyz tw_dot, eyepos, vert1 + + mul.xyz tw_dot, tw_dot, tw_normal + add.x tw_dot, tw_dot, tw_dot[y] + add.x tw_dot, tw_dot, tw_dot[z] + + fsand result, 0x2 +#endmacro + +;//-------------------------------------------------------------------- +;// STATUSFLAGS_BGTZ - Branch if status shows "greater than zero". +;// Thanks to David Etherton (Angel Studios) for that one +;//-------------------------------------------------------------------- +#macro STATUSFLAGS_BGTZ: label + fsand vclsmlitemp, 0x3 ; NEG | ZERO + ibeq vclsmlitemp, VI00, label ; Jump if NEITHER NEG NOR ZERO +#endmacro + +;//-------------------------------------------------------------------- +;// STATUSFLAGS_BGEZ - Branch if status shows "greater or equal to +;// zero". +;// Thanks to David Etherton (Angel Studios) for that one +;//-------------------------------------------------------------------- +#macro STATUSFLAGS_BGEZ: label + fsand vclsmlitemp, 0x2 ; NEG + ibeq vclsmlitemp, VI00, label ; Jump if NOT NEG +#endmacro + +;//-------------------------------------------------------------------- +;// STATUSFLAGS_BLEZ - Branch if status shows "less or equal to zero". +;// Thanks to David Etherton (Angel Studios) for that one +;//-------------------------------------------------------------------- +#macro STATUSFLAGS_BLEZ: label + fsand vclsmlitemp, 0x3 ; NEG | ZERO + ibne vclsmlitemp, VI00, label ; Jump if NEG OR ZERO +#endmacro + +;//-------------------------------------------------------------------- +;// STATUSFLAGS_BLTZ - Branch if status shows "less than zero". +;// Thanks to David Etherton (Angel Studios) for that one +;//-------------------------------------------------------------------- +#macro STATUSFLAGS_BLTZ: label + fsand vclsmlitemp, 0x2 ; NEG + ibne vclsmlitemp, VI00, label ; Jump if NEG +#endmacro + +;//--------------------------------------------------------- +;// DistanceVertexToPlane - Return the distance between a +;// homegeneous vertex "vertex" and a plane (described as +;// ABCD) "plane", and the result is returned in "distance". +;//--------------------------------------------------------- +#macro DistanceVertexToPlane: distance, vertex, plane + mul distance, vertex, plane + add.x distance, distance, distance[y] + add.x distance, distance, distance[z] + add.x distance, distance, distance[w] +#endmacro + +;//--------------------------------------------------------- +;// ResetClipFlags - +;// VCL won't let us use CLIP without first zeroing +;//--------------------------------------------------------- +#macro ResetClipFlags + fcset 0x000000 +#endmacro + +;//--------------------------------------------------------- +;// PerformClipCheck - checks if the vertex is outside the viewing frustum. +;// If it is, then the appropriate clipping flags are set. +;// Not using sqi instruction, because VCL cannot optimize it. +;// Primtag contains information about how many polys we will send +;// 2 - Bitwise AND the clipping flags with 0x3FFFF, this makes sure that +;// we get the clipping judgement for the last three verts +;// (i.e. that make up the triangle we are about to draw) +;// 3 - Add 0x7FFF. If any of the clipping flags were set this will +;// cause the triangle not to be drawn (any values above 0x8000 +;// that are stored in the w component of XYZ2 will set the ADC +;// bit, which tells the GS not to perform a drawing kick on this +;// triangle. +;//--------------------------------------------------------- +#macro PerformClipCheck: t_vertex, t_destAddress, t_destAddressOffset + clipw.xyz t_vertex, t_vertex + fcand VI01, 0x3FFFF + iaddiu adcBit, VI01, 0x7FFF + isw.w adcBit, t_destAddressOffset(t_destAddress) +#endmacro + +;//--------------------------------------------------------- +;// ScaleVertexToGSFormat - Scale vertex to GS screen space +;// 2 - Multiply and add the scales -> vert = vert * scale + scale +;// 3 - Convert vertex to 12:4 fixed point format +;//--------------------------------------------------------- +#macro ScaleVertexToGSFormat: t_scale, t_vertex + mula.xyz acc, t_scale, vf00[w] + madd.xyz t_vertex, t_vertex, t_scale + ftoi4.xyz t_vertex, t_vertex +#endmacro + +;//--------------------------------------------------------- +;// PerformTexturePerspectiveCorrection +;//--------------------------------------------------------- +#macro PerformTexturePerspectiveCorrection: t_resultStq, t_stq + mulq t_resultStq, t_stq, q +#endmacro + +;//--------------------------------------------------------- +;// FixColor - Clamp colors to 0-255 and convert to integer +;// +;// 1. Load 255 and put it into the alpha value +;// 2. Clamp result to 0-255 values +;// 3. And write to the output buffer +;//--------------------------------------------------------- +#macro FixColor: t_color + loi 255 + mini.xyz t_color, t_color, i + max.xyz t_color, t_color, vf00[x] + ftoi0 t_color, t_color +#endmacro diff --git a/engine/src/renderer/core/paths/path1/vu1_program.cpp b/engine/src/renderer/core/paths/path1/vu1_program.cpp new file mode 100644 index 0000000..a3ff158 --- /dev/null +++ b/engine/src/renderer/core/paths/path1/vu1_program.cpp @@ -0,0 +1,40 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/paths/path1/vu1_program.hpp" + +namespace Tyra { + +VU1Program::VU1Program(u32* t_start, u32* t_end) : start(t_start), end(t_end) { + packetSize = packet2_utils_get_packet_size_for_program(start, end); + programSize = calculateProgramSize(); +} + +VU1Program::~VU1Program() {} + +const u32& VU1Program::getPacketSize() const { return packetSize; } +const u32& VU1Program::getProgramSize() const { return programSize; } +const u32& VU1Program::getDestinationAddress() const { + return destinationAddress; +} +void VU1Program::setDestinationAddress(const u32& addr) { + destinationAddress = addr; +} +u32* VU1Program::getStart() const { return start; } +u32* VU1Program::getEnd() const { return end; } + +u32 VU1Program::calculateProgramSize() const { + u32 count = (getEnd() - getStart()) / 2; + if (count & 1) count++; + + return count; +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/paths/path3/path3.cpp b/engine/src/renderer/core/paths/path3/path3.cpp new file mode 100644 index 0000000..304fd86 --- /dev/null +++ b/engine/src/renderer/core/paths/path3/path3.cpp @@ -0,0 +1,94 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/paths/path3/path3.hpp" + +namespace Tyra { + +Path3::Path3() { + drawFinishPacket = packet2_create(3, P2_TYPE_NORMAL, P2_MODE_CHAIN, false); + clearScreenPacket = packet2_create(36, P2_TYPE_NORMAL, P2_MODE_CHAIN, false); + texturePacket = packet2_create(128, P2_TYPE_NORMAL, P2_MODE_CHAIN, false); + + packet2_chain_open_end(drawFinishPacket, 0, 0); + packet2_update(drawFinishPacket, draw_finish(drawFinishPacket->next)); + packet2_chain_close_tag(drawFinishPacket); +} + +Path3::~Path3() { + packet2_free(drawFinishPacket); + packet2_free(clearScreenPacket); + packet2_free(texturePacket); +} + +void Path3::init(RendererSettings* t_settings) { + settings = t_settings; + + dma_channel_initialize(DMA_CHANNEL_GIF, NULL, 0); + + TYRA_LOG("Path3 initialized"); +} + +void Path3::addDrawFinishTag() { + dma_channel_wait(DMA_CHANNEL_GIF, 0); + dma_channel_send_packet2(drawFinishPacket, DMA_CHANNEL_GIF, true); +} + +void Path3::clearScreen(zbuffer_t* z, const Color& color) { + packet2_reset(clearScreenPacket, false); + packet2_chain_open_end(clearScreenPacket, 0, 0); + packet2_update(clearScreenPacket, + draw_disable_tests(clearScreenPacket->next, 0, z)); + packet2_update( + clearScreenPacket, + draw_clear(clearScreenPacket->next, 0, + 2048.0F - (settings->getWidth() / 2), + 2048.0F - (settings->getHeight() / 2), settings->getWidth(), + settings->getHeight(), static_cast(color.r), + static_cast(color.g), static_cast(color.b))); + packet2_update(clearScreenPacket, + draw_enable_tests(clearScreenPacket->next, 0, z)); + packet2_update(clearScreenPacket, draw_finish(clearScreenPacket->next)); + packet2_chain_close_tag(clearScreenPacket); + dma_channel_wait(DMA_CHANNEL_GIF, 0); + dma_channel_send_packet2(clearScreenPacket, DMA_CHANNEL_GIF, true); +} + +void Path3::sendTexture(Texture* texture, + const RendererCoreTextureBuffers& texBuffers) { + packet2_reset(texturePacket, false); + + packet2_update( + texturePacket, + draw_texture_transfer(texturePacket->base, texture->getCoreData().data, + texture->getWidth(), texture->getHeight(), + texture->getCoreData().psm, + texBuffers.core->address, texBuffers.core->width)); + + if (texBuffers.clut != nullptr) { + auto* clut = texture->getClutData(); + packet2_update(texturePacket, draw_texture_transfer( + texturePacket->next, clut->data, + clut->width, clut->height, clut->psm, + texBuffers.clut->address, clut->width)); + } + + packet2_chain_open_cnt(texturePacket, 0, 0, 0); + packet2_update(texturePacket, + draw_texture_wrapping(texturePacket->next, 0, + texture->getWrapSettings())); + packet2_chain_close_tag(texturePacket); + + packet2_update(texturePacket, draw_texture_flush(texturePacket->next)); + dma_channel_wait(DMA_CHANNEL_GIF, 0); + dma_channel_send_packet2(texturePacket, DMA_CHANNEL_GIF, true); +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/renderer_core.cpp b/engine/src/renderer/core/renderer_core.cpp new file mode 100644 index 0000000..4511c84 --- /dev/null +++ b/engine/src/renderer/core/renderer_core.cpp @@ -0,0 +1,43 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/renderer_core.hpp" +#include "thread/threading.hpp" + +namespace Tyra { + +RendererCore::RendererCore() { isFrameLimitOn = true; } +RendererCore::~RendererCore() {} + +void RendererCore::init() { + gs.init(&settings); + texture.init(&gs, &path3); + path3.init(&settings); + renderer3D.init(&settings, &path1); + renderer2D.init(&settings, &texture.clut); + sync.init(&path3); +} + +void RendererCore::setClearScreenColor(const Color& color) { bgColor = color; } + +void RendererCore::beginFrame(const CameraInfo3D& cameraInfo) { + renderer3D.update(cameraInfo); + texture.onFrameChange(); + Threading::switchThread(); + path3.clearScreen(&gs.zBuffer, bgColor); +} + +void RendererCore::endFrame() { + Threading::switchThread(); + if (isFrameLimitOn) graph_wait_vsync(); + gs.flipBuffers(); +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/renderer_core_sync.cpp b/engine/src/renderer/core/renderer_core_sync.cpp new file mode 100644 index 0000000..b862976 --- /dev/null +++ b/engine/src/renderer/core/renderer_core_sync.cpp @@ -0,0 +1,38 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/renderer_core_sync.hpp" + +namespace Tyra { + +RendererCoreSync::RendererCoreSync() {} +RendererCoreSync::~RendererCoreSync() {} + +void RendererCoreSync::init(Path3* t_path3) { path3 = t_path3; } + +void RendererCoreSync::align() { + clear(); + add(); + waitAndClear(); +} + +void RendererCoreSync::add() { path3->addDrawFinishTag(); } + +u8 RendererCoreSync::check() { return *GS_REG_CSR & 2; } + +void RendererCoreSync::clear() { *GS_REG_CSR |= 2; } + +void RendererCoreSync::waitAndClear() { + while (!check()) { + } + clear(); +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/texture/cache_manager/renderer_texture_cache_manager.cpp b/engine/src/renderer/core/texture/cache_manager/renderer_texture_cache_manager.cpp new file mode 100644 index 0000000..616766d --- /dev/null +++ b/engine/src/renderer/core/texture/cache_manager/renderer_texture_cache_manager.cpp @@ -0,0 +1,74 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/texture/cache_manager/renderer_texture_cache_manager.hpp" + +namespace Tyra { + +RendererTextureCacheManager::RendererTextureCacheManager() {} +RendererTextureCacheManager::~RendererTextureCacheManager() {} + +void RendererTextureCacheManager::onFrameChange() { statistics.changeFrame(); } + +void RendererTextureCacheManager::addRequestedTexture(Texture* texture) { + statistics.addNewRequest(texture->getId()); +} + +u32 RendererTextureCacheManager::getTextureIdToDealloc( + const std::vector& currentAllocs) { + if (!statistics.isReady() || statistics.isProbablyNotCorrect()) { + return currentAllocs.size() > 0 ? currentAllocs.at(0).id : 0; + } + + u8 tuner = currentAllocs.size() > 254 ? 254 : currentAllocs.size(); + if (tuner >= 6) { + tuner /= 3; + } else if (tuner >= 4) { + tuner /= 2; + } else { + tuner = 1; + } + + std::vector temp; + + auto probableTextureIds = statistics.getTopTextureIdsForNextChanges(tuner); + + tryAddIdThatNotExistInAnalysis(&temp, currentAllocs, probableTextureIds); + + if (temp.size() == 0) { + addIdThatExistInAnalysisButIsOnTheBottom(&temp, currentAllocs, + probableTextureIds); + } + + return temp.at(0); +} + +void RendererTextureCacheManager::tryAddIdThatNotExistInAnalysis( + std::vector* result, + const std::vector& currentAllocs, + const std::vector& analysis) { + for (u32 i = 0; i < currentAllocs.size(); i++) { + if (std::find(analysis.begin(), analysis.end(), currentAllocs.at(i).id) == + analysis.end()) { + result->push_back(currentAllocs.at(i).id); + break; + } + } +} + +void RendererTextureCacheManager::addIdThatExistInAnalysisButIsOnTheBottom( + std::vector* result, + const std::vector& currentAllocs, + const std::vector& analysis) { + u32 lastId = analysis.at(analysis.size() - 1); + result->push_back(lastId); +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/texture/cache_manager/renderer_texture_cm_analysis.cpp b/engine/src/renderer/core/texture/cache_manager/renderer_texture_cm_analysis.cpp new file mode 100644 index 0000000..10b31e1 --- /dev/null +++ b/engine/src/renderer/core/texture/cache_manager/renderer_texture_cm_analysis.cpp @@ -0,0 +1,152 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/texture/cache_manager/renderer_texture_cm_analysis.hpp" + +namespace Tyra { + +RendererTextureCMAnalysis::RendererTextureCMAnalysis() { + last = &buffers[0]; + current = &buffers[1]; + currentIndex = 0; +} +RendererTextureCMAnalysis::~RendererTextureCMAnalysis() {} + +void RendererTextureCMAnalysis::changeFrame() { + flipBuffers(); + current->clear(); + currentIndex = 0; +} + +void RendererTextureCMAnalysis::addNewRequest(const u32& id) { + current->push_back(id); + currentIndex++; +} + +std::vector RendererTextureCMAnalysis::getTopTextureIdsForNextChanges( + const u8& count) { + TYRA_ASSERT(!isProbablyNotCorrect(), + "Analysis not available, because its probably not correct - " + "internal error"); + + auto nextTextureIds = getNextTextureIds(count); + auto occurences = getOccurencesForTextureIds(nextTextureIds); + auto uniqueTextureIds = getTextureIdsUnique(nextTextureIds); + auto weights = getWeights(uniqueTextureIds, occurences); + auto result = weightsToResult(&weights); + + return result; +} + +std::vector RendererTextureCMAnalysis::getNextTextureIds(const u8& count) { + std::vector result; + + for (u32 i = 0; i < count; i++) { + u32 index = i + currentIndex; + u32 offset = index >= last->size() ? index - last->size() : index; + result.push_back(last->at(offset)); + } + + return result; +} + +std::vector +RendererTextureCMAnalysis::getOccurencesForTextureIds( + const std::vector& textureIds) { + std::vector result; + + for (u8 i = 0; i < textureIds.size(); i++) { + // If not exist, add with 1 occurence + if (std::find_if(result.begin(), result.end(), + [textureIds, + i](const RendererTextureCMAnalysisOccurence& occurence) { + return occurence.id == textureIds[i]; + }) == result.end()) { + result.push_back({textureIds[i], 1}); + } else { + // If exist, increase occurence + auto occurence = std::find_if( + result.begin(), result.end(), + [textureIds, i](const RendererTextureCMAnalysisOccurence& occurence) { + return occurence.id == textureIds[i]; + }); + occurence->occurence++; + } + } + + return result; +} + +std::vector RendererTextureCMAnalysis::getTextureIdsUnique( + const std::vector& textureIds) { + std::vector result; + + for (u8 i = 0; i < textureIds.size(); i++) { + if (std::find(result.begin(), result.end(), textureIds[i]) == + result.end()) { + result.push_back(textureIds[i]); + } + } + + return result; +} + +std::vector +RendererTextureCMAnalysis::getWeights( + const std::vector& uniqueTexIds, + const std::vector& occurencies) { + std::vector result; + + for (u8 i = 0; i < uniqueTexIds.size(); i++) { + u16 weight = uniqueTexIds.size() - i; + + for (u8 j = 0; j < occurencies.size(); j++) { + if (occurencies[j].id == uniqueTexIds[i]) { + weight *= occurencies[j].occurence; + break; + } + } + + result.push_back({uniqueTexIds[i], weight}); + } + + return result; +} + +std::vector RendererTextureCMAnalysis::weightsToResult( + std::vector* weights) { + std::sort(weights->begin(), weights->end(), + [](const auto& a, const auto& b) { return a.weight > b.weight; }); + + std::vector result; + for (u8 i = 0; i < weights->size(); i++) { + result.push_back(weights->at(i).id); + } + + return result; +} + +bool RendererTextureCMAnalysis::isReady() { return last->size() != 0; } + +bool RendererTextureCMAnalysis::isProbablyNotCorrect() { + return currentIndex > last->size(); +} + +void RendererTextureCMAnalysis::flipBuffers() { + if (current == &buffers[0]) { + current = &buffers[1]; + last = &buffers[0]; + } else { + current = &buffers[0]; + last = &buffers[1]; + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/texture/models/texture.cpp b/engine/src/renderer/core/texture/models/texture.cpp new file mode 100644 index 0000000..2898785 --- /dev/null +++ b/engine/src/renderer/core/texture/models/texture.cpp @@ -0,0 +1,173 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include +#include +#include +#include +#include +#include +#include "renderer/core/texture/models/texture.hpp" + +namespace Tyra { + +Texture::Texture(TextureBuilderData* t_data) { + id = rand() % 1000000; + + name = t_data->name; + + core = new TextureData(t_data->data, t_data->bpp, t_data->gsComponents, + t_data->width, t_data->height); + + clut = + new TextureData(t_data->clut, t_data->clutBpp, t_data->clutGsComponents, + t_data->clutWidth, t_data->clutHeight); + + setDefaultWrapSettings(); +} + +Texture::~Texture() { + if (getTextureLinksCount() > 0) links.clear(); + if (core) delete core; + if (clut) delete clut; +} + +float Texture::getSizeInMB() const { + return (core->width / 100.0F) * (core->height / 100.0F) * + (core->bpp / 100.0F) / 8.0F; +} + +/** Based on gsKit code, thank you guys! */ +u32 Texture::getTextureSize() const { + int widthBlocks, heightBlocks; + int widthAlign, heightAlign; + + // Calculate the number of blocks width and height + // A block is 256 bytes in size + switch (core->bpp) { + case bpp32: + case bpp24: + // 1 block = 8x8 pixels + widthBlocks = (core->width + 7) / 8; + heightBlocks = (core->height + 7) / 8; + break; + + case bpp8: + // 1 block = 16x16 pixels + widthBlocks = (core->width + 15) / 16; + heightBlocks = (core->height + 15) / 16; + break; + case bpp4: + // 1 block = 32x16 pixels + widthBlocks = (core->width + 31) / 32; + heightBlocks = (core->height + 15) / 16; + break; + default: + TYRA_TRAP("Unknown texture bpp"); + return -1; + } + + // Calculate the minimum block alignment + if (core->bpp == bpp32 || core->bpp == bpp24 || core->bpp == bpp8) { + // 8x4 blocks in a page. + // block traversing order: + // 0145.... + // 2367.... + // ........ + // ........ + if (widthBlocks <= 2 && heightBlocks <= 1) { + widthAlign = 1; + heightAlign = 1; + } else if (widthBlocks <= 4 && heightBlocks <= 2) { + widthAlign = 2; + heightAlign = 2; + } else if (widthBlocks <= 8 && heightBlocks <= 4) { + widthAlign = 4; + heightAlign = 4; + } else { + widthAlign = 8; + heightAlign = 4; + } + } else { + if (widthBlocks <= 1 && heightBlocks <= 2) { + widthAlign = 1; + heightAlign = 1; + } else if (widthBlocks <= 2 && heightBlocks <= 2) { + widthAlign = 2; + heightAlign = 2; + } else if (widthBlocks <= 2 && heightBlocks <= 8) { + widthAlign = 2; + heightAlign = 8; + } else { + widthAlign = 4; + heightAlign = 8; + } + } + + widthBlocks = (-widthAlign) & (widthBlocks + widthAlign - 1); + heightBlocks = (-heightAlign) & (heightBlocks + heightAlign - 1); + + return widthBlocks * heightBlocks * 256; +} + +void Texture::setDefaultWrapSettings() { + wrap.horizontal = WRAP_REPEAT; + wrap.vertical = WRAP_REPEAT; + wrap.maxu = 0; + wrap.maxv = 0; + wrap.minu = 0; + wrap.minv = 0; +} + +void Texture::setWrapSettings(const TextureWrap t_horizontal, + const TextureWrap t_vertical) { + wrap.horizontal = t_horizontal; + wrap.vertical = t_vertical; +} + +void Texture::addLink(const u32& t_id) { + TextureLink link; + link.id = t_id; + links.push_back(link); +} + +void Texture::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void Texture::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string Texture::getPrint(const char* objectName) const { + std::stringstream res; + if (objectName) { + res << objectName << "("; + } else { + res << "Texture("; + } + + std::string wrapString; + if (wrap.horizontal == WRAP_REPEAT) + wrapString = "WRAP_REPEAT"; + else if (wrap.horizontal == WRAP_CLAMP) + wrapString = "WRAP_CLAMP"; + + res << "id: " << id << ", "; + res << "name: " << name << ", "; + res << "core: " << core->getPrint() << ", "; + if (clut != nullptr) res << "clut: " << clut->getPrint() << ", "; + res << "wrap: " << wrapString << ")"; + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/texture/renderer_core_texture.cpp b/engine/src/renderer/core/texture/renderer_core_texture.cpp new file mode 100644 index 0000000..d2e69bb --- /dev/null +++ b/engine/src/renderer/core/texture/renderer_core_texture.cpp @@ -0,0 +1,95 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/texture/renderer_core_texture.hpp" + +namespace Tyra { + +RendererCoreTexture::RendererCoreTexture() {} + +RendererCoreTexture::~RendererCoreTexture() {} + +void RendererCoreTexture::onFrameChange() { cacheManager.onFrameChange(); } + +void RendererCoreTexture::init(RendererCoreGS* t_gs, Path3* t_path3) { + sender.init(t_path3, t_gs); + path3 = t_path3; + initClut(); +} + +void RendererCoreTexture::updateClutBuffer(texbuffer_t* clutBuffer) { + if (clutBuffer == nullptr) { + clut.psm = 0; + clut.load_method = CLUT_NO_LOAD; + clut.address = 0; + } else { + clut.psm = clutBuffer->psm; + clut.load_method = CLUT_LOAD; + clut.address = clutBuffer->address; + } +} + +RendererCoreTextureBuffers RendererCoreTexture::useTexture(Texture* t_tex) { + TYRA_ASSERT(t_tex != nullptr, "Provided nullptr texture!"); + + cacheManager.addRequestedTexture(t_tex); + + auto allocated = getAllocatedBuffersByTextureId(t_tex->getId()); + if (allocated.id != 0) return allocated; + + while (t_tex->getSizeInMB() > sender.getFreeVRamInMB()) { + auto idToDealloc = cacheManager.getTextureIdToDealloc(currentAllocations); + auto buffToDealloc = getAllocatedBuffersByTextureId(idToDealloc); + sender.deallocate(buffToDealloc); + unregisterAllocation(idToDealloc); + } + + auto newTexBuffer = sender.allocate(t_tex); + path3->sendTexture(t_tex, newTexBuffer); + registerAllocation(newTexBuffer); + + return newTexBuffer; +} + +RendererCoreTextureBuffers RendererCoreTexture::getAllocatedBuffersByTextureId( + const u32& t_id) { + for (u32 i = 0; i < currentAllocations.size(); i++) + if (currentAllocations[i].id == t_id) return currentAllocations[i]; + return {0, nullptr, nullptr}; +} + +void RendererCoreTexture::registerAllocation( + const RendererCoreTextureBuffers& t_buffers) { + currentAllocations.push_back(t_buffers); +} + +void RendererCoreTexture::unregisterAllocation(const u32& textureId) { + u32 foundIndex; + + for (u32 i = 0; i < currentAllocations.size(); i++) { + if (currentAllocations[i].id == textureId) { + foundIndex = i; + break; + } + } + + currentAllocations.erase(currentAllocations.begin() + foundIndex); +} + +void RendererCoreTexture::initClut() { + clut.storage_mode = CLUT_STORAGE_MODE1; + clut.start = 0; + clut.psm = 0; + clut.load_method = CLUT_NO_LOAD; + clut.address = 0; + TYRA_LOG("Clut set!"); +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/texture/renderer_core_texture_sender.cpp b/engine/src/renderer/core/texture/renderer_core_texture_sender.cpp new file mode 100644 index 0000000..ca2df10 --- /dev/null +++ b/engine/src/renderer/core/texture/renderer_core_texture_sender.cpp @@ -0,0 +1,120 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/texture/renderer_core_texture_sender.hpp" +#include + +namespace Tyra { + +RendererCoreTextureSender::RendererCoreTextureSender() { + isTextureVRAMAllocated = false; + allocatedVRamMemForTextures = 0; +} +RendererCoreTextureSender::~RendererCoreTextureSender() {} + +void RendererCoreTextureSender::init(Path3* t_path3, RendererCoreGS* t_gs) { + gs = t_gs; + path3 = t_path3; + TYRA_LOG("Renderer texture initialized!"); +} + +RendererCoreTextureBuffers RendererCoreTextureSender::allocate( + Texture* t_texture) { + texbuffer_t* core = allocateTextureCore(t_texture); + texbuffer_t* clut = nullptr; + + if (t_texture->getClutData() != nullptr) { + clut = allocateTextureClut(t_texture); + } + return {t_texture->getId(), core, clut}; +} + +float RendererCoreTextureSender::getSizeInMB(texbuffer_t* texBuffer) { + auto bpp = getBppByPsm(texBuffer->psm); + auto width = pow(2, texBuffer->info.width); + auto height = pow(2, texBuffer->info.height); + return (width / 100.0F) * (height / 100.0F) * (bpp / 100.0F) / 8.0F; +} + +void RendererCoreTextureSender::deallocate( + const RendererCoreTextureBuffers& texBuffers) { + graph_vram_free(texBuffers.core->address); + delete texBuffers.core; + + if (texBuffers.clut != nullptr) { + graph_vram_free(texBuffers.clut->address); + delete texBuffers.clut; + } + + allocatedVRamMemForTextures -= getSizeInMB(texBuffers.core); +} + +texbuffer_t* RendererCoreTextureSender::allocateTextureCore( + Texture* t_texture) { + auto* result = new texbuffer_t; + result->width = t_texture->getWidth(); + result->psm = t_texture->getCoreData().psm; + result->info.components = t_texture->getCoreData().components; + + TYRA_ASSERT(t_texture->getSizeInMB() <= getFreeVRamInMB(), + "Not enough VRAM memory for texture!"); + + result->address = + graph_vram_allocate(t_texture->getWidth(), t_texture->getHeight(), + result->psm, GRAPH_ALIGN_BLOCK); + + TYRA_ASSERT(result->address > 0, + "Texture buffer allocation error. No memory!"); + + allocatedVRamMemForTextures += t_texture->getSizeInMB(); + result->info.width = draw_log2(t_texture->getWidth()); + result->info.height = draw_log2(t_texture->getHeight()); + result->info.function = TEXTURE_FUNCTION_MODULATE; + return result; +} + +texbuffer_t* RendererCoreTextureSender::allocateTextureClut( + Texture* t_texture) { + auto* result = new texbuffer_t; + const auto* clut = t_texture->getClutData(); + + result->width = clut->width; + result->psm = clut->psm; + result->info.components = clut->components; + + result->address = graph_vram_allocate(clut->width, clut->height, result->psm, + GRAPH_ALIGN_BLOCK); + + result->info.width = draw_log2(clut->width); + result->info.height = draw_log2(clut->height); + result->info.function = TEXTURE_FUNCTION_MODULATE; + return result; +} + +float RendererCoreTextureSender::getFreeVRamInMB() { + return gs->getVRamFreeSpaceInMB() - allocatedVRamMemForTextures; +} + +TextureBpp RendererCoreTextureSender::getBppByPsm(const u32& psm) { + if (psm == GS_PSM_32) { + return bpp32; + } else if (psm == GS_PSM_24) { + return bpp24; + } else if (psm == GS_PSM_8) { + return bpp8; + } else if (psm == GS_PSM_4) { + return bpp4; + } else { + TYRA_TRAP("Unknown bpp!"); + return bpp32; + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/texture/texture_data.cpp b/engine/src/renderer/core/texture/texture_data.cpp new file mode 100644 index 0000000..854fddc --- /dev/null +++ b/engine/src/renderer/core/texture/texture_data.cpp @@ -0,0 +1,94 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/texture/texture_data.hpp" +#include "debug/debug.hpp" +#include +#include + +namespace Tyra { + +TextureData::TextureData(unsigned char* t_data, const TextureBpp& t_bpp, + const unsigned char& t_gsComponents, + const int& t_width, const int& t_height) { + data = t_data; + bpp = t_bpp; + components = t_gsComponents; + width = t_width; + height = t_height; + psm = getPsmByBpp(t_bpp); +} + +TextureData::~TextureData() { + if (data) { + delete[] data; + } +} + +u32 TextureData::getPsmByBpp(const TextureBpp& bpp) { + if (bpp == bpp32) { + return GS_PSM_32; + } else if (bpp == bpp24) { + return GS_PSM_24; + } else if (bpp == bpp8) { + return GS_PSM_8; + } else if (bpp == bpp4) { + return GS_PSM_4; + } else { + TYRA_TRAP("Unknown bpp!"); + return 0; + } +} + +void TextureData::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void TextureData::print(const unsigned char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string TextureData::getPrint(const unsigned char* objectName) const { + std::stringstream res; + if (objectName) { + res << objectName << "("; + } else { + res << "TextureData("; + } + + std::string psmString; + if (psm == GS_PSM_32) + psmString = "GS_PSM_32"; + else if (psm == GS_PSM_24) + psmString = "GS_PSM_24"; + else if (psm == GS_PSM_8) + psmString = "GS_PSM_8"; + else if (psm == GS_PSM_4) + psmString = "GS_PSM_4"; + + std::string componentsString; + if (components == TEXTURE_COMPONENTS_RGBA) + componentsString = "TEXTURE_COMPONENTS_RGBA"; + else if (components == TEXTURE_COMPONENTS_RGB) + componentsString = "TEXTURE_COMPONENTS_RGB"; + + res << "data exist: " << static_cast(data != nullptr) << ", "; + res << "bpp: " << static_cast(bpp) << ", "; + res << "psm: " << psmString << ", "; + res << "width: " << width << ", "; + res << "height: " << height << ", "; + res << "components: " << componentsString << ")"; + + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/renderer/core/texture/texture_repository.cpp b/engine/src/renderer/core/texture/texture_repository.cpp new file mode 100644 index 0000000..d9a71b9 --- /dev/null +++ b/engine/src/renderer/core/texture/texture_repository.cpp @@ -0,0 +1,65 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/core/texture/texture_repository.hpp" + +namespace Tyra { + +TextureRepository::TextureRepository() {} + +TextureRepository::~TextureRepository() { + TYRA_LOG("Texture repository destructed!"); + if (getTexturesCount() > 0) { + for (u32 i = 0; i < getTexturesCount(); i++) delete textures[i]; + textures.clear(); + } +} + +// ---- +// Methods +// ---- + +Texture* TextureRepository::add(Texture* texture) { + textures.push_back(texture); + return texture; +} + +Texture* TextureRepository::add(const char* fullpath) { + TextureLoader& loader = texLoaderSelector.getLoaderByFileName(fullpath); + + auto* data = loader.load(fullpath); + Texture* texture = new Texture(data); + delete data; + + textures.push_back(texture); + return texture; +} + +void TextureRepository::addByMesh(Mesh* mesh, const char* directory, + const char* extension) { + auto& loader = texLoaderSelector.getLoaderByExtension(extension); + + std::string dirFixed = directory; + if (dirFixed.back() != '/' && dirFixed.back() != ':') dirFixed += "/"; + + for (u32 i = 0; i < mesh->getMaterialsCount(); i++) { + std::string fullPath = + dirFixed + mesh->getMaterial(i)->getName() + "." + extension; + + auto* data = loader.load(fullPath.c_str()); + Texture* texture = new Texture(data); + delete data; + + texture->addLink(mesh->getMaterial(i)->getId()); + textures.push_back(texture); + } +} + +} // namespace Tyra diff --git a/engine/src/renderer/models/color.cpp b/engine/src/renderer/models/color.cpp new file mode 100644 index 0000000..a48ab4d --- /dev/null +++ b/engine/src/renderer/models/color.cpp @@ -0,0 +1,68 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/models/color.hpp" + +namespace Tyra { + +void Color::copy(Color* out, const float* in) { + Vec4::copy(reinterpret_cast(out), in); +} + +void Color::operator=(const Color& v) { copy(this, v); } + +void Color::set(const float& t_r, const float& t_g, const float& t_b, + const float& t_a) { + r = t_r; + g = t_g; + b = t_b; + a = t_a; +} + +void Color::lerp(const Color& v1, const Color& v2, const float& interp) { + setLerp(this, v1, v2, interp); +} + +Color Color::getByLerp(const Color& v1, const Color& v2, const float& interp) { + Color result; + setLerp(&result, v1, v2, interp); + return result; +} + +void Color::print() const { + auto text = getPrint(nullptr); + printf("%s\n", text.c_str()); +} + +void Color::print(const char* name) const { + auto text = getPrint(name); + printf("%s\n", text.c_str()); +} + +std::string Color::getPrint(const char* name) const { + std::stringstream res; + if (name) { + res << name << "("; + } else { + res << "Color("; + } + res << std::fixed << std::setprecision(4); + res << r << ", " << g << ", " << b << ", " << a << ")"; + return res.str(); +} + +void Color::setLerp(Color* output, const Color& v1, const Color& v2, + const float& interp) { + Vec4::setLerp(reinterpret_cast(output), + reinterpret_cast(v1), + reinterpret_cast(v2), interp); +} + +} // namespace Tyra diff --git a/engine/src/renderer/renderer.cpp b/engine/src/renderer/renderer.cpp new file mode 100644 index 0000000..d1207a5 --- /dev/null +++ b/engine/src/renderer/renderer.cpp @@ -0,0 +1,29 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/renderer.hpp" + +namespace Tyra { + +Renderer::Renderer() {} +Renderer::~Renderer() {} + +void Renderer::init() { + core.init(); + renderer2D.init(&core); +} + +void Renderer::beginFrame(const CameraInfo3D& cameraInfo) { + core.beginFrame(cameraInfo); +} + +void Renderer::endFrame() { core.endFrame(); } + +} // namespace Tyra diff --git a/engine/src/renderer/renderer_settings.cpp b/engine/src/renderer/renderer_settings.cpp new file mode 100644 index 0000000..277bafd --- /dev/null +++ b/engine/src/renderer/renderer_settings.cpp @@ -0,0 +1,46 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "renderer/renderer_settings.hpp" + +namespace Tyra { + +RendererSettings::~RendererSettings() {} + +void RendererSettings::copy(RendererSettings* out, const RendererSettings* in) { + out->width = in->width; + out->height = in->height; + out->near = in->near; + out->far = in->far; + out->projectionScale = in->projectionScale; + out->aspectRatio = in->aspectRatio; +} + +void RendererSettings::set(const RendererSettings& v) { copy(this, &v); } + +void RendererSettings::print() const { + auto text = getPrint(); + printf("%s\n", text.c_str()); +} + +std::string RendererSettings::getPrint() const { + std::stringstream res; + res << "RendererSettings("; + res << "width: " << width << ", "; + res << "height: " << height << ", "; + res << "near: " << near << ", "; + res << "far: " << far << ", "; + res << "projectionScale: " << projectionScale << ", "; + res << "aspectRatio: " << aspectRatio; + res << ")"; + return res.str(); +} + +} // namespace Tyra diff --git a/engine/src/thread/threading.cpp b/engine/src/thread/threading.cpp new file mode 100644 index 0000000..da826d7 --- /dev/null +++ b/engine/src/thread/threading.cpp @@ -0,0 +1,26 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020-2022, tyra - https://github.com/h4570/tyrav2 +# Licenced under Apache License 2.0 +# Sandro Sobczyński +# Sandro Wellinator +*/ + +#include "thread/threading.hpp" +#include + +namespace Tyra { + +void Threading::sleep(const u32& ms) { + struct timespec tv = {0}; + tv.tv_sec = ms / 1000; + tv.tv_nsec = (ms % 1000) * 1000000; + nanosleep(&tv, nullptr); +} + +void Threading::switchThread() { sleep(1); } + +} // Namespace Tyra \ No newline at end of file diff --git a/engine/src/time/timer.cpp b/engine/src/time/timer.cpp new file mode 100644 index 0000000..fb922a1 --- /dev/null +++ b/engine/src/time/timer.cpp @@ -0,0 +1,29 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "time/timer.hpp" + +namespace Tyra { + +Timer::Timer() { prime(); } + +Timer::~Timer() {} + +u32 Timer::getTimeDelta() { + time = *T3_COUNT; + + if (time < lastTime) // The counter has wrapped + change = time + (65536 - lastTime); + else + change = time - lastTime; + return change; +} + +} // namespace Tyra diff --git a/samples/Makefile.samples-base b/samples/Makefile.samples-base new file mode 100644 index 0000000..ac585e8 --- /dev/null +++ b/samples/Makefile.samples-base @@ -0,0 +1,15 @@ +LIB += -ltyra +LIBDIRS += -L$(ENGINEDIR)/bin +INC += -I$(ENGINEDIR)/inc +INCDEP += -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/samples/h4570/Makefile b/samples/h4570/Makefile new file mode 100644 index 0000000..354610f --- /dev/null +++ b/samples/h4570/Makefile @@ -0,0 +1,24 @@ +TARGET := h4570.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 := +LIBDIRS := +INC := -I$(INCDIR) +INCDEP := -I$(INCDIR) + +include ../Makefile.samples-base \ No newline at end of file diff --git a/samples/h4570/inc/h4570.hpp b/samples/h4570/inc/h4570.hpp new file mode 100644 index 0000000..3f59399 --- /dev/null +++ b/samples/h4570/inc/h4570.hpp @@ -0,0 +1,53 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020 - 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#pragma once + +#include +#include +#include "renderer/3d/pipeline/minecraft/minecraft_pipeline.hpp" +#include "renderer/3d/pipeline/std/std_pipeline.hpp" + +namespace Tyra { + +class H4570 : public Game { + public: + H4570(Engine* engine); + ~H4570(); + + void init(); + void loop(); + + private: + Engine* engine; + + Mesh* warrior; + Mesh* warrior2; + Mesh* warrior3; + Mesh* warrior4; + Sprite* picture; + audsrv_adpcm_t* adpcmSample; + Timer adpcmTimer; + + Vec4 cameraPosition, cameraLookAt; + + MinecraftPipeline mcPip; + StdPipeline stdPip; + StdpipOptions* renderOptions; + Texture* blocksTex; + + u32 blocksCount; + McpipBlock* blocks; + M4x4* translations; + M4x4* rotations; + M4x4* scales; +}; + +} // namespace Tyra diff --git a/samples/h4570/run.ps1 b/samples/h4570/run.ps1 new file mode 100644 index 0000000..58035c6 --- /dev/null +++ b/samples/h4570/run.ps1 @@ -0,0 +1,4 @@ +$ConfigFile = Join-Path $PSScriptRoot '../windows-pcsx2.ps1' +. $ConfigFile + +RunPCSX2 diff --git a/samples/h4570/src/h4570.cpp b/samples/h4570/src/h4570.cpp new file mode 100644 index 0000000..7bccfc4 --- /dev/null +++ b/samples/h4570/src/h4570.cpp @@ -0,0 +1,211 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Wellington Carvalho +*/ + +#include "h4570.hpp" +#include "file/file_utils.hpp" +#include "loaders/3d/md2/md2_loader.hpp" + +namespace Tyra { + +H4570::H4570(Engine* t_engine) { engine = t_engine; } +H4570::~H4570() {} + +Mesh* getWarrior(Renderer* renderer); +StdpipOptions* getRenderingOptions(); +Sprite* get2DPicture(Renderer* renderer); + +void H4570::init() { + // Song + + engine->audio.loadSong(FileUtils::fromCwd("mafikizolo-loot.wav")); + engine->audio.setSongLoop(true); + engine->audio.setSongVolume(30); + + adpcmSample = engine->audio.loadADPCM(FileUtils::fromCwd("walk.adpcm")); + engine->audio.setADPCMVolume(80, 1); + + engine->renderer.setClearScreenColor(Color(64.0F, 64.0F, 64.0F)); + + warrior = getWarrior(&engine->renderer); + + auto* warriorTex = engine->renderer.core.texture.repository.getBySpriteOrMesh( + warrior->getMaterial(0)->getId()); + + warrior2 = new Mesh(*warrior); + warrior2->translation.translateX(-3.0F); + warriorTex->addLink(warrior2->getMaterial(0)->getId()); + warrior2->playAnimation(0, warrior2->getFramesCount() - 1); + warrior2->setAnimSpeed(0.10F); + + warrior3 = new Mesh(*warrior); + warrior3->translation.translateX(-6.0F); + warriorTex->addLink(warrior3->getMaterial(0)->getId()); + warrior3->playAnimation(0, warrior3->getFramesCount() - 1); + warrior3->setAnimSpeed(0.7F); + + warrior4 = new Mesh(*warrior); + warrior4->translation.translateX(3.0F); + warriorTex->addLink(warrior4->getMaterial(0)->getId()); + warrior4->playAnimation(0, warrior4->getFramesCount() - 1); + warrior4->setAnimSpeed(0.5F); + + renderOptions = getRenderingOptions(); + + cameraPosition = Vec4(0.0F, 0.0F, 20.0F); + cameraLookAt = *warrior->getPosition(); + + blocksTex = engine->renderer.core.texture.repository.add( + FileUtils::fromCwd("blocks.png")); + + mcPip.init(&engine->renderer.core); + stdPip.init(&engine->renderer.core); + + picture = get2DPicture(&engine->renderer); + + u32 rows = 16; // 64 + u32 columns = 16; // 64 + blocksCount = rows * columns; + float initialCameraZ = -400.0F; + + float offset = 40.0F; + blocks = new McpipBlock[blocksCount]; + translations = new M4x4[blocksCount]; + rotations = new M4x4[blocksCount]; + scales = new M4x4[blocksCount]; + float center = (rows / 2.0F) * offset; + + for (u32 i = 0; i < blocksCount; i++) { + u32 column = i % columns; + u32 row = i / rows; + + scales[i].scale(10.0F); + translations[i].translateX(row * offset - center); + translations[i].translateY(column * offset - center); + translations[i].translateZ(initialCameraZ); + + u32 randRow = rand() % (rows + 1); + u32 randColumn = rand() % (columns + 1); + + blocks[i].textureOffset = + Vec4(mcPip.getTextureOffset() * randRow, + mcPip.getTextureOffset() * randColumn, 0.0F, 1.0F); + + blocks[i].color = Color(128.0F, 128.0F, 128.0F, 128.0F); + } + + engine->audio.playSong(); +} + +u32 counter = 0; + +void H4570::loop() { + if (counter++ > 100) { + counter = 0; + TYRA_LOG(engine->info.getFps()); + } + + warrior->animate(); + warrior2->animate(); + warrior3->animate(); + warrior4->animate(); + + // if ((engine->pad.getPressed().DpadUp || engine->pad.getPressed().DpadDown + // || + // engine->pad.getPressed().DpadLeft || + // engine->pad.getPressed().DpadRight) && + // adpcmTimer.getTimeDelta() > 8000) { + // adpcmTimer.prime(); + // engine->audio.playADPCM(adpcmSample, 1); + // } + + engine->renderer.beginFrame(CameraInfo3D(&cameraPosition, &cameraLookAt)); + { + for (u32 i = 0; i < blocksCount; i++) { + rotations[i].rotateX(0.04F); + rotations[i].rotateY(0.01F); + rotations[i].rotateZ(0.01F); + + blocks[i].model = translations[i] * rotations[i] * scales[i]; + } + + // engine->renderer.renderer2D.render(picture); + + engine->renderer.renderer3D.usePipeline(&stdPip); + { + stdPip.render(warrior, renderOptions); + // stdPip.render(warrior2, renderOptions); + // stdPip.render(warrior3, renderOptions); + // stdPip.render(warrior4, renderOptions); + } + + engine->renderer.renderer3D.usePipeline(&mcPip); + { mcPip.render(blocks, blocksCount, blocksTex, false); } + } + engine->renderer.endFrame(); +} + +Mesh* getWarrior(Renderer* renderer) { + MD2Loader loader; + auto* data = loader.load(FileUtils::fromCwd("warrior.md2"), .08F, false); + auto* result = new Mesh(*data); + // result->translation.translateZ(-30.0F); + delete data; + + renderer->core.texture.repository.addByMesh(result, FileUtils::getCwd(), + "png"); + + result->playAnimation(0, result->getFramesCount() - 1); + result->setAnimSpeed(0.15F); + + return result; +} + +Sprite* get2DPicture(Renderer* renderer) { + auto* sprite = new Sprite; + + sprite->size.set(128.0F, 128.0F); + sprite->position.set(500.0F, 280.0F); + + renderer->core.texture.repository.add(FileUtils::fromCwd("reward.png")) + ->addLink(sprite->getId()); + + return sprite; +} + +StdpipOptions* getRenderingOptions() { + auto* options = new StdpipOptions(); + + auto* ambientColor = new Color(32.0F, 32.0F, 32.0F, 32.0F); + auto* directionalColors = new Color[3]; + for (int i = 0; i < 3; i++) directionalColors[i].set(0.0F, 0.0F, 0.0F, 1.0F); + auto* directionalDirections = new Vec4[3]; + for (int i = 0; i < 3; i++) + directionalDirections[i].set(1.0F, 1.0F, 1.0F, 1.0F); + + auto* lightingOptions = new StdpipLightingOptions(); // Memory leak! + lightingOptions->ambientColor = ambientColor; + lightingOptions->directionalColors = directionalColors; + lightingOptions->directionalDirections = directionalDirections; + + options->shadingType = Tyra::StdpipShadingGouraud; + options->blendingEnabled = true; + options->antiAliasingEnabled = false; + options->lighting = lightingOptions; + + directionalDirections[0].set(-1.0F, 0.0F, 0.0F); + directionalColors[0].set(0.0F, 96.0F, 0.0F); + + directionalDirections[1].set(1.0F, 0.0F, 0.0F); + directionalColors[1].set(96.0F, 0.0F, 0.0F); + + return options; +} + +} // namespace Tyra diff --git a/samples/h4570/src/main.cpp b/samples/h4570/src/main.cpp new file mode 100644 index 0000000..6f47255 --- /dev/null +++ b/samples/h4570/src/main.cpp @@ -0,0 +1,20 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Sandro Sobczyński +*/ + +#include "engine.hpp" +#include "h4570.hpp" + +int main() { + Tyra::Engine engine; + Tyra::H4570 game(&engine); + engine.run(&game); + SleepThread(); + return 0; +} diff --git a/samples/wellinator/Makefile b/samples/wellinator/Makefile new file mode 100644 index 0000000..33c7b49 --- /dev/null +++ b/samples/wellinator/Makefile @@ -0,0 +1,24 @@ +TARGET := wellinator.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 := +LIBDIRS := +INC := -I$(INCDIR) +INCDEP := -I$(INCDIR) + +include ../Makefile.samples-base \ No newline at end of file diff --git a/samples/wellinator/inc/wellinator.hpp b/samples/wellinator/inc/wellinator.hpp new file mode 100644 index 0000000..236516a --- /dev/null +++ b/samples/wellinator/inc/wellinator.hpp @@ -0,0 +1,50 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020 - 2022, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Wellington Carvalho +*/ + +#pragma once + +#include +#include +#include "renderer/3d/pipeline/minecraft/minecraft_pipeline.hpp" +#include "renderer/3d/pipeline/std/std_pipeline.hpp" + +namespace Tyra { + +class Wellinator : public Game { + public: + Wellinator(Engine* engine); + ~Wellinator(); + + void init(); + void loop(); + + private: + Engine* engine; + + Mesh* warrior; + Sprite* picture; + audsrv_adpcm_t* adpcmSample; + Timer adpcmTimer; + + Vec4 cameraPosition, cameraLookAt; + + MinecraftPipeline mcPip; + StdPipeline stdPip; + StdpipOptions* renderOptions; + Texture* blocksTex; + + u32 blocksCount; + McpipBlock* blocks; + M4x4* translations; + M4x4* rotations; + M4x4* scales; +}; + +} // namespace Tyra diff --git a/samples/wellinator/run.ps1 b/samples/wellinator/run.ps1 new file mode 100644 index 0000000..58035c6 --- /dev/null +++ b/samples/wellinator/run.ps1 @@ -0,0 +1,4 @@ +$ConfigFile = Join-Path $PSScriptRoot '../windows-pcsx2.ps1' +. $ConfigFile + +RunPCSX2 diff --git a/samples/wellinator/src/main.cpp b/samples/wellinator/src/main.cpp new file mode 100644 index 0000000..1a024d6 --- /dev/null +++ b/samples/wellinator/src/main.cpp @@ -0,0 +1,20 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Wellington Carvalho +*/ + +#include "engine.hpp" +#include "wellinator.hpp" + +int main() { + Tyra::Engine engine; + Tyra::Wellinator game(&engine); + engine.run(&game); + SleepThread(); + return 0; +} diff --git a/samples/wellinator/src/wellinator.cpp b/samples/wellinator/src/wellinator.cpp new file mode 100644 index 0000000..4504c30 --- /dev/null +++ b/samples/wellinator/src/wellinator.cpp @@ -0,0 +1,182 @@ +/* +# ______ ____ ___ +# | \/ ____| |___| +# | | | \ | | +#----------------------------------------------------------------------- +# Copyright 2020, tyra - https://github.com/h4570/tyra +# Licenced under Apache License 2.0 +# Wellington Carvalho +*/ + +#include "wellinator.hpp" +#include "file/file_utils.hpp" +#include "loaders/3d/md2/md2_loader.hpp" + +namespace Tyra { + +Wellinator::Wellinator(Engine* t_engine) { engine = t_engine; } +Wellinator::~Wellinator() {} + +Mesh* getWarrior(Renderer* renderer); +StdpipOptions* getRenderingOptions(); +Sprite* get2DPicture(Renderer* renderer); + +void Wellinator::init() { + // Song + + engine->audio.loadSong(FileUtils::fromCwd("mafikizolo-loot.wav")); + engine->audio.setSongLoop(true); + engine->audio.setSongVolume(30); + + adpcmSample = engine->audio.loadADPCM(FileUtils::fromCwd("walk.adpcm")); + engine->audio.setADPCMVolume(80, 1); + + engine->renderer.setClearScreenColor(Color(64.0F, 64.0F, 64.0F)); + warrior = getWarrior(&engine->renderer); + renderOptions = getRenderingOptions(); + + cameraPosition = Vec4(0.0F, 0.0F, 0.0F, 1.0F); + cameraLookAt = *warrior->getPosition(); + + blocksTex = engine->renderer.core.texture.repository.add( + FileUtils::fromCwd("blocks.png")); + + mcPip.init(&engine->renderer.core); + stdPip.init(&engine->renderer.core); + + picture = get2DPicture(&engine->renderer); + + u32 rows = 8; // 64 + u32 columns = 8; // 64 + blocksCount = rows * columns; + float initialCameraZ = -400.0F; + + float offset = 40.0F; + blocks = new McpipBlock[blocksCount]; + translations = new M4x4[blocksCount]; + rotations = new M4x4[blocksCount]; + scales = new M4x4[blocksCount]; + float center = (rows / 2.0F) * offset; + + for (u32 i = 0; i < blocksCount; i++) { + u32 column = i % columns; + u32 row = i / rows; + + scales[i].scale(10.0F); + translations[i].translateX(row * offset - center); + translations[i].translateY(column * offset - center); + translations[i].translateZ(initialCameraZ); + + u32 randRow = rand() % (rows + 1); + u32 randColumn = rand() % (columns + 1); + + blocks[i].textureOffset = + Vec4(mcPip.getTextureOffset() * randRow, + mcPip.getTextureOffset() * randColumn, 0.0F, 1.0F); + + blocks[i].color = Color(128.0F, 128.0F, 128.0F, 128.0F); + } + + // Vec4 warriorInitPosition = *warrior->getPosition(); + + engine->audio.playSong(); +} + +void Wellinator::loop() { + warrior->animate(); + + if ((engine->pad.getPressed().DpadUp || engine->pad.getPressed().DpadDown || + engine->pad.getPressed().DpadLeft || + engine->pad.getPressed().DpadRight) && + adpcmTimer.getTimeDelta() > 8000) { + adpcmTimer.prime(); + engine->audio.playADPCM(adpcmSample, 1); + } + + engine->renderer.beginFrame(CameraInfo3D(&cameraPosition, &cameraLookAt)); + { + for (u32 i = 0; i < blocksCount; i++) { + rotations[i].rotateX(0.04F); + rotations[i].rotateY(0.01F); + rotations[i].rotateZ(0.01F); + + blocks[i].model = translations[i] * rotations[i] * scales[i]; + } + + engine->renderer.renderer2D.render(picture); + + // Vec4* nextPos = warrior->getPosition(); + + // if (pad.getPressed().DpadUp) nextPos->y += 0.2; + // if (pad.getPressed().DpadDown) nextPos->y -= 0.2; + // if (pad.getPressed().DpadRight) nextPos->x += 0.2; + // if (pad.getPressed().DpadLeft) nextPos->x -= 0.2; + // if (pad.getLeftJoyPad().v > 200) nextPos->z += 0.2; + // if (pad.getLeftJoyPad().v < 100) nextPos->z -= 0.2; + + // warrior->setPosition(*nextPos); + + engine->renderer.renderer3D.usePipeline(&stdPip); + { stdPip.render(warrior, renderOptions); } + + engine->renderer.renderer3D.usePipeline(&mcPip); + { mcPip.render(blocks, blocksCount, blocksTex, false); } + } + engine->renderer.endFrame(); +} + +Mesh* getWarrior(Renderer* renderer) { + MD2Loader loader; + auto* data = loader.load(FileUtils::fromCwd("warrior.md2"), .08F, false); + auto* result = new Mesh(*data); + result->translation.translateZ(-30.0F); + delete data; + renderer->core.texture.repository.addByMesh(result, FileUtils::getCwd(), + "png"); + result->playAnimation(0, result->getFramesCount() - 1); + result->setAnimSpeed(0.15F); + return result; +} + +Sprite* get2DPicture(Renderer* renderer) { + auto* sprite = new Sprite; + + sprite->size.set(128.0F, 128.0F); + sprite->position.set(500.0F, 280.0F); + + renderer->core.texture.repository.add(FileUtils::fromCwd("reward.png")) + ->addLink(sprite->getId()); + + return sprite; +} + +StdpipOptions* getRenderingOptions() { + auto* options = new StdpipOptions(); + + auto* ambientColor = new Color(32.0F, 32.0F, 32.0F, 128.0F); + auto* directionalColors = new Color[3]; + for (int i = 0; i < 3; i++) directionalColors[i].set(0.0F, 0.0F, 0.0F, 1.0F); + auto* directionalDirections = new Vec4[3]; + for (int i = 0; i < 3; i++) + directionalDirections[i].set(1.0F, 1.0F, 1.0F, 1.0F); + + auto* lightingOptions = new StdpipLightingOptions(); // Memory leak! + lightingOptions->ambientColor = ambientColor; + lightingOptions->directionalColors = directionalColors; + lightingOptions->directionalDirections = directionalDirections; + + options->shadingType = Tyra::StdpipShadingGouraud; + options->blendingEnabled = true; + options->antiAliasingEnabled = false; + options->lighting = lightingOptions; + + directionalDirections[0].set(-1.0F, 0.0F, 0.0F); + directionalColors[0].set(0.0F, 96.0F, 0.0F); + + directionalDirections[1].set(1.0F, 0.0F, 0.0F); + directionalColors[1].set(96.0F, 0.0F, 0.0F); + + return options; +} + +} // namespace Tyra diff --git a/samples/windows-pcsx2.ps1 b/samples/windows-pcsx2.ps1 new file mode 100644 index 0000000..98f87fe --- /dev/null +++ b/samples/windows-pcsx2.ps1 @@ -0,0 +1,12 @@ +$PCSX2_PATH = "${Env:ProgramFiles(x86)}/PCSX2" +$PCSX2_EXE = 'pcsx2.exe' + +function GetTargetELFName { + return (Select-String -Path './Makefile' -Pattern "[^ ]*.elf").Matches.Value +} + +function RunPCSX2 { + $PCSX2_EXE_WITHOUT_EXT = (Split-Path $PCSX2_EXE -Leaf).Split('.')[0] + Stop-Process -Name $PCSX2_EXE_WITHOUT_EXT -ErrorAction 'SilentlyContinue' + Start-Process -FilePath "$PCSX2_PATH/$PCSX2_EXE" -ArgumentList "--elf=$PWD/bin/$(GetTargetELFName)" +} \ No newline at end of file