Compare commits

...

14 Commits

Author SHA1 Message Date
Stefanos Kornilios Mitsis Poiitidis
284f24c48c Cluster indexes, not vertexes 2025-01-13 01:33:34 +02:00
Stefanos Kornilios Mitsis Poiitidis
e4b296c4cf backface cone rejection 2025-01-12 18:57:25 +02:00
Stefanos Kornilios Mitsis Poiitidis
2065c9a93f Merge branch 'skmp/fix-AllocateBoatWakeArray' into 'main'
Some checks failed
re3 conan+cmake / build-cmake (openal, glfw, macos-latest, gl3) (push) Has been cancelled
re3 conan+cmake / build-cmake (openal, glfw, ubuntu-18.04, gl3) (push) Has been cancelled
re3 conan+cmake / build-cmake (openal, glfw, windows-latest, gl3) (push) Has been cancelled
re3 conan+cmake / build-cmake (openal, windows-latest, d3d9) (push) Has been cancelled
re3 cmake devkitA64 (Nintendo Switch) / build-nintendo-switch (push) Has been cancelled
re3 premake amd64 / build (Debug, win-amd64-librw_d3d9-oal) (push) Has been cancelled
re3 premake amd64 / build (Debug, win-amd64-librw_gl3_glfw-oal) (push) Has been cancelled
re3 premake amd64 / build (Release, win-amd64-librw_d3d9-oal) (push) Has been cancelled
re3 premake amd64 / build (Release, win-amd64-librw_gl3_glfw-oal) (push) Has been cancelled
re3 premake x86 / build (Debug, win-x86-librw_d3d9-mss) (push) Has been cancelled
re3 premake x86 / build (Debug, win-x86-librw_d3d9-oal) (push) Has been cancelled
re3 premake x86 / build (Debug, win-x86-librw_gl3_glfw-mss) (push) Has been cancelled
re3 premake x86 / build (Debug, win-x86-librw_gl3_glfw-oal) (push) Has been cancelled
re3 premake x86 / build (Release, win-x86-librw_d3d9-mss) (push) Has been cancelled
re3 premake x86 / build (Release, win-x86-librw_d3d9-oal) (push) Has been cancelled
re3 premake x86 / build (Release, win-x86-librw_gl3_glfw-mss) (push) Has been cancelled
re3 premake x86 / build (Release, win-x86-librw_gl3_glfw-oal) (push) Has been cancelled
re3 premake x86 / build (Vanilla, win-x86-librw_d3d9-mss) (push) Has been cancelled
re3 premake x86 / build (Vanilla, win-x86-librw_d3d9-oal) (push) Has been cancelled
re3 premake x86 / build (Vanilla, win-x86-librw_gl3_glfw-mss) (push) Has been cancelled
re3 premake x86 / build (Vanilla, win-x86-librw_gl3_glfw-oal) (push) Has been cancelled
Fix AllocateBoatWakeArray, it needs RpGeometryCreate with HAS_TRIANGLES

See merge request skmp/dca3-game!12
2025-01-06 11:46:50 +00:00
Stefanos Kornilios Mitsis Poiitidis
a2caa56275 Merge branch 'skmp/material-alpha-override-pt' into 'main'
Perform full blending for mat.a != 0xFF, should fade-in sprites (eg, Trees)

See merge request skmp/dca3-game!11
2025-01-06 11:41:11 +00:00
Stefanos Kornilios Mitsis Poiitidis
fa65f637ad Fix AllocateBoatWakeArray, it needs RpGeometryCreate with HAS_TRIANGLES 2025-01-06 13:40:31 +02:00
Stefanos Kornilios Mitsis Poiitidis
0a0a664428 Perform full blending for mat.a != 0xFF, should fade-in sprites (eg, Trees) 2025-01-05 15:40:18 +02:00
Stefanos Kornilios Mitsis Poiitidis
5abd0641a4 Merge branch 'skmp/fix-staring-into-the-void' into 'main'
Some checks are pending
re3 conan+cmake / build-cmake (openal, glfw, macos-latest, gl3) (push) Waiting to run
re3 conan+cmake / build-cmake (openal, glfw, ubuntu-18.04, gl3) (push) Waiting to run
re3 conan+cmake / build-cmake (openal, glfw, windows-latest, gl3) (push) Waiting to run
re3 conan+cmake / build-cmake (openal, windows-latest, d3d9) (push) Waiting to run
re3 cmake devkitA64 (Nintendo Switch) / build-nintendo-switch (push) Waiting to run
re3 premake amd64 / build (Debug, win-amd64-librw_d3d9-oal) (push) Waiting to run
re3 premake amd64 / build (Debug, win-amd64-librw_gl3_glfw-oal) (push) Waiting to run
re3 premake amd64 / build (Release, win-amd64-librw_d3d9-oal) (push) Waiting to run
re3 premake amd64 / build (Release, win-amd64-librw_gl3_glfw-oal) (push) Waiting to run
re3 premake x86 / build (Debug, win-x86-librw_d3d9-mss) (push) Waiting to run
re3 premake x86 / build (Debug, win-x86-librw_d3d9-oal) (push) Waiting to run
re3 premake x86 / build (Debug, win-x86-librw_gl3_glfw-mss) (push) Waiting to run
re3 premake x86 / build (Debug, win-x86-librw_gl3_glfw-oal) (push) Waiting to run
re3 premake x86 / build (Release, win-x86-librw_d3d9-mss) (push) Waiting to run
re3 premake x86 / build (Release, win-x86-librw_d3d9-oal) (push) Waiting to run
re3 premake x86 / build (Release, win-x86-librw_gl3_glfw-mss) (push) Waiting to run
re3 premake x86 / build (Release, win-x86-librw_gl3_glfw-oal) (push) Waiting to run
re3 premake x86 / build (Vanilla, win-x86-librw_d3d9-mss) (push) Waiting to run
re3 premake x86 / build (Vanilla, win-x86-librw_d3d9-oal) (push) Waiting to run
re3 premake x86 / build (Vanilla, win-x86-librw_gl3_glfw-mss) (push) Waiting to run
re3 premake x86 / build (Vanilla, win-x86-librw_gl3_glfw-oal) (push) Waiting to run
Revert buggy optimizations in CMatrix::Rotate*. Didn't investigate much, they aren't often called

Closes #8 and #6

See merge request skmp/dca3-game!9
2025-01-05 08:21:54 +00:00
Stefanos Kornilios Mitsis Poiitidis
ca866bdb2a Fix sim build 2025-01-05 10:15:30 +02:00
Stefanos Kornilios Mitsis Poiitidis
bf0e36858e Use fipr in CMatrix::Rotate, simplifies it so it doesn't ICE
Some checks failed
re3 conan+cmake / build-cmake (openal, glfw, macos-latest, gl3) (push) Has been cancelled
re3 conan+cmake / build-cmake (openal, glfw, ubuntu-18.04, gl3) (push) Has been cancelled
re3 conan+cmake / build-cmake (openal, glfw, windows-latest, gl3) (push) Has been cancelled
re3 conan+cmake / build-cmake (openal, windows-latest, d3d9) (push) Has been cancelled
re3 cmake devkitA64 (Nintendo Switch) / build-nintendo-switch (push) Has been cancelled
re3 premake amd64 / build (Debug, win-amd64-librw_d3d9-oal) (push) Has been cancelled
re3 premake amd64 / build (Debug, win-amd64-librw_gl3_glfw-oal) (push) Has been cancelled
re3 premake amd64 / build (Release, win-amd64-librw_d3d9-oal) (push) Has been cancelled
re3 premake amd64 / build (Release, win-amd64-librw_gl3_glfw-oal) (push) Has been cancelled
re3 premake x86 / build (Debug, win-x86-librw_d3d9-mss) (push) Has been cancelled
re3 premake x86 / build (Debug, win-x86-librw_d3d9-oal) (push) Has been cancelled
re3 premake x86 / build (Debug, win-x86-librw_gl3_glfw-mss) (push) Has been cancelled
re3 premake x86 / build (Debug, win-x86-librw_gl3_glfw-oal) (push) Has been cancelled
re3 premake x86 / build (Release, win-x86-librw_d3d9-mss) (push) Has been cancelled
re3 premake x86 / build (Release, win-x86-librw_d3d9-oal) (push) Has been cancelled
re3 premake x86 / build (Release, win-x86-librw_gl3_glfw-mss) (push) Has been cancelled
re3 premake x86 / build (Release, win-x86-librw_gl3_glfw-oal) (push) Has been cancelled
re3 premake x86 / build (Vanilla, win-x86-librw_d3d9-mss) (push) Has been cancelled
re3 premake x86 / build (Vanilla, win-x86-librw_d3d9-oal) (push) Has been cancelled
re3 premake x86 / build (Vanilla, win-x86-librw_gl3_glfw-mss) (push) Has been cancelled
re3 premake x86 / build (Vanilla, win-x86-librw_gl3_glfw-oal) (push) Has been cancelled
2025-01-05 09:19:02 +02:00
Stefanos Kornilios Mitsis Poiitidis
b90e8433da Revert buggy optimizations in CMatrix::Rotate*. Didn't investigate much, they aren't often called 2025-01-05 09:12:56 +02:00
Stefanos Kornilios Mitsis Poiitidis
133110c15c Merge branch 'skmp/async-pedsfx-load' into 'main'
Some checks are pending
re3 conan+cmake / build-cmake (openal, glfw, macos-latest, gl3) (push) Waiting to run
re3 conan+cmake / build-cmake (openal, glfw, ubuntu-18.04, gl3) (push) Waiting to run
re3 conan+cmake / build-cmake (openal, glfw, windows-latest, gl3) (push) Waiting to run
re3 conan+cmake / build-cmake (openal, windows-latest, d3d9) (push) Waiting to run
re3 cmake devkitA64 (Nintendo Switch) / build-nintendo-switch (push) Waiting to run
re3 premake amd64 / build (Debug, win-amd64-librw_d3d9-oal) (push) Waiting to run
re3 premake amd64 / build (Debug, win-amd64-librw_gl3_glfw-oal) (push) Waiting to run
re3 premake amd64 / build (Release, win-amd64-librw_d3d9-oal) (push) Waiting to run
re3 premake amd64 / build (Release, win-amd64-librw_gl3_glfw-oal) (push) Waiting to run
re3 premake x86 / build (Debug, win-x86-librw_d3d9-mss) (push) Waiting to run
re3 premake x86 / build (Debug, win-x86-librw_d3d9-oal) (push) Waiting to run
re3 premake x86 / build (Debug, win-x86-librw_gl3_glfw-mss) (push) Waiting to run
re3 premake x86 / build (Debug, win-x86-librw_gl3_glfw-oal) (push) Waiting to run
re3 premake x86 / build (Release, win-x86-librw_d3d9-mss) (push) Waiting to run
re3 premake x86 / build (Release, win-x86-librw_d3d9-oal) (push) Waiting to run
re3 premake x86 / build (Release, win-x86-librw_gl3_glfw-mss) (push) Waiting to run
re3 premake x86 / build (Release, win-x86-librw_gl3_glfw-oal) (push) Waiting to run
re3 premake x86 / build (Vanilla, win-x86-librw_d3d9-mss) (push) Waiting to run
re3 premake x86 / build (Vanilla, win-x86-librw_d3d9-oal) (push) Waiting to run
re3 premake x86 / build (Vanilla, win-x86-librw_gl3_glfw-mss) (push) Waiting to run
re3 premake x86 / build (Vanilla, win-x86-librw_gl3_glfw-oal) (push) Waiting to run
PedSfx: Read from I/O thread, load them on the background

See merge request skmp/dca3-game!10
2025-01-04 19:15:39 +00:00
Stefanos Kornilios Mitsis Poiitidis
6f71cfc458 PedSfx: Read from I/O thread, load them on the background 2025-01-04 17:46:48 +02:00
Stefanos Kornilios Mitsis Poiitidis
02d0103b20 Redirect to dca3.net 2024-12-31 09:05:08 +00:00
Stefanos Kornilios Mitsis Poiitidis
72da27ef0b Edit README.md 2024-12-29 22:23:08 +00:00
10 changed files with 430 additions and 202 deletions

24
.vscode/launch.json vendored
View File

@@ -24,6 +24,30 @@
}
]
},
{
"name": "dca3-sim (linux)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/dreamcast/dca3-sim.elf",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/dreamcast",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"name": "dca3-sim (mac)",
"type": "cppdbg",

View File

@@ -28,12 +28,12 @@ You will also need the following tools installed
### Cloning the dca3-game repo and downloading the prebuilt elf
- Open dreamsdk shell
- type `git clone https://gitlab.com/skmp/dca3-game.git` (and press enter)
- type `git clone --branch alpha https://gitlab.com/skmp/dca3-game.git` (and press enter)
- It should take a moment and successfully clone the repo
- type `cd dca3-game/dreamcast` (and press enter)
- type `explorer .` (and press enter).
- A folder named dreamcast with some files should be open. Keep it on the side.
- Download the artifacts from https://gitlab.com/skmp/dca3-game/-/jobs/8725216645
- Download the Alpha Prebuilt Elf from https://gitlab.com/skmp/dca3-game/-/releases
- Open artifacts.zip and extract dca3.elf to the folder that was kept open before.
- Close the folder and dreamsdk shell

View File

@@ -3,100 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DCA3 - A port of RE3 for Dreamcast</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
background-color: #121212;
color: #ffffff;
}
.navbar {
background-color: #1f1f1f;
}
.hero {
text-align: center;
padding: 100px 20px;
background-color: #333333;
border-radius: 10px;
}
.features {
margin-top: 50px;
}
.footer {
text-align: center;
padding: 20px;
background-color: #1f1f1f;
margin-top: 20px;
border-radius: 10px;
}
</style>
<title>DCA3 Redirect</title>
<meta http-equiv="refresh" content="5;url=https://dca3.net">
</head>
<body>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-dark">
<div class="container">
<a class="navbar-brand" href="#">DCA3</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#features">Features</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#download">Download</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Hero Section -->
<div class="container">
<div class="hero mt-4">
<h1>DCA3</h1>
<p>The first-ever port of Grand Theft Auto III to the SEGA Dreamcast, built from the REGTA reverse engineering project.</p>
<a href="#download" class="btn btn-primary">Download Now</a>
</div>
</div>
<!-- Features Section -->
<div class="container features text-center">
<h2 id="features" class="mb-4">Key Features</h2>
<div class="row">
<div class="col-md-4">
<h3>Faithful Port</h3>
<p>Experience GTA3 as it was meant to be, now on the Dreamcast with okay visuals and sluggish gameplay.</p>
</div>
<div class="col-md-4">
<h3>Open Source</h3>
<p>Built on the REGTA reverse engineering project, ensuring accuracy and community-driven development.</p>
</div>
<div class="col-md-4">
<h3>First Release</h3>
<p>Celebrating our first release with not very much optimized performance and compatibility for Dreamcast hardware.</p>
</div>
</div>
</div>
<!-- Download Section -->
<div class="container text-center mt-5">
<h2 id="download" class="mb-4">Download DCA3</h2>
<p>Get started with the first release of and bring GTA3 to your Dreamcast. You'll need a PC copy of GTA3 to create your .cdi</p>
<a href="#" class="btn btn-success btn-lg">Download Version 1.0</a>
</div>
<!-- Footer -->
<footer class="footer mt-4">
<p>No rights reserved. Made with love for the Dreamcast community.</p>
</footer>
<!-- Bootstrap JS Bundle -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<p>You will be redirected to <a href="https://dca3.net">dca3.net</a> in 5 seconds.</p>
</body>
</html>
</html>

View File

@@ -6302,7 +6302,7 @@ cPedComments::Process()
{
case LOADING_STATUS_NOT_LOADED:
SampleManager.LoadPedComment(sampleIndex);
#ifdef GTA_PS2 // on PC ped comment is loaded at once
#if defined(GTA_PS2) || defined(RW_DC) // on PC ped comment is loaded at once
break;
#endif
case LOADING_STATUS_LOADED:

View File

@@ -166,8 +166,12 @@ struct sfx_bank {
std::map<int, sfx_bank> sfx_banks;
int nPedSlotSfx[MAX_PEDSFX];
uint32 nPedSlotSfxReqId[MAX_PEDSFX];
uintptr_t nPedSlotSfxAddr[MAX_PEDSFX];
uint8_t nCurrentPedSlot;
file_t fdPedSfx;
volatile uint32 nPedSfxReqReadId = 1;
volatile uint32 nPedSfxReqNextId = 1;
struct WavHeader {
// RIFF Header
@@ -442,15 +446,22 @@ cSampleManager::Initialise(void)
}
});
nPedSfxReqNextId = 1;
nPedSfxReqReadId = 1;
for ( int32 i = 0; i < MAX_PEDSFX; i++ )
{
nPedSlotSfx[i] = -1;
nPedSlotSfxReqId[i] = 0;
nPedSlotSfxAddr[i] = snd_mem_malloc(PED_BLOCKSIZE_ADPCM);
debugf("PedSlot %d buffer: %p\n", i, (void*)nPedSlotSfxAddr[i]);
}
nCurrentPedSlot = 0;
fdPedSfx = fs_open(SampleBankDataFilename, O_RDONLY);
assert(fdPedSfx >= 0);
_dcAudioInitialized = true;
return TRUE;
}
@@ -458,7 +469,7 @@ cSampleManager::Initialise(void)
void
cSampleManager::Terminate(void)
{
fs_close(fdPedSfx);
}
bool8 cSampleManager::CheckForAnAudioFileOnCD(void)
@@ -637,7 +648,7 @@ cSampleManager::IsPedCommentLoaded(uint32 nComment)
slot += ARRAY_SIZE(nPedSlotSfx);
#endif
if ( nComment == nPedSlotSfx[slot] )
return LOADING_STATUS_LOADED;
return nPedSlotSfxReqId[slot] <= nPedSfxReqReadId ? LOADING_STATUS_LOADED : LOADING_STATUS_LOADING;
}
return LOADING_STATUS_NOT_LOADED;
@@ -698,27 +709,28 @@ cSampleManager::LoadPedComment(uint32 nComment)
assert(m_aSamples[nComment].nByteSize < PED_BLOCKSIZE_ADPCM);
file_t fd = fs_open(SampleBankDataFilename, O_RDONLY);
assert(fd >= 0);
debugf("Loading ped comment %d, offset: %d, size: %d\n", nComment, m_aSamples[nComment].nFileOffset, m_aSamples[nComment].nByteSize);
fs_seek(fd, m_aSamples[nComment].nFileOffset, SEEK_SET);
CdStreamQueueAudioRead(nComment, (void*)nPedSlotSfxAddr[nCurrentPedSlot], m_aSamples[nComment].nByteSize, m_aSamples[nComment].nFileOffset, [](AudioReadCmd* cmd) {
debugf("Loading ped comment %d, offset: %d, size: %d\n", nComment, m_aSamples[nComment].nFileOffset, m_aSamples[nComment].nByteSize);
fs_seek(fdPedSfx, cmd->seek, SEEK_SET);
// TODO: When we can dma directly to AICA, we can use this instead
// fs_read(fd, SPU_BASE_U8 + nPedSlotSfxAddr[nCurrentPedSlot], sizeof(nPedSlotSfxAddr));
// TODO: When we can dma directly to AICA, we can use this instead
// fs_read(fdPedSfx, SPU_BASE_U8 + (uintptr_t)cmd->dest, cmd->size);
void* stagingBuffer = memalign(32, m_aSamples[nComment].nByteSize);
assert(stagingBuffer != 0);
debugf("Allocated %d bytes at %p\n", m_aSamples[nComment].nByteSize, stagingBuffer);
int rs = fs_read(fd, stagingBuffer, m_aSamples[nComment].nByteSize);
debugf("Read %d bytes, expected %d\n", rs, m_aSamples[nComment].nByteSize);
assert(rs == m_aSamples[nComment].nByteSize);
void* stagingBuffer = memalign(32, cmd->size);
assert(stagingBuffer != 0);
debugf("Allocated %d bytes at %p\n", cmd->size, stagingBuffer);
int rs = fs_read(fdPedSfx, stagingBuffer, cmd->size);
debugf("Read %d bytes, expected %d\n", rs, cmd->size);
assert(rs == cmd->size);
fs_close(fd);
spu_memload((uintptr_t)cmd->dest, stagingBuffer, cmd->size);
free(stagingBuffer);
nPedSfxReqReadId = nPedSfxReqReadId + 1;
});
spu_memload(nPedSlotSfxAddr[nCurrentPedSlot], stagingBuffer, m_aSamples[nComment].nByteSize);
free(stagingBuffer);
nPedSlotSfxReqId[nCurrentPedSlot] = ++nPedSfxReqNextId;
nPedSlotSfx[nCurrentPedSlot] = nComment;
if ( ++nCurrentPedSlot >= MAX_PEDSFX )

View File

@@ -1,4 +1,5 @@
#pragma once
#include <functional>
#define CDSTREAM_SECTOR_SIZE 2048
@@ -43,7 +44,14 @@ char *CdStreamGetImageName(int32 cd);
void CdStreamRemoveImages(void);
int32 CdStreamGetNumImages(void);
void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek);
struct AudioReadCmd {
void* dest;
int fd;
size_t size;
size_t seek;
std::function<void(AudioReadCmd*)> callback;
};
void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek, std::function<void(AudioReadCmd*)> callback = nullptr);
void CdStreamDiscardAudioRead(int fd);
#ifdef FLUSHABLE_STREAMING

View File

@@ -26,12 +26,6 @@
#include "CdStream.h"
#include "rwcore.h"
#include "MemoryMgr.h"
struct AudioReadCmd {
void* dest;
int fd;
size_t size;
size_t seek;
};
#define CDDEBUG(f, ...) debug ("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__)
#define CDTRACE(f, ...) printf("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__)
@@ -489,8 +483,16 @@ std::vector<AudioReadCmd> pendingAudioReads;
std::mutex pendingAudioReadsMutex;
#endif
// Will replace a previous read request for the same file descriptor
void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek) {
void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek, std::function<void(AudioReadCmd*)> callback) {
AudioReadCmd cmd = { pBuffer, fd, bytes, seek};
if (!callback) {
cmd.callback = [](AudioReadCmd* cmd){
lseek(cmd->fd, cmd->seek, SEEK_SET);
read(cmd->fd, cmd->dest, cmd->size);
};
} else {
cmd.callback = callback;
}
{
#if !defined(DC_SH4)
std::lock_guard<std::mutex> lock(pendingAudioReadsMutex);
@@ -514,6 +516,7 @@ void CdStreamQueueAudioRead(int fd, void* pBuffer, size_t bytes, size_t seek) {
sem_post(gCdStreamSema);
}
void CdStreamDiscardAudioRead(int fd) {
#if !defined(DC_SH4)
std::lock_guard<std::mutex> lock(pendingAudioReadsMutex);
@@ -564,8 +567,7 @@ int read_loop(int fd, void* pBuffer, size_t bytes) {
total_read += read_bytes;
auto cmd = CdStreamNextAudioRead();
while (cmd.fd != -1) {
lseek(cmd.fd, cmd.seek, SEEK_SET);
read(cmd.fd, cmd.dest, cmd.size);
cmd.callback(&cmd);
cmd = CdStreamNextAudioRead();
}
}
@@ -581,8 +583,7 @@ void *CdStreamThread(void *param)
auto cmd = CdStreamNextAudioRead();
while (cmd.fd != -1) {
lseek(cmd.fd, cmd.seek, SEEK_SET);
read(cmd.fd, cmd.dest, cmd.size);
cmd.callback(&cmd);
cmd = CdStreamNextAudioRead();
}

View File

@@ -282,7 +282,7 @@ CMatrix::SetRotate(float xAngle, float yAngle, float zAngle)
void
CMatrix::RotateX(float x)
{
#ifdef DC_SH4
#if 0 && defined(DC_SH4) // this is bugged and does not yield correct results
mat_load(reinterpret_cast<matrix_t *>(this));
mat_rotate_x(x);
mat_store(reinterpret_cast<matrix_t *>(this));
@@ -312,7 +312,7 @@ CMatrix::RotateX(float x)
void
CMatrix::RotateY(float y)
{
#ifdef DC_SH4
#if 0 && defined(DC_SH4) // this is bugged and does not yield correct results
mat_load(reinterpret_cast<matrix_t *>(this));
mat_rotate_y(y);
mat_store(reinterpret_cast<matrix_t *>(this));
@@ -342,7 +342,7 @@ CMatrix::RotateY(float y)
void
CMatrix::RotateZ(float z)
{
#ifdef DC_SH4
#if 0 && defined(DC_SH4) // this is bugged and does not yield correct results
mat_load(reinterpret_cast<matrix_t *>(this));
mat_rotate_z(z);
mat_store(reinterpret_cast<matrix_t *>(this));
@@ -372,7 +372,7 @@ CMatrix::RotateZ(float z)
void
CMatrix::Rotate(float x, float y, float z)
{
#ifdef DC_SH4
#if 0 && defined(DC_SH4) // this is bugged and does not yield correct results
mat_load(reinterpret_cast<matrix_t *>(this));
mat_rotate(x, y, z);
mat_store(reinterpret_cast<matrix_t *>(this));
@@ -404,6 +404,20 @@ CMatrix::Rotate(float x, float y, float z)
float z2 = sZ * sY - (cZ * sX) * cY;
float z3 = cX * cY;
#if !defined(DC_TEXCONV) && !defined(DC_SIM)
this->rx = fipr(x1, y1, z1, 0, rx, ry, rz, 0);
this->ry = fipr(x2, y2, z2, 0, rx, ry, rz, 0);
this->rz = fipr(x3, y3, z3, 0, rx, ry, rz, 0);
this->fx = fipr(x1, y1, z1, 0, ux, uy, uz, 0);
this->fy = fipr(x2, y2, z2, 0, ux, uy, uz, 0);
this->fz = fipr(x3, y3, z3, 0, ux, uy, uz, 0);
this->ux = fipr(x1, y1, z1, 0, ax, ay, az, 0);
this->uy = fipr(x2, y2, z2, 0, ax, ay, az, 0);
this->uz = fipr(x3, y3, z3, 0, ax, ay, az, 0);
this->px = fipr(x1, y1, z1, 0, px, py, pz, 0);
this->py = fipr(x2, y2, z2, 0, px, py, pz, 0);
this->pz = fipr(x3, y3, z3, 0, px, py, pz, 0);
#else
this->rx = x1 * rx + y1 * ry + z1 * rz;
this->ry = x2 * rx + y2 * ry + z2 * rz;
this->rz = x3 * rx + y3 * ry + z3 * rz;
@@ -416,6 +430,7 @@ CMatrix::Rotate(float x, float y, float z)
this->px = x1 * px + y1 * py + z1 * pz;
this->py = x2 * px + y2 * py + z2 * pz;
this->pz = x3 * px + y3 * py + z3 * pz;
#endif
#endif
}

View File

@@ -1479,7 +1479,8 @@ CWaterLevel::AllocateBoatWakeArray()
apGeomArray[geom] = RpGeometryCreate(9*9, 8*8*2, rpGEOMETRYTRISTRIP
| rpGEOMETRYPRELIT
| rpGEOMETRYMODULATEMATERIALCOLOR
| rpGEOMETRYTEXTURED);
| rpGEOMETRYTEXTURED
| rw::Geometry::HAS_TRIANGLES/* RW_DC specific */);
ASSERT(apGeomArray[geom] != nil);
RpTriangle *geomTriangles = RpGeometryGetTriangles(apGeomArray[geom]);

View File

@@ -4,6 +4,11 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <cstdint>
#include <vector>
#include <iostream>
#include <cmath>
#include <set>
#if !defined(DC_TEXCONV) && !defined(MACOS64)
#include <malloc.h>
@@ -595,6 +600,8 @@ struct atomic_context_t {
matrix_t worldView, mtx;
UniformObject uniform;
V3d cameraDir;
float cosPhi;
};
/* END Ligting Structs and Defines */
@@ -1754,24 +1761,23 @@ void addInterpolatedVertex(const pvr_vertex16_t& v1, const pvr_vertex16_t& v2, u
}
struct MeshInfo {
int16_t meshletCount;
int16_t meshletOffset;
uint32_t meshletCount;
uint32_t meshletOffset;
};
static_assert(sizeof(MeshInfo) == 4);
static_assert(sizeof(MeshInfo) == 8);
struct MeshletInfo {
RwSphere boundingSphere;
uint16_t flags;
int8_t pad;
uint8_t flags;
uint8_t clusterCount;
int8_t vertexSize;
uint16_t vertexCount;
uint16_t indexCount;
uint8_t vertexCount;
uint32_t vertexOffset;
uint32_t indexOffset;
uint32_t skinIndexOffset;
uint32_t skinWeightOffset;
};
static_assert(sizeof(MeshletInfo) == 40); // or 32 if !skin
static_assert(sizeof(MeshletInfo) == 36); // or 28 if !skin
inline __attribute__((always_inline)) void setLights(Atomic *atomic, WorldLights *lightData, UniformObject &uniformObject)
@@ -3229,6 +3235,41 @@ size_t vertexBufferFree() {
}
float calculateMaxAngularOffset(
const V3d& cameraPos, // Camera position
const V3d& cameraLookAt, // Camera look-at point (not direction)
const V3d& sphereCenter, // Sphere center
float sphereRadius) // Sphere radius
{
// Compute the look-at direction vector (normalized)
V3d frustumAxis = cameraLookAt;
// Vector from camera to sphere center
V3d V = sub(sphereCenter, cameraPos);
if (length(V) < sphereRadius) {
return 1;
}
// Distance along the camera look-at direction
float d_parallel = dot(V, frustumAxis);
// Perpendicular vector (V_perp = V - d_parallel * frustumAxis)
V3d V_perp = {
V.x - d_parallel * frustumAxis.x,
V.y - d_parallel * frustumAxis.y,
V.z - d_parallel * frustumAxis.z
};
float d_perpendicular = length(V_perp);
// Total perpendicular distance including sphere radius
float d_total = d_perpendicular + sphereRadius;
// Maximum angular offset
float theta_max = std::atan2(d_total, d_parallel);
return theta_max; // Result in radians
}
void defaultRenderCB(ObjPipeline *pipe, Atomic *atomic) {
rw::Camera *cam = engine->currentCamera;
// Frustum Culling
@@ -3286,6 +3327,28 @@ void defaultRenderCB(ObjPipeline *pipe, Atomic *atomic) {
mat_apply((matrix_t*)&atomicContexts.back().worldView);
mat_store((matrix_t*)&atomicContexts.back().mtx);
// TODO: Don't invert twice for the same atomic
Matrix magic;
Matrix::invert(&magic, atomic->getFrame()->getLTM());
V3d camera_dir;
V3d::transformVectors(&camera_dir, &cam->getFrame()->getLTM()->at, 1, &magic);
camera_dir = normalize(camera_dir);
// required because of camera up being negative
camera_dir.x*=-1;
camera_dir.y*=-1;
camera_dir.z*=-1;
atomicContexts.back().cameraDir = camera_dir;
float angle = calculateMaxAngularOffset(cam->getFrame()->getLTM()->pos, cam->getFrame()->getLTM()->at, atomic->getWorldBoundingSphere()->center, atomic->getWorldBoundingSphere()->radius);
angle = fabs(angle);
if (angle > 70/2 * M_PI / 180) {
angle = 70/2 * M_PI / 180;
}
atomicContexts.back().cosPhi = cosf(90 * M_PI / 180 + 5.1 * M_PI / 180 + angle);
int16_t contextId = atomicContexts.size() - 1;
assert(numMeshes <= 32767);
@@ -3294,6 +3357,8 @@ void defaultRenderCB(ObjPipeline *pipe, Atomic *atomic) {
for (int16_t n = 0; n < numMeshes; n++) {
bool doBlend = meshes[n].material->color.alpha != 255; // TODO: check all vertexes for alpha?
bool doBlendMaterial = doBlend;
bool textured = geo->numTexCoordSets && meshes[n].material->texture;
if (textured) {
doBlend |= Raster::formatHasAlpha(meshes[n].material->texture->raster->format);
@@ -3343,7 +3408,7 @@ void defaultRenderCB(ObjPipeline *pipe, Atomic *atomic) {
pvr_poly_cxt_t cxt;
int pvrList;
if (doBlend || isMatFX) {
if (doAlphaTest) {
if (doAlphaTest && !doBlendMaterial) {
pvrList = PVR_LIST_PT_POLY;
} else {
pvrList = PVR_LIST_TR_POLY;
@@ -3613,9 +3678,37 @@ void defaultRenderCB(ObjPipeline *pipe, Atomic *atomic) {
auto indexData = (int8_t*)&dcModel->data[meshlet->indexOffset];
if (!clippingRequired) {
submitMeshletSelector[textured](OCR_SPACE, indexData, meshlet->indexCount);
unsigned numClusters = meshlet->clusterCount;
auto currentIndexData = indexData;
do {
V3d coneNormal = { currentIndexData[0] / 127.f, currentIndexData[1] / 127.f, currentIndexData[2] / 127.f };
float costheta = dot(acp->cameraDir, coneNormal);
unsigned indexCount = (uint8_t&)currentIndexData[3];
currentIndexData += 4;
if (costheta >= acp->cosPhi) {
submitMeshletSelector[textured](OCR_SPACE, currentIndexData, indexCount);
} else {
// printf("CONE CULL, %f %f, %f %f %f\n", costheta, acp->cosPhi, meshlet->coneNormal.x, meshlet->coneNormal.y, meshlet->coneNormal.z);
}
currentIndexData += indexCount;
} while(--numClusters != 0);
} else {
clipAndsubmitMeshletSelector[textured](OCR_SPACE, indexData, meshlet->indexCount);
unsigned numClusters = meshlet->clusterCount;
auto currentIndexData = indexData;
do {
V3d coneNormal = { currentIndexData[0] / 127.f, currentIndexData[1] / 127.f, currentIndexData[2] / 127.f };
float costheta = dot(acp->cameraDir, coneNormal);
unsigned indexCount = (uint8_t&)currentIndexData[3];
currentIndexData += 4;
if (costheta >= acp->cosPhi) {
clipAndsubmitMeshletSelector[textured](OCR_SPACE, currentIndexData, indexCount);
} else {
// printf("CONE CULL, %f %f, %f %f %f\n", costheta, acp->cosPhi, meshlet->coneNormal.x, meshlet->coneNormal.y, meshlet->coneNormal.z);
}
currentIndexData += indexCount;
} while(--numClusters != 0);
}
if (meshContext->matfxContextOffset != SIZE_MAX) {
@@ -3639,9 +3732,37 @@ void defaultRenderCB(ObjPipeline *pipe, Atomic *atomic) {
tnlMeshletEnvMap(OCR_SPACE, &dcModel->data[meshlet->vertexOffset] + normalOffset, meshlet->vertexCount, meshlet->vertexSize, &matfxContext->mtx, matfxContext->coefficient);
if (!clippingRequired) {
submitMeshletSelector[true](OCR_SPACE, indexData, meshlet->indexCount);
unsigned numClusters = meshlet->clusterCount;
auto currentIndexData = indexData;
do {
V3d coneNormal = { currentIndexData[0] / 127.f, currentIndexData[1] / 127.f, currentIndexData[2] / 127.f };
float costheta = dot(acp->cameraDir, coneNormal);
unsigned indexCount = (uint8_t&)currentIndexData[3];
currentIndexData += 4;
if (costheta >= acp->cosPhi) {
submitMeshletSelector[true](OCR_SPACE, currentIndexData, indexCount);
} else {
// printf("CONE CULL, %f %f, %f %f %f\n", costheta, acp->cosPhi, meshlet->coneNormal.x, meshlet->coneNormal.y, meshlet->coneNormal.z);
}
currentIndexData += indexCount;
} while(--numClusters != 0);
} else {
clipAndsubmitMeshletSelector[true](OCR_SPACE, indexData, meshlet->indexCount);
unsigned numClusters = meshlet->clusterCount;
auto currentIndexData = indexData;
do {
V3d coneNormal = { currentIndexData[0] / 127.f, currentIndexData[1] / 127.f, currentIndexData[2] / 127.f };
float costheta = dot(acp->cameraDir, coneNormal);
unsigned indexCount = (uint8_t&)currentIndexData[3];
currentIndexData += 4;
if (costheta >= acp->cosPhi) {
clipAndsubmitMeshletSelector[true](OCR_SPACE, currentIndexData, indexCount);
} else {
// printf("CONE CULL, %f %f, %f %f %f\n", costheta, acp->cosPhi, meshlet->coneNormal.x, meshlet->coneNormal.y, meshlet->coneNormal.z);
}
currentIndexData += indexCount;
} while(--numClusters != 0);
}
}
}
@@ -3709,7 +3830,7 @@ void defaultRenderCB(ObjPipeline *pipe, Atomic *atomic) {
};
if (doBlend || isMatFX) {
if (doAlphaTest) {
if (doAlphaTest && !doBlendMaterial) {
ptCallbacks.emplace_back(std::move(renderCB));
} else {
blendCallbacks.emplace_back(std::move(renderCB));
@@ -4780,17 +4901,84 @@ centerTexCoords(Geometry *g)
rwFree(groupIDs);
}
bool isDegenerate(const V3d& v1, const V3d& v2, const V3d& v3) {
// Cluster structure
struct ConeCluster {
V3d normal; // Average normal of the cluster
std::vector<uint32_t> faces; // Indices of triangles in this cluster
};
V3d calculateNormal(const V3d& v1, const V3d& v2, const V3d& v3) {
V3d u = {v2.x - v1.x, v2.y - v1.y, v2.z - v1.z};
V3d v = {v3.x - v1.x, v3.y - v1.y, v3.z - v1.z};
V3d crs = cross(u, v);
if (length(crs) < 0.0000001f) {
return true;
if (length(crs) < 0.00001f) {
return {0, 0, 0};
} else {
return false;
return normalize(crs);
}
}
// Process mesh with cone clustering logic
std::vector<ConeCluster> processMeshWithClustering(uint16_t* indices, uint32_t numIndices, const V3d* vertices, float cosThreshold) {
std::vector<V3d> triangleNormals(numIndices / 3); // Store triangle normals
std::vector<bool> processed(numIndices / 3, false); // Track processed triangles
// Compute normals for each triangle
for (uint32_t i = 0; i < numIndices; i += 3) {
uint16_t idx1 = indices[i];
uint16_t idx2 = indices[i + 1];
uint16_t idx3 = indices[i + 2];
// Calculate and store the triangle normal
triangleNormals[i / 3] = calculateNormal(vertices[idx1], vertices[idx2], vertices[idx3]);
}
// Perform clustering
std::vector<ConeCluster> clusters;
for (size_t i = 0; i < triangleNormals.size(); ++i) {
if (processed[i]) continue;
if (length(triangleNormals[i]) < 0.000001f)
continue;
// Create a new cluster
ConeCluster cluster;
cluster.normal = triangleNormals[i];
cluster.faces.push_back(i);
processed[i] = true;
// Find and add similar triangles to this cluster
for (size_t j = 0; j < triangleNormals.size(); ++j) {
if (processed[j]) continue;
float similarity = dot(cluster.normal, triangleNormals[j]);
if (similarity >= cosThreshold) {
cluster.faces.push_back(j);
processed[j] = true;
}
}
// Normalize the cluster normal (optional)
cluster.normal = normalize(cluster.normal);
// Store the cluster
clusters.push_back(cluster);
}
// // Process each cluster
// for (size_t c = 0; c < clusters.size(); ++c) {
// std::cout << "Cluster " << c << ":\n";
// std::cout << " Average Normal: (" << clusters[c].normal.x << ", " << clusters[c].normal.y << ", " << clusters[c].normal.z << ")\n";
// std::cout << " Triangles: ";
// for (auto face : clusters[c].faces) {
// std::cout << face << " ";
// }
// std::cout << "\n";
// }
return clusters;
}
bool isDegenerateByIndex(uint16_t idx1, uint16_t idx2, uint16_t idx3) {
return idx1 == idx2 || idx1 == idx3 || idx2 == idx3;
}
@@ -4961,10 +5149,16 @@ RwSphere calculateBoundingSphere(V3d* vertexData, size_t count) {
return sphere;
}
struct ConeClusterStrip {
triangle_stripper::primitive_group* strip;
ConeCluster* cluster;
};
struct meshlet {
std::set<uint16_t> vertices;
std::map<uint16_t, uint8_t> vertexToLocalIndex;
std::vector<triangle_stripper::primitive_group*> strips;
std::vector<ConeClusterStrip*> strips;
size_t vertexDataOffset;
size_t indexDataOffset;
size_t skinIndexDataOffset;
@@ -5022,7 +5216,8 @@ void processGeom(Geometry *geo) {
int32 n = geo->meshHeader->numMeshes;
auto meshes = geo->meshHeader->getMeshes();
std::vector<primitive_vector> pvecs(n);
std::vector<std::vector<primitive_vector>> pvecs(n);
std::vector<std::vector<ConeCluster>> pclus(n);
std::vector<std::vector<meshlet>> meshMeshlets(n);
size_t totalIndices = 0, strips = 0, totalTrilist = 0;
@@ -5048,6 +5243,10 @@ void processGeom(Geometry *geo) {
skinIndices = (uint32_t*)skin->indices;
}
float angularThreshold = 5.0f * M_PI / 180.0f;
float cosThreshold = std::cos(angularThreshold);
std::vector<size_t> canonicalIdx(geo->numVertices, SIZE_MAX);
for (size_t i = 0; i < geo->numVertices; i++) {
@@ -5095,6 +5294,7 @@ void processGeom(Geometry *geo) {
}
}
texconvf("Found %zu vertex duplicates, %.2f%%\n", dups, (float)dups/geo->numVertices*100);
for (int meshNum = 0; meshNum < n; meshNum++) {
auto mesh = &meshes[meshNum];
@@ -5130,28 +5330,44 @@ void processGeom(Geometry *geo) {
mesh->indices[i] = canonicalIdx[mesh->indices[i]];
}
{
indices Indices(mesh->indices, mesh->indices + mesh->numIndices);
auto clusters = processMeshWithClustering(mesh->indices, mesh->numIndices, geo->morphTargets[0].vertices, cosThreshold);
for (auto&& cluster: clusters) {
std::vector<uint16_t> idx;
idx.reserve(cluster.faces.size()*3);
for (auto && face: cluster.faces) {
if (isDegenerateByIndex(mesh->indices[face*3 + 0], mesh->indices[face*3 + 1], mesh->indices[face*3 + 2])) {
continue;
}
idx.push_back(mesh->indices[face*3 + 0]);
idx.push_back(mesh->indices[face*3 + 1]);
idx.push_back(mesh->indices[face*3 + 2]);
}
indices Indices(idx.begin(), idx.end());
primitive_vector PrimitivesVector;
tri_stripper TriStripper(Indices);
TriStripper.SetMinStripSize(0);
TriStripper.SetCacheSize(0);
TriStripper.SetBackwardSearch(true);
TriStripper.Strip(&pvecs[meshNum]);
TriStripper.Strip(&PrimitivesVector);
pvecs[meshNum].push_back(PrimitivesVector);
}
pclus[meshNum] = clusters;
mesh->indices = oldIndices;
mesh->numIndices = oldNumIndices;
for (auto &&strip: pvecs[meshNum]) {
totalIndices += strip.Indices.size();
if (strip.Type == TRIANGLES) {
assert(strip.Indices.size()%3==0);
strips += strip.Indices.size()/3;
} else {
strips ++;
for (auto &&strip2: pvecs[meshNum]) {
for (auto &&strip: strip2) {
totalIndices += strip.Indices.size();
if (strip.Type == TRIANGLES) {
assert(strip.Indices.size()%3==0);
strips += strip.Indices.size()/3;
} else {
strips ++;
}
}
}
}
@@ -5164,23 +5380,34 @@ void processGeom(Geometry *geo) {
size_t meshVerticesCount = 0;
size_t meshletIndexesCount = 0;
size_t meshletVerticesCount = 0;
std::vector<std::vector<ConeClusterStrip>> meshConeClusterStrips(pvecs.size());
for (int pvn = 0; pvn < pvecs.size(); pvn++) {
auto &&prims = pvecs[pvn];
auto &&prims2 = pvecs[pvn];
std::set<uint16_t> meshletVertices;
std::vector<primitive_group*> meshletStrips;
std::list<ConeClusterStrip*> strips;
std::vector<ConeClusterStrip*> meshletStrips;
std::list<primitive_group*> strips;
for (auto &&strip: prims) {
strips.push_back(&strip);
for (int cvn = 0; cvn < prims2.size(); cvn++) {
auto&& prims = prims2[cvn];
std::list<ConeClusterStrip> strips;
for (auto &&strip: prims) {
meshConeClusterStrips[pvn].push_back({&strip, &pclus[pvn][cvn]});
}
}
for (auto&& ccs: meshConeClusterStrips[pvn]) {
strips.push_back(&ccs);
}
#undef printf
while(strips.size()) {
for(;;) {
// pluck strip with fewest new indices
primitive_group* bestStrip = nullptr;
ConeClusterStrip* bestStrip = nullptr;
size_t remainingVertices = 128 - meshletVertices.size();
size_t bestSharedVertices = 0;
@@ -5189,7 +5416,7 @@ void processGeom(Geometry *geo) {
auto &&strip = *strip_ptr;
std::set<uint16_t> newVertices;
size_t sharedVertices = 0;
for (auto &&idx: strip.Indices) {
for (auto &&idx: strip.strip->Indices) {
if (meshletVertices.find(idx) == meshletVertices.end()) {
newVertices.insert(idx);
} else {
@@ -5212,7 +5439,7 @@ void processGeom(Geometry *geo) {
// add strip to meshlet
meshletStrips.push_back(bestStrip);
for (auto &&idx: bestStrip->Indices) {
for (auto &&idx: bestStrip->strip->Indices) {
meshletVertices.insert(idx);
}
strips.remove(bestStrip);
@@ -5222,7 +5449,7 @@ void processGeom(Geometry *geo) {
// printf("Meshlet constructed, %ld strips, %zu vertices\n", meshletStrips.size(), meshletVertices.size());
for (auto &&strip: meshletStrips) {
meshletIndexesCount += strip->Indices.size();
meshletIndexesCount += strip->strip->Indices.size();
}
meshletVerticesCount += meshletVertices.size();
@@ -5240,10 +5467,13 @@ void processGeom(Geometry *geo) {
}
std::set<uint16_t> meshVertices;
for (auto &&strip: prims) {
meshIndexesCount += strip.Indices.size();
for (auto &&idx: strip.Indices) {
meshVertices.insert(idx);
for (int cvn = 0; cvn < prims2.size(); cvn++) {
auto&& prims = prims2[cvn];
for (auto &&strip: prims) {
meshIndexesCount += strip.Indices.size();
for (auto &&idx: strip.Indices) {
meshVertices.insert(idx);
}
}
}
meshVerticesCount += meshVertices.size();
@@ -5260,19 +5490,15 @@ void processGeom(Geometry *geo) {
for (size_t i = 0; i < meshMeshlets.size(); i++) {
auto &&mesh = meshMeshlets[i];
assert(mesh.size() <= 32767);
meshData.write<int16_t>(mesh.size());
assert(mesh.size() <= UINT32_MAX);
meshData.write<uint32_t>(mesh.size());
assert((meshletData.size() + meshMeshlets.size() * 4) <= 32767);
meshData.write<int16_t>(meshletData.size() + meshMeshlets.size() * 4);
assert((meshletData.size() + meshMeshlets.size() * sizeof(MeshInfo)) <= UINT32_MAX);
meshData.write<uint32_t>(meshletData.size() + meshMeshlets.size() * sizeof(MeshInfo));
for (auto && meshlet: mesh) {
auto boundingSphere = meshlet.calculateBoundingSphere(vertices);
uint32_t totalIndexes = 0;
for(auto&& strip: meshlet.strips) {
totalIndexes += strip->Indices.size();
}
// write out vertex data
@@ -5295,18 +5521,50 @@ void processGeom(Geometry *geo) {
// write out index data
meshlet.indexDataOffset = indexData.size();
for(auto&& strip: meshlet.strips) {
if (strip->Type == TRIANGLES) {
for (size_t i = 0; i < strip->Indices.size(); i+=3) {
indexData.write<uint8_t>(meshlet.vertexToLocalIndex[strip->Indices[i]]);
indexData.write<uint8_t>(meshlet.vertexToLocalIndex[strip->Indices[i+1]]);
indexData.write<uint8_t>(meshlet.vertexToLocalIndex[strip->Indices[i+2]] | 128);
std::map<ConeCluster*, std::vector<triangle_stripper::primitive_group*>> groupedStrips;
for(auto&& clusterStrip: meshlet.strips) {
groupedStrips[clusterStrip->cluster].push_back(clusterStrip->strip);
}
size_t clusterCount = 0;
for(auto&& stripGroup: groupedStrips) {
uint32_t totalIndexes = 256; // force a new cluster
size_t totalIndexPatchPoint = 0;
for (auto&& strip: stripGroup.second) {
if (totalIndexes + strip->Indices.size() > 255) {
if (totalIndexPatchPoint) {
assert(totalIndexes != 0);
assert(totalIndexes <= 255);
indexData[totalIndexPatchPoint] = totalIndexes;
}
totalIndexes = 0;
clusterCount++;
indexData.write<int8_t>(stripGroup.first->normal.x * 127);
indexData.write<int8_t>(stripGroup.first->normal.y * 127);
indexData.write<int8_t>(stripGroup.first->normal.z * 127);
totalIndexPatchPoint = indexData.size();
indexData.write<uint8_t>(0);
}
} else {
for (size_t i = 0; i < strip->Indices.size(); i++) {
indexData.write<uint8_t>(meshlet.vertexToLocalIndex[strip->Indices[i]] | ((i + 1) == strip->Indices.size() ? 128 : 0));
totalIndexes += strip->Indices.size();
if (strip->Type == TRIANGLES) {
for (size_t i = 0; i < strip->Indices.size(); i+=3) {
indexData.write<uint8_t>(meshlet.vertexToLocalIndex[strip->Indices[i]]);
indexData.write<uint8_t>(meshlet.vertexToLocalIndex[strip->Indices[i+1]]);
indexData.write<uint8_t>(meshlet.vertexToLocalIndex[strip->Indices[i+2]] | 128);
}
} else {
for (size_t i = 0; i < strip->Indices.size(); i++) {
indexData.write<uint8_t>(meshlet.vertexToLocalIndex[strip->Indices[i]] | ((i + 1) == strip->Indices.size() ? 128 : 0));
}
}
}
assert(totalIndexPatchPoint != 0);
assert(totalIndexes != 0);
assert(totalIndexes <= 255);
indexData[totalIndexPatchPoint] = totalIndexes;
}
// write out skinning data
@@ -5457,15 +5715,14 @@ void processGeom(Geometry *geo) {
// write out meshlet data
meshletData.write(boundingSphere);
//isTextured, isNormaled, isColored, small_xyz, pad_xyz, small_uv
uint16_t flags = texcoorded | (normaled << 1) | (colored << 2) | (!big_vertex << 3) | (pad_xyz << 4) | (!big_uv << 5);
meshletData.write<uint16_t>(flags);
meshletData.write<uint8_t>(0);
//bool textured, bool normaled, bool colored, bool big_vertex, bool big_uv, bool pad_xyz
uint8_t flags = texcoorded | (normaled << 1) | (colored << 2) | (!big_vertex << 3) | (pad_xyz << 4) | (!big_uv << 5);
meshletData.write<uint8_t>(flags);
assert(clusterCount <= 255);
meshletData.write<uint8_t>(clusterCount);
meshletData.write<uint8_t>(vertexSize);
assert(meshlet.vertices.size() <= 65535);
meshletData.write<uint16_t>(meshlet.vertices.size());
assert(totalIndexes <= 65535);
meshletData.write<uint16_t>(totalIndexes);
//bool textured, bool normaled, bool colored, bool big_vertex, bool big_uv, bool pad_xyz
assert(meshlet.vertices.size() <= 255);
meshletData.write<uint8_t>(meshlet.vertices.size());
meshlet.rewriteOffsetVDO = meshletData.size();
meshletData.write<uint32_t>(meshlet.vertexDataOffset); // will be patched
meshlet.rewriteOffsetIDO = meshletData.size();