This commit is contained in:
2025-03-30 21:54:05 +02:00
parent 9d1dd809b5
commit cc1b2c84ef
17 changed files with 354 additions and 185 deletions

8
.gitignore vendored
View File

@@ -8,7 +8,13 @@ PSX.Dev-README.md
*.a
.cache/
.vscode/
.vscode/*
!.vscode/launch.json
!.vscode/tasks.json
.editorconfig
.clang-format
compile_commands.json
LICENSEE.DAT
*.iso
system.cnf
iso.xml

29
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,29 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug",
"type": "gdb",
"request": "attach",
"target": "localhost:3333",
"remote": true,
"cwd": "${workspaceRoot}",
"valuesFormatting": "parseText",
"stopAtConnect": true,
"gdbpath": "gdb-multiarch",
"windows": {
"gdbpath": "gdb-multiarch.exe"
},
"osx": {
"gdbpath": "gdb"
},
"executable": "${workspaceRoot}/${workspaceRootFolderName}.elf",
"autorun": [
"monitor reset shellhalt",
"load ${workspaceRootFolderName}.elf",
"tbreak main",
"continue"
]
}
]
}

37
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,37 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build Debug",
"type": "shell",
"command": "make BUILD=Debug",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [
"$gcc"
]
},
{
"label": "Build Release",
"type": "shell",
"command": "make",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [
"$gcc"
]
},
{
"label": "Clean",
"type": "shell",
"command": "make clean",
"group": {
"kind": "build"
}
}
]
}

View File

@@ -6,6 +6,7 @@ src/main.cpp \
src/renderer.cpp \
src/splashpack.cpp \
src/camera.cpp \
src/gtemath.cpp \
output.o
include third_party/nugget/psyqo/psyqo.mk

Binary file not shown.

View File

@@ -6,37 +6,20 @@
#include <psyqo/trigonometry.hh>
psxsplash::Camera::Camera() {
// Load identity
m_rotationMatrix = psyqo::SoftMath::generateRotationMatrix33(0, psyqo::SoftMath::Axis::X, m_trig);
}
void psxsplash::Camera::moveX(psyqo::FixedPoint<12> x) { m_rotation.x += -x; }
void psxsplash::Camera::moveX(psyqo::FixedPoint<12> x) { m_position.x += -x; }
void psxsplash::Camera::moveY(psyqo::FixedPoint<12> y) { m_rotation.y += -y; }
void psxsplash::Camera::moveY(psyqo::FixedPoint<12> y) { m_position.y += -y; }
void psxsplash::Camera::moveZ(psyqo::FixedPoint<12> z) { m_rotation.z += -z; }
void psxsplash::Camera::moveZ(psyqo::FixedPoint<12> z) { m_position.z += -z; }
void psxsplash::Camera::setPosition(psyqo::FixedPoint<12> x, psyqo::FixedPoint<12> y, psyqo::FixedPoint<12> z) {
m_rotation.x = -x;
m_rotation.y = -y;
m_rotation.z = -z;
}
void psxsplash::Camera::rotateX(psyqo::Angle x) {
auto rot = psyqo::SoftMath::generateRotationMatrix33(-x, psyqo::SoftMath::Axis::X, m_trig);
psyqo::SoftMath::multiplyMatrix33(m_rotationMatrix, rot, &m_rotationMatrix);
}
void psxsplash::Camera::rotateY(psyqo::Angle y) {
auto rot = psyqo::SoftMath::generateRotationMatrix33(-y, psyqo::SoftMath::Axis::Y, m_trig);
psyqo::SoftMath::multiplyMatrix33(m_rotationMatrix, rot, &m_rotationMatrix);
}
void psxsplash::Camera::rotateZ(psyqo::Angle z) {
auto rot = psyqo::SoftMath::generateRotationMatrix33(-z, psyqo::SoftMath::Axis::Y, m_trig);
psyqo::SoftMath::multiplyMatrix33(m_rotationMatrix, rot, &m_rotationMatrix);
m_position.x = x;
m_position.y = y;
m_position.z = z;
}
void psxsplash::Camera::setRotation(psyqo::Angle x, psyqo::Angle y, psyqo::Angle z) {
@@ -44,9 +27,10 @@ void psxsplash::Camera::setRotation(psyqo::Angle x, psyqo::Angle y, psyqo::Angle
auto rotY = psyqo::SoftMath::generateRotationMatrix33(y, psyqo::SoftMath::Axis::Y, m_trig);
auto rotZ = psyqo::SoftMath::generateRotationMatrix33(z, psyqo::SoftMath::Axis::Z, m_trig);
// XYZ multiplication order (matches C#)
psyqo::SoftMath::multiplyMatrix33(rotY, rotX, &rotY);
psyqo::SoftMath::multiplyMatrix33(rotY, rotZ, &rotY);
m_rotationMatrix = rotY;
}

View File

@@ -1,7 +1,5 @@
#pragma once
#include <sys/types.h>
#include <psyqo/fixed-point.hh>
#include <psyqo/matrix.hh>
#include <psyqo/trigonometry.hh>
@@ -17,12 +15,7 @@ class Camera {
void moveZ(psyqo::FixedPoint<12> y);
void setPosition(psyqo::FixedPoint<12> x, psyqo::FixedPoint<12> y, psyqo::FixedPoint<12> z);
psyqo::Vec3& getPosition() { return m_rotation; }
void rotateX(psyqo::Angle x);
void rotateY(psyqo::Angle y);
void rotateZ(psyqo::Angle z);
psyqo::Vec3& getPosition() { return m_position; }
void setRotation(psyqo::Angle x, psyqo::Angle y, psyqo::Angle z);
psyqo::Matrix33& getRotation();
@@ -30,6 +23,6 @@ class Camera {
private:
psyqo::Matrix33 m_rotationMatrix;
psyqo::Trig<> m_trig;
psyqo::Vec3 m_rotation;
psyqo::Vec3 m_position;
};
} // namespace psxsplash

View File

@@ -10,15 +10,13 @@ namespace psxsplash {
class GameObject final {
public:
psyqo::Vec3 position; // 12 bytes
psyqo::Matrix33 rotation; // 36 bytes
uint16_t polyCount; // 2 bytes
psyqo::PrimPieces::TPageAttr texture; // 2 bytes
uint16_t clutX, clutY;
uint16_t clut[256];
union { // 4 bytes
psyqo::Vec3 position;
psyqo::Matrix33 rotation;
union {
Tri *polygons;
uint32_t polygonsOffset;
};
int polyCount;
};
static_assert(sizeof(GameObject) == 56, "GameObject is not 56 bytes");
} // namespace psxsplash

37
src/gtemath.cpp Normal file
View File

@@ -0,0 +1,37 @@
#include "gtemath.hh"
#include <psyqo/gte-kernels.hh>
#include <psyqo/gte-registers.hh>
using namespace psyqo::GTE;
void psxsplash::matrixMultiplyGTE(const psyqo::Matrix33 &matA, const psyqo::Matrix33 &matB, psyqo::Matrix33 *result) {
writeSafe<PseudoRegister::Rotation>(matA);
psyqo::Vec3 t;
psyqo::GTE::writeSafe<PseudoRegister::V0>(psyqo::Vec3{matB.vs[0].x, matB.vs[1].x, matB.vs[2].x});
psyqo::GTE::Kernels::mvmva<Kernels::MX::RT, Kernels::MV::V0>();
t = psyqo::GTE::readSafe<psyqo::GTE::PseudoRegister::SV>();
result->vs[0].x = t.x;
result->vs[1].x = t.y;
result->vs[2].x = t.z;
psyqo::GTE::writeSafe<PseudoRegister::V0>(psyqo::Vec3{matB.vs[0].y, matB.vs[1].y, matB.vs[2].y});
psyqo::GTE::Kernels::mvmva<Kernels::MX::RT, Kernels::MV::V0>();
t = psyqo::GTE::readSafe<psyqo::GTE::PseudoRegister::SV>();
result->vs[0].y = t.x;
result->vs[1].y = t.y;
result->vs[2].y = t.z;
psyqo::GTE::writeSafe<PseudoRegister::V0>(psyqo::Vec3{matB.vs[0].z, matB.vs[1].z, matB.vs[2].z});
psyqo::GTE::Kernels::mvmva<Kernels::MX::RT, Kernels::MV::V0>();
t = psyqo::GTE::readSafe<psyqo::GTE::PseudoRegister::SV>();
result->vs[0].z = t.x;
result->vs[1].z = t.y;
result->vs[2].z = t.z;
}

5
src/gtemath.hh Normal file
View File

@@ -0,0 +1,5 @@
#include <psyqo/matrix.hh>
namespace psxsplash {
void matrixMultiplyGTE(const psyqo::Matrix33 &matA, const psyqo::Matrix33 &matB, psyqo::Matrix33 *result);
};

View File

@@ -15,6 +15,7 @@
#include "renderer.hh"
#include "splashpack.hh"
// Data from the splashpack
extern uint8_t _binary_output_bin_start[];
namespace {
@@ -58,6 +59,8 @@ void PSXSplash::prepare() {
.set(psyqo::GPU::ColorMode::C15BITS)
.set(psyqo::GPU::Interlace::PROGRESSIVE);
gpu().initialize(config);
// Initialize the Renderer singleton
psxsplash::Renderer::init(gpu());
}
@@ -75,68 +78,71 @@ void MainScene::start(StartReason reason) {
void MainScene::frame() {
uint32_t beginFrame = gpu().now();
auto currentFrameCounter = gpu().getFrameCount();
auto frameDiff = currentFrameCounter - mainScene.m_lastFrameCounter;
if (frameDiff == 0) {
auto deltaTime = currentFrameCounter - mainScene.m_lastFrameCounter;
// Unlike the torus example, this DOES happen...
if (deltaTime == 0) {
return;
}
mainScene.m_lastFrameCounter = currentFrameCounter;
auto& input = psxSplash.m_input;
if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Right)) {
m_mainCamera.moveX((m_trig.cos(camRotY) * moveSpeed));
m_mainCamera.moveZ(-(m_trig.sin(camRotY) * moveSpeed));
m_mainCamera.moveX((m_trig.cos(camRotY) * moveSpeed * deltaTime));
m_mainCamera.moveZ(-(m_trig.sin(camRotY) * moveSpeed * deltaTime));
}
if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Left)) {
m_mainCamera.moveX(-(m_trig.cos(camRotY) * moveSpeed));
m_mainCamera.moveZ((m_trig.sin(camRotY) * moveSpeed));
m_mainCamera.moveX(-(m_trig.cos(camRotY) * moveSpeed * deltaTime));
m_mainCamera.moveZ((m_trig.sin(camRotY) * moveSpeed * deltaTime));
}
if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Up)) {
m_mainCamera.moveX((m_trig.sin(camRotY) * m_trig.cos(camRotX)) * moveSpeed);
m_mainCamera.moveX((m_trig.sin(camRotY) * m_trig.cos(camRotX)) * moveSpeed * deltaTime);
m_mainCamera.moveY(-(m_trig.sin(camRotX) * moveSpeed));
m_mainCamera.moveZ((m_trig.cos(camRotY) * m_trig.cos(camRotX)) * moveSpeed);
m_mainCamera.moveZ((m_trig.cos(camRotY) * m_trig.cos(camRotX)) * moveSpeed * deltaTime);
}
if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Down)) {
m_mainCamera.moveX(-((m_trig.sin(camRotY) * m_trig.cos(camRotX)) * moveSpeed));
m_mainCamera.moveY((m_trig.sin(camRotX) * moveSpeed));
m_mainCamera.moveZ(-((m_trig.cos(camRotY) * m_trig.cos(camRotX)) * moveSpeed));
m_mainCamera.moveX(-((m_trig.sin(camRotY) * m_trig.cos(camRotX)) * moveSpeed * deltaTime));
m_mainCamera.moveY((m_trig.sin(camRotX) * moveSpeed * deltaTime));
m_mainCamera.moveZ(-((m_trig.cos(camRotY) * m_trig.cos(camRotX)) * moveSpeed * deltaTime));
}
if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::R1)) {
m_mainCamera.moveY(-moveSpeed);
m_mainCamera.moveY(-moveSpeed * deltaTime);
}
if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::L1)) {
m_mainCamera.moveY(moveSpeed);
m_mainCamera.moveY(moveSpeed * deltaTime);
}
if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Cross)) {
camRotX -= rotSpeed;
camRotX -= rotSpeed * deltaTime;
m_mainCamera.setRotation(camRotX, camRotY, camRotZ);
}
if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Triangle)) {
camRotX += rotSpeed;
camRotX += rotSpeed * deltaTime;
m_mainCamera.setRotation(camRotX, camRotY, camRotZ);
}
if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Circle)) {
camRotY += rotSpeed;
camRotY += rotSpeed * deltaTime;
m_mainCamera.setRotation(camRotX, camRotY, camRotZ);
}
if (input.isButtonPressed(psyqo::AdvancedPad::Pad::Pad1a, psyqo::AdvancedPad::Square)) {
camRotY -= rotSpeed;
camRotY -= rotSpeed * deltaTime;
m_mainCamera.setRotation(camRotX, camRotY, camRotZ);
}
psxsplash::Renderer::getInstance().render(m_objects);
psxSplash.m_font.chainprintf(gpu(), {{.x = 2, .y = 2}}, {{.r = 0xff, .g = 0xff, .b = 0xff}}, "FPS: %i",
gpu().getRefreshRate() / frameDiff);
gpu().getRefreshRate() / deltaTime);
gpu().pumpCallbacks();
uint32_t endFrame = gpu().now();
uint32_t spent = endFrame - beginFrame;

View File

@@ -1,17 +1,25 @@
#pragma once
#include "psyqo/gte-registers.hh"
#include "psyqo/primitives/common.hh"
#include <psyqo/gte-registers.hh>
#include <psyqo/primitives/common.hh>
namespace psxsplash {
class Tri final {
class Tri final {
public:
psyqo::GTE::PackedVec3 v0, v1, v2;
psyqo::GTE::PackedVec3 normal;
psyqo::Color colorA, colorB, colorC;
psyqo::PrimPieces::UVCoords uvA, uvB;
psyqo::PrimPieces::UVCoordsPadded uvC;
psyqo::Color colorA, colorB, colorC;
};
psyqo::PrimPieces::TPageAttr tpage;
uint16_t clutX;
uint16_t clutY;
uint16_t padding;
};
static_assert(sizeof(Tri) == 52, "Tri is not 52 bytes");
} // namespace psxsplash

View File

@@ -7,29 +7,35 @@
#include <psyqo/gte-kernels.hh>
#include <psyqo/gte-registers.hh>
#include <psyqo/kernel.hh>
#include <psyqo/matrix.hh>
#include <psyqo/primitives/common.hh>
#include <psyqo/primitives/triangles.hh>
#include <psyqo/soft-math.hh>
#include <psyqo/trigonometry.hh>
#include <psyqo/vector.hh>
#include "gtemath.hh"
using namespace psyqo::fixed_point_literals;
using namespace psyqo::trig_literals;
using namespace psyqo::GTE;
psxsplash::Renderer *psxsplash::Renderer::instance = nullptr;
void psxsplash::Renderer::init(psyqo::GPU &gpuInstance) {
psyqo::Kernel::assert(instance == nullptr, "A second intialization of Renderer was tried");
psyqo::GTE::clear<psyqo::GTE::Register::TRX, psyqo::GTE::Unsafe>();
psyqo::GTE::clear<psyqo::GTE::Register::TRY, psyqo::GTE::Unsafe>();
psyqo::GTE::clear<psyqo::GTE::Register::TRZ, psyqo::GTE::Unsafe>();
clear<Register::TRX, Safe>();
clear<Register::TRY, Safe>();
clear<Register::TRZ, Safe>();
psyqo::GTE::write<psyqo::GTE::Register::OFX, psyqo::GTE::Unsafe>(psyqo::FixedPoint<16>(160.0).raw());
psyqo::GTE::write<psyqo::GTE::Register::OFY, psyqo::GTE::Unsafe>(psyqo::FixedPoint<16>(120.0).raw());
write<Register::OFX, Safe>(psyqo::FixedPoint<16>(160.0).raw());
write<Register::OFY, Safe>(psyqo::FixedPoint<16>(120.0).raw());
psyqo::GTE::write<psyqo::GTE::Register::H, psyqo::GTE::Unsafe>(190);
write<Register::H, Safe>(120);
psyqo::GTE::write<psyqo::GTE::Register::ZSF3, psyqo::GTE::Unsafe>(ORDERING_TABLE_SIZE / 3);
psyqo::GTE::write<psyqo::GTE::Register::ZSF4, psyqo::GTE::Unsafe>(ORDERING_TABLE_SIZE / 4);
write<Register::ZSF3, Safe>(ORDERING_TABLE_SIZE / 3);
write<Register::ZSF4, Safe>(ORDERING_TABLE_SIZE / 4);
if (!instance) {
instance = new Renderer(gpuInstance);
@@ -47,96 +53,165 @@ void psxsplash::Renderer::render(eastl::vector<GameObject *> &objects) {
auto &clear = m_clear[parity];
auto &balloc = m_ballocs[parity];
eastl::array<psyqo::Vertex, 3> projected;
m_gpu.getNextClear(clear.primitive, m_clearcolor);
m_gpu.chain(clear);
balloc.reset();
eastl::array<psyqo::Vertex, 3> projected;
for (auto &obj : objects) {
psyqo::Vec3 cameraPosition, objectPosition;
psyqo::Matrix33 finalMatrix;
::clear<Register::TRX, Safe>();
::clear<Register::TRY, Safe>();
::clear<Register::TRZ, Safe>();
// Rotate the camera Translation vector by the camera rotation
writeSafe<PseudoRegister::Rotation>(m_currentCamera->getRotation());
writeSafe<PseudoRegister::V0>(m_currentCamera->getPosition());
Kernels::mvmva<Kernels::MX::RT, Kernels::MV::V0, Kernels::TV::TR>();
cameraPosition = readSafe<PseudoRegister::SV>();
// Rotate the object Translation vector by the camera rotation
writeSafe<PseudoRegister::V0>(obj->position);
Kernels::mvmva<Kernels::MX::RT, Kernels::MV::V0, Kernels::TV::TR>();
objectPosition = readSafe<PseudoRegister::SV>();
objectPosition.x += cameraPosition.x;
objectPosition.y += cameraPosition.y;
objectPosition.z += cameraPosition.z;
// Combine object and camera rotations
matrixMultiplyGTE(m_currentCamera->getRotation(), obj->rotation, &finalMatrix);
psyqo::GTE::writeSafe<psyqo::GTE::PseudoRegister::Translation>(objectPosition);
psyqo::GTE::writeSafe<psyqo::GTE::PseudoRegister::Rotation>(finalMatrix);
for (int i = 0; i < obj->polyCount; i++) {
Tri &tri = obj->polygons[i];
psyqo::Vec3 result;
psyqo::GTE::writeUnsafe<psyqo::GTE::PseudoRegister::Rotation>(obj->rotation);
psyqo::GTE::write<psyqo::GTE::Register::TRX, psyqo::GTE::Unsafe>(obj->position.x.raw() +
m_currentCamera->getPosition().x.raw());
psyqo::GTE::write<psyqo::GTE::Register::TRY, psyqo::GTE::Unsafe>(obj->position.y.raw() +
m_currentCamera->getPosition().y.raw());
psyqo::GTE::write<psyqo::GTE::Register::TRZ, psyqo::GTE::Safe>(obj->position.z.raw() +
m_currentCamera->getPosition().z.raw());
writeSafe<PseudoRegister::V0>(tri.v0);
writeSafe<PseudoRegister::V1>(tri.v1);
writeSafe<PseudoRegister::V2>(tri.v2);
psyqo::GTE::writeSafe<psyqo::GTE::PseudoRegister::V0>(tri.v0);
psyqo::GTE::writeSafe<psyqo::GTE::PseudoRegister::V1>(tri.v1);
psyqo::GTE::writeSafe<psyqo::GTE::PseudoRegister::V2>(tri.v2);
psyqo::GTE::Kernels::mvmva<psyqo::GTE::Kernels::MX::RT, psyqo::GTE::Kernels::MV::V0,
psyqo::GTE::Kernels::TV::TR>();
result = psyqo::GTE::readSafe<psyqo::GTE::PseudoRegister::SV>();
psyqo::GTE::writeSafe<psyqo::GTE::PseudoRegister::V0>(result);
psyqo::GTE::Kernels::mvmva<psyqo::GTE::Kernels::MX::RT, psyqo::GTE::Kernels::MV::V1,
psyqo::GTE::Kernels::TV::TR>();
result = psyqo::GTE::readSafe<psyqo::GTE::PseudoRegister::SV>();
psyqo::GTE::writeSafe<psyqo::GTE::PseudoRegister::V1>(result);
psyqo::GTE::Kernels::mvmva<psyqo::GTE::Kernels::MX::RT, psyqo::GTE::Kernels::MV::V2,
psyqo::GTE::Kernels::TV::TR>();
result = psyqo::GTE::readSafe<psyqo::GTE::PseudoRegister::SV>();
psyqo::GTE::writeSafe<psyqo::GTE::PseudoRegister::V2>(result);
psyqo::GTE::writeUnsafe<psyqo::GTE::PseudoRegister::Rotation>(m_currentCamera->getRotation());
psyqo::GTE::clear<psyqo::GTE::Register::TRX, psyqo::GTE::Unsafe>();
psyqo::GTE::clear<psyqo::GTE::Register::TRY, psyqo::GTE::Unsafe>();
psyqo::GTE::clear<psyqo::GTE::Register::TRZ, psyqo::GTE::Unsafe>();
psyqo::GTE::Kernels::rtpt();
psyqo::GTE::Kernels::nclip();
Kernels::rtpt();
Kernels::nclip();
int32_t mac0 = 0;
psyqo::GTE::read<psyqo::GTE::Register::MAC0>(reinterpret_cast<uint32_t *>(&mac0));
read<Register::MAC0>(reinterpret_cast<uint32_t *>(&mac0));
if (mac0 <= 0) continue;
int32_t zIndex = 0;
uint32_t sz0, sz1, sz2;
psyqo::GTE::read<psyqo::GTE::Register::SZ0>(&sz0);
psyqo::GTE::read<psyqo::GTE::Register::SZ1>(&sz1);
psyqo::GTE::read<psyqo::GTE::Register::SZ2>(&sz2);
read<Register::SZ0>(&sz0);
read<Register::SZ1>(&sz1);
read<Register::SZ2>(&sz2);
zIndex = eastl::max(eastl::max(sz0, sz1), sz2);
// psyqo::GTE::read<psyqo::GTE::Register::OTZ>(reinterpret_cast<uint32_t *>(&zIndex));
if (zIndex < 0 || zIndex >= ORDERING_TABLE_SIZE) continue;
psyqo::GTE::read<psyqo::GTE::Register::SXY0>(&projected[0].packed);
psyqo::GTE::read<psyqo::GTE::Register::SXY1>(&projected[1].packed);
psyqo::GTE::read<psyqo::GTE::Register::SXY2>(&projected[2].packed);
read<Register::SXY0>(&projected[0].packed);
read<Register::SXY1>(&projected[1].packed);
read<Register::SXY2>(&projected[2].packed);
iterativeSubdivideAndRender(tri, projected, zIndex, 3);
}
}
m_gpu.getNextClear(clear.primitive, m_clearcolor);
m_gpu.chain(clear);
m_gpu.chain(ot);
}
static inline psyqo::Color averageColor(const psyqo::Color &c1, const psyqo::Color &c2) {
uint8_t r = (c1.r + c2.r) >> 1;
uint8_t g = (c1.g + c2.g) >> 1;
uint8_t b = (c1.b + c2.b) >> 1;
psyqo::Color c;
c.r = r;
c.g = g;
c.b = b;
return c;
}
// Temporary subdivision code. I'm told this is terrible.
void psxsplash::Renderer::iterativeSubdivideAndRender(const Tri &initialTri,
const eastl::array<psyqo::Vertex, 3> &initialProj, int zIndex,
int maxIterations) {
struct Subdiv {
Tri tri;
eastl::array<psyqo::Vertex, 3> proj;
int iterations;
};
// Reserve space knowing the max subdivisions (for maxIterations=3, max elements are small)
eastl::vector<Subdiv> stack;
stack.reserve(16);
stack.push_back({initialTri, initialProj, maxIterations});
while (!stack.empty()) {
Subdiv s = stack.back();
stack.pop_back();
uint16_t minX = eastl::min({s.proj[0].x, s.proj[1].x, s.proj[2].x});
uint16_t maxX = eastl::max({s.proj[0].x, s.proj[1].x, s.proj[2].x});
uint16_t minY = eastl::min({s.proj[0].y, s.proj[1].y, s.proj[2].y});
uint16_t maxY = eastl::max({s.proj[0].y, s.proj[1].y, s.proj[2].y});
uint16_t width = maxX - minX;
uint16_t height = maxY - minY;
// Base case: small enough or no iterations left.
if (s.iterations == 0 || (width < 2048 && height < 1024)) {
auto &balloc = m_ballocs[m_gpu.getParity()];
auto &prim = balloc.allocateFragment<psyqo::Prim::GouraudTexturedTriangle>();
psyqo::PrimPieces::ClutIndex clut(obj->clutX, obj->clutY);
prim.primitive.pointA = projected[0];
prim.primitive.pointB = projected[1];
prim.primitive.pointC = projected[2];
prim.primitive.uvA = tri.uvA;
prim.primitive.uvB = tri.uvB;
prim.primitive.uvC = tri.uvC;
prim.primitive.tpage = obj->texture;
prim.primitive.pointA = s.proj[0];
prim.primitive.pointB = s.proj[1];
prim.primitive.pointC = s.proj[2];
prim.primitive.uvA = s.tri.uvA;
prim.primitive.uvB = s.tri.uvB;
prim.primitive.uvC = s.tri.uvC;
prim.primitive.tpage = s.tri.tpage;
psyqo::PrimPieces::ClutIndex clut(s.tri.clutX, s.tri.clutY);
prim.primitive.clutIndex = clut;
prim.primitive.setColorA(tri.colorA);
prim.primitive.setColorB(tri.colorB);
prim.primitive.setColorC(tri.colorC);
prim.primitive.setColorA(s.tri.colorA);
prim.primitive.setColorB(s.tri.colorB);
prim.primitive.setColorC(s.tri.colorC);
prim.primitive.setOpaque();
ot.insert(prim, zIndex);
}
m_ots[m_gpu.getParity()].insert(prim, zIndex);
continue;
}
m_gpu.chain(ot);
// Compute midpoint between projected[0] and projected[1].
psyqo::Vertex mid;
mid.x = (s.proj[0].x + s.proj[1].x) >> 1;
mid.y = (s.proj[0].y + s.proj[1].y) >> 1;
// Interpolate UV and color.
psyqo::PrimPieces::UVCoords newUV;
newUV.u = (s.tri.uvA.u + s.tri.uvB.u) / 2;
newUV.v = (s.tri.uvA.v + s.tri.uvB.v) / 2;
psyqo::Color newColor = averageColor(s.tri.colorA, s.tri.colorB);
// Prepare new projected vertices.
eastl::array<psyqo::Vertex, 3> projA = {s.proj[0], mid, s.proj[2]};
eastl::array<psyqo::Vertex, 3> projB = {mid, s.proj[1], s.proj[2]};
// Construct new Tris
Tri triA = s.tri;
triA.uvB = newUV;
triA.colorB = newColor;
Tri triB = s.tri;
triB.uvA = newUV;
triB.colorA = newColor;
// Push new subdivisions on stack.
stack.push_back({triA, projA, s.iterations - 1});
stack.push_back({triB, projB, s.iterations - 1});
}
}
void psxsplash::Renderer::vramUpload(const uint16_t *imageData, int16_t posX, int16_t posY, int16_t width,

View File

@@ -1,5 +1,6 @@
#pragma once
#include <EASTL/array.h>
#include <EASTL/vector.h>
#include <cstdint>
@@ -10,6 +11,7 @@
#include <psyqo/ordering-table.hh>
#include <psyqo/primitives/common.hh>
#include <psyqo/primitives/misc.hh>
#include <psyqo/primitives/triangles.hh>
#include <psyqo/trigonometry.hh>
#include "camera.hh"
@@ -22,14 +24,16 @@ class Renderer final {
Renderer(const Renderer&) = delete;
Renderer& operator=(const Renderer&) = delete;
static constexpr size_t ORDERING_TABLE_SIZE = 4096 * 16;
static constexpr size_t BUMP_ALLOCATOR_SIZE = 8192 * 8;
static constexpr size_t ORDERING_TABLE_SIZE = 2048 * 3;
static constexpr size_t BUMP_ALLOCATOR_SIZE = 8096 * 16;
static void init(psyqo::GPU& gpuInstance);
void setCamera(Camera& camera);
void render(eastl::vector<GameObject*>& objects);
void iterativeSubdivideAndRender(const Tri& initialTri, const eastl::array<psyqo::Vertex, 3>& initialProj,
int zIndex, int maxIterations);
void vramUpload(const uint16_t* imageData, int16_t posX, int16_t posY, int16_t width, int16_t height);
static Renderer& getInstance() {
@@ -50,8 +54,9 @@ class Renderer final {
psyqo::OrderingTable<ORDERING_TABLE_SIZE> m_ots[2];
psyqo::Fragments::SimpleFragment<psyqo::Prim::FastFill> m_clear[2];
psyqo::Color m_clearcolor = {.r = 63, .g = 63, .b = 63};
psyqo::BumpAllocator<BUMP_ALLOCATOR_SIZE> m_ballocs[2];
psyqo::Color m_clearcolor = {.r = 63, .g = 63, .b = 100};
};
} // namespace psxsplash

View File

@@ -2,7 +2,6 @@
#include <cstdint>
#include <cstring>
#include <memory>
#include <psyqo/primitives/common.hh>
#include "gameobject.hh"
@@ -23,25 +22,7 @@ eastl::vector<psxsplash::GameObject *> LoadSplashpack(uint8_t *data) {
for (uint16_t i = 0; i < header->gameObjectCount; i++) {
psxsplash::GameObject *go = reinterpret_cast<psxsplash::GameObject *>(curentPointer);
int16_t width = 0;
switch ((psyqo::Prim::TPageAttr::ColorMode)((std::bit_cast<short>(go->texture) & 0x0180) >> 7)) {
case psyqo::Prim::TPageAttr::ColorMode::Tex4Bits:
width = 16;
break;
case psyqo::Prim::TPageAttr::ColorMode::Tex8Bits:
width = 256;
break;
default:
width = -1;
}
if (width > 0) {
psxsplash::Renderer::getInstance().vramUpload(go->clut, go->clutX * 16, go->clutY, width, 1);
}
go->polygons = reinterpret_cast<psxsplash::Tri *>(data + go->polygonsOffset);
gameObjects.push_back(go);
curentPointer += sizeof(psxsplash::GameObject);
}
@@ -49,12 +30,18 @@ eastl::vector<psxsplash::GameObject *> LoadSplashpack(uint8_t *data) {
for (uint16_t i = 0; i < header->textureAtlasCount; i++) {
psxsplash::SPLASHPACKTextureAtlas *atlas = reinterpret_cast<psxsplash::SPLASHPACKTextureAtlas *>(curentPointer);
uint8_t *offsetData = data + atlas->polygonsOffset; // Ensure correct byte offset
uint16_t *castedData = reinterpret_cast<uint16_t *>(offsetData); // Safe cast
uint8_t *offsetData = data + atlas->polygonsOffset;
uint16_t *castedData = reinterpret_cast<uint16_t *>(offsetData);
psxsplash::Renderer::getInstance().vramUpload(castedData, atlas->x, atlas->y, atlas->width, atlas->height);
curentPointer += sizeof(psxsplash::SPLASHPACKTextureAtlas);
}
for (uint16_t i = 0; i < header->clutCount; i++) {
psxsplash::SPLASHPACKClut *clut = reinterpret_cast<psxsplash::SPLASHPACKClut *>(curentPointer);
psxsplash::Renderer::getInstance().vramUpload(clut->clut, clut->clutPackingX * 16, clut->clutPackingY, clut->length, 1);
curentPointer += sizeof(psxsplash::SPLASHPACKClut);
}
return gameObjects;
}

View File

@@ -13,6 +13,8 @@ struct SPLASHPACKFileHeader {
uint16_t version;
uint16_t gameObjectCount;
uint16_t textureAtlasCount;
uint16_t clutCount;
uint16_t pad;
};
struct SPLASHPACKTextureAtlas {
@@ -21,6 +23,15 @@ struct SPLASHPACKTextureAtlas {
uint16_t x, y;
};
struct SPLASHPACKClut {
uint16_t clut[256];
uint16_t clutPackingX;
uint16_t clutPackingY;
uint16_t length;
uint16_t pad;
};
eastl::vector<GameObject *> LoadSplashpack(uint8_t *data);
}; // namespace psxsplash

View File

@@ -1,13 +0,0 @@
#pragma once
#include <cstdint>
#include <psyqo/primitives/common.hh>
namespace psxsplash {
class Texture final {
public:
psyqo::PrimPieces::TPageAttr m_tpage;
uint8_t m_width, m_height;
};
} // namespace psxsplash