summaryrefslogtreecommitdiff
path: root/src/geoplg.cpp
diff options
context:
space:
mode:
authorclaude-bot <[email protected]>2026-07-13 12:40:03 +0000
committerclaude-bot <[email protected]>2026-07-13 12:40:03 +0000
commit847962910f0bff071f3bf07c9abb87764fb6cac3 (patch)
treeddcd429e134c7fd5f72ddc97ced175de8d66fcd0 /src/geoplg.cpp
downloadlibrw-master.tar.gz
librw-master.zip
Import aap/librw @ masterHEADmaster
Snapshot for re3/reVC vendoring, per @lzcnt. Source: https://github.com/aap/librw (master).
Diffstat (limited to 'src/geoplg.cpp')
-rw-r--r--src/geoplg.cpp339
1 files changed, 339 insertions, 0 deletions
diff --git a/src/geoplg.cpp b/src/geoplg.cpp
new file mode 100644
index 0000000..c1e1356
--- /dev/null
+++ b/src/geoplg.cpp
@@ -0,0 +1,339 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "rwbase.h"
+#include "rwerror.h"
+#include "rwplg.h"
+#include "rwpipeline.h"
+#include "rwobjects.h"
+#include "rwengine.h"
+#include "rwanim.h"
+#include "rwplugins.h"
+#include "ps2/rwps2.h"
+#include "ps2/rwps2plg.h"
+#include "d3d/rwxbox.h"
+#include "d3d/rwd3d8.h"
+#include "d3d/rwd3d9.h"
+#include "gl/rwwdgl.h"
+#include "gl/rwgl3.h"
+
+#define PLUGIN_ID 2
+
+namespace rw {
+
+static uint16 nextSerialNum = 1;
+
+// Mesh
+
+// Allocate a mesh header, meshes and optionally indices.
+// If existing meshes already exist, retain their information.
+MeshHeader*
+Geometry::allocateMeshes(int32 numMeshes, uint32 numIndices, bool32 noIndices)
+{
+ uint32 sz;
+ MeshHeader *mh;
+ Mesh *m;
+ uint16 *indices;
+ int32 oldNumMeshes;
+ int32 i;
+ sz = sizeof(MeshHeader) + numMeshes*sizeof(Mesh);
+ if(!noIndices)
+ sz += numIndices*sizeof(uint16);
+ if(this->meshHeader){
+ oldNumMeshes = this->meshHeader->numMeshes;
+ mh = (MeshHeader*)rwResize(this->meshHeader, sz, MEMDUR_EVENT | ID_GEOMETRY);
+ this->meshHeader = mh;
+ }else{
+ oldNumMeshes = 0;
+ mh = (MeshHeader*)rwNew(sz, MEMDUR_EVENT | ID_GEOMETRY);
+ mh->flags = 0;
+ this->meshHeader = mh;
+ }
+ mh->numMeshes = numMeshes;
+ mh->serialNum = nextSerialNum++;
+ mh->totalIndices = numIndices;
+ m = mh->getMeshes();
+ indices = (uint16*)&m[numMeshes];
+ for(i = 0; i < mh->numMeshes; i++){
+ // keep these
+ if(i >= oldNumMeshes){
+ m->material = nil;
+ m->numIndices = 0;
+ }
+ // always init indices
+ if(noIndices)
+ m->indices = nil;
+ else{
+ m->indices = indices;
+ indices += m->numIndices;
+ }
+ m++;
+ }
+ return mh;
+}
+
+void
+MeshHeader::setupIndices(void)
+{
+ int32 i;
+ uint16 *indices;
+ Mesh *m;
+ m = this->getMeshes();
+ indices = m->indices;
+ // return if native
+ if(indices == nil)
+ return;
+ for(i = 0; i < this->numMeshes; i++){
+ m->indices = indices;
+ indices += m->numIndices;
+ m++;
+ }
+}
+
+struct MeshHeaderStream
+{
+ uint32 flags;
+ uint32 numMeshes;
+ uint32 totalIndices;
+};
+
+struct MeshStream
+{
+ uint32 numIndices;
+ int32 matIndex;
+};
+
+static Stream*
+readMesh(Stream *stream, int32 len, void *object, int32, int32)
+{
+ MeshHeaderStream mhs;
+ MeshStream ms;
+ MeshHeader *mh;
+ Mesh *mesh;
+ int32 indbuf[256];
+ uint16 *indices;
+ Geometry *geo = (Geometry*)object;
+
+ stream->read32(&mhs, sizeof(MeshHeaderStream));
+ // Have to do this dance for War Drum's meshes
+ bool32 hasData = len > int32(sizeof(MeshHeaderStream)+mhs.numMeshes*sizeof(MeshStream));
+ assert(geo->meshHeader == nil);
+ geo->meshHeader = nil;
+ mh = geo->allocateMeshes(mhs.numMeshes, mhs.totalIndices,
+ geo->flags & Geometry::NATIVE && !hasData);
+ mh->flags = mhs.flags;
+
+ mesh = mh->getMeshes();
+ indices = mesh->indices;
+ for(uint32 i = 0; i < mh->numMeshes; i++){
+ stream->read32(&ms, sizeof(MeshStream));
+ mesh->numIndices = ms.numIndices;
+ mesh->material = geo->matList.materials[ms.matIndex];
+ if(geo->flags & Geometry::NATIVE){
+ // War Drum OpenGL stores uint16 indices here
+ if(hasData){
+ mesh->indices = indices;
+ indices += mesh->numIndices;
+ stream->read16(mesh->indices,
+ mesh->numIndices*2);
+ }
+ }else{
+ mesh->indices = indices;
+ indices += mesh->numIndices;
+ uint16 *ind = mesh->indices;
+ int32 numIndices = mesh->numIndices;
+ for(; numIndices > 0; numIndices -= 256){
+ int32 n = numIndices < 256 ? numIndices : 256;
+ stream->read32(indbuf, n*4);
+ for(int32 j = 0; j < n; j++)
+ ind[j] = indbuf[j];
+ ind += n;
+ }
+ }
+ mesh++;
+ }
+ return stream;
+}
+
+static Stream*
+writeMesh(Stream *stream, int32, void *object, int32, int32)
+{
+ MeshHeaderStream mhs;
+ MeshStream ms;
+ int32 indbuf[256];
+ Geometry *geo = (Geometry*)object;
+ mhs.flags = geo->meshHeader->flags;
+ mhs.numMeshes = geo->meshHeader->numMeshes;
+ mhs.totalIndices = geo->meshHeader->totalIndices;
+ stream->write32(&mhs, sizeof(MeshHeaderStream));
+ Mesh *mesh = geo->meshHeader->getMeshes();
+ for(uint32 i = 0; i < geo->meshHeader->numMeshes; i++){
+ ms.numIndices = mesh->numIndices;
+ ms.matIndex = geo->matList.findIndex(mesh->material);
+ stream->write32(&ms, sizeof(MeshStream));
+ if(geo->flags & Geometry::NATIVE){
+ assert(geo->instData != nil);
+ if(geo->instData->platform == PLATFORM_WDGL)
+ stream->write16(mesh->indices,
+ mesh->numIndices*2);
+ }else{
+ uint16 *ind = mesh->indices;
+ int32 numIndices = mesh->numIndices;
+ for(; numIndices > 0; numIndices -= 256){
+ int32 n = numIndices < 256 ? numIndices : 256;
+ for(int32 j = 0; j < n; j++)
+ indbuf[j] = ind[j];
+ stream->write32(indbuf, n*4);
+ ind += n;
+ }
+ }
+ mesh++;
+ }
+ return stream;
+}
+
+static int32
+getSizeMesh(void *object, int32, int32)
+{
+ Geometry *geo = (Geometry*)object;
+ if(geo->meshHeader == nil)
+ return -1;
+ int32 size = 12 + geo->meshHeader->numMeshes*8;
+ if(geo->flags & Geometry::NATIVE){
+ assert(geo->instData != nil);
+ if(geo->instData->platform == PLATFORM_WDGL)
+ size += geo->meshHeader->totalIndices*2;
+ }else{
+ size += geo->meshHeader->totalIndices*4;
+ }
+ return size;
+}
+
+void
+registerMeshPlugin(void)
+{
+ Geometry::registerPlugin(0, ID_MESH, nil, nil, nil);
+ Geometry::registerPluginStream(ID_MESH, readMesh, writeMesh, getSizeMesh);
+}
+
+// Returns the maximum number of triangles. Just so
+// we can allocate enough before instancing. This does not
+// take into account degerate triangles or ADC bits as
+// we don't look at the data.
+uint32
+MeshHeader::guessNumTriangles(void)
+{
+ if(this->flags == MeshHeader::TRISTRIP)
+ return this->totalIndices - 2*this->numMeshes;
+ else
+ return this->totalIndices/3;
+}
+
+// Native Data
+
+static void*
+destroyNativeData(void *object, int32 offset, int32 size)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil)
+ return object;
+ if(geometry->instData->platform == PLATFORM_PS2)
+ return ps2::destroyNativeData(object, offset, size);
+ if(geometry->instData->platform == PLATFORM_WDGL)
+ return wdgl::destroyNativeData(object, offset, size);
+ if(geometry->instData->platform == PLATFORM_XBOX)
+ return xbox::destroyNativeData(object, offset, size);
+ if(geometry->instData->platform == PLATFORM_D3D8)
+ return d3d8::destroyNativeData(object, offset, size);
+ if(geometry->instData->platform == PLATFORM_D3D9)
+ return d3d9::destroyNativeData(object, offset, size);
+ if(geometry->instData->platform == PLATFORM_GL3)
+ return gl3::destroyNativeData(object, offset, size);
+ return object;
+}
+
+static Stream*
+readNativeData(Stream *stream, int32 len, void *object, int32 o, int32 s)
+{
+ ChunkHeaderInfo header;
+ uint32 libid;
+ uint32 platform;
+ // ugly hack to find out platform
+ stream->seek(-4);
+ libid = stream->readU32();
+ readChunkHeaderInfo(stream, &header);
+ if(header.type == ID_STRUCT &&
+ libraryIDPack(header.version, header.build) == libid){
+ platform = stream->readU32();
+ stream->seek(-16);
+ if(platform == PLATFORM_PS2)
+ return ps2::readNativeData(stream, len, object, o, s);
+ else if(platform == PLATFORM_XBOX)
+ return xbox::readNativeData(stream, len, object, o, s);
+ else if(platform == PLATFORM_D3D8)
+ return d3d8::readNativeData(stream, len, object, o, s);
+ else if(platform == PLATFORM_D3D9)
+ return d3d9::readNativeData(stream, len, object, o, s);
+ else{
+ fprintf(stderr, "unknown platform %d\n", platform);
+ stream->seek(len);
+ }
+ }else{
+ stream->seek(-12);
+ wdgl::readNativeData(stream, len, object, o, s);
+ }
+ return stream;
+}
+
+static Stream*
+writeNativeData(Stream *stream, int32 len, void *object, int32 o, int32 s)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil)
+ return stream;
+ if(geometry->instData->platform == PLATFORM_PS2)
+ return ps2::writeNativeData(stream, len, object, o, s);
+ else if(geometry->instData->platform == PLATFORM_WDGL)
+ return wdgl::writeNativeData(stream, len, object, o, s);
+ else if(geometry->instData->platform == PLATFORM_XBOX)
+ return xbox::writeNativeData(stream, len, object, o, s);
+ else if(geometry->instData->platform == PLATFORM_D3D8)
+ return d3d8::writeNativeData(stream, len, object, o, s);
+ else if(geometry->instData->platform == PLATFORM_D3D9)
+ return d3d9::writeNativeData(stream, len, object, o, s);
+ return stream;
+}
+
+static int32
+getSizeNativeData(void *object, int32 offset, int32 size)
+{
+ Geometry *geometry = (Geometry*)object;
+ if(geometry->instData == nil)
+ return 0;
+ if(geometry->instData->platform == PLATFORM_PS2)
+ return ps2::getSizeNativeData(object, offset, size);
+ else if(geometry->instData->platform == PLATFORM_WDGL)
+ return wdgl::getSizeNativeData(object, offset, size);
+ else if(geometry->instData->platform == PLATFORM_XBOX)
+ return xbox::getSizeNativeData(object, offset, size);
+ else if(geometry->instData->platform == PLATFORM_D3D8)
+ return d3d8::getSizeNativeData(object, offset, size);
+ else if(geometry->instData->platform == PLATFORM_D3D9)
+ return d3d9::getSizeNativeData(object, offset, size);
+ return 0;
+}
+
+void
+registerNativeDataPlugin(void)
+{
+ Geometry::registerPlugin(0, ID_NATIVEDATA,
+ nil, destroyNativeData, nil);
+ Geometry::registerPluginStream(ID_NATIVEDATA,
+ readNativeData,
+ writeNativeData,
+ getSizeNativeData);
+}
+
+}