librw/src/geoplg.cpp

338 lines
8.9 KiB
C++
Raw Normal View History

2016-06-26 10:16:33 +01:00
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include "rwbase.h"
#include "rwerror.h"
#include "rwplg.h"
#include "rwpipeline.h"
#include "rwobjects.h"
2017-08-09 09:57:32 +01:00
#include "rwengine.h"
#include "rwanim.h"
2016-06-26 10:16:33 +01:00
#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 {
2020-04-16 11:53:25 +01:00
static uint16 nextSerialNum = 1;
2016-06-26 10:16:33 +01:00
// Mesh
2017-08-25 13:06:53 +01:00
// 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);
2017-10-04 21:08:26 +01:00
this->meshHeader = mh;
2017-08-25 13:06:53 +01:00
}else{
oldNumMeshes = 0;
mh = (MeshHeader*)rwNew(sz, MEMDUR_EVENT | ID_GEOMETRY);
mh->flags = 0;
this->meshHeader = mh;
}
mh->numMeshes = numMeshes;
2020-04-16 11:53:25 +01:00
mh->serialNum = nextSerialNum++;
2017-08-25 13:06:53 +01:00
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;
};
2016-06-26 10:16:33 +01:00
static Stream*
readMesh(Stream *stream, int32 len, void *object, int32, int32)
{
2017-08-25 13:06:53 +01:00
MeshHeaderStream mhs;
MeshStream ms;
MeshHeader *mh;
Mesh *mesh;
2016-06-26 10:16:33 +01:00
int32 indbuf[256];
2017-08-25 13:06:53 +01:00
uint16 *indices;
Geometry *geo = (Geometry*)object;
stream->read(&mhs, sizeof(MeshHeaderStream));
// Have to do this dance for War Drum's meshes
bool32 hasData = len > 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);
2017-10-20 21:38:28 +01:00
mh->flags = mhs.flags;
2017-08-25 13:06:53 +01:00
mesh = mh->getMeshes();
indices = mesh->indices;
for(uint32 i = 0; i < mh->numMeshes; i++){
stream->read(&ms, sizeof(MeshStream));
mesh->numIndices = ms.numIndices;
mesh->material = geo->matList.materials[ms.matIndex];
2017-03-16 10:42:59 +00:00
if(geo->flags & Geometry::NATIVE){
2017-08-25 13:06:53 +01:00
// War Drum OpenGL stores uint16 indices here
2016-06-26 10:16:33 +01:00
if(hasData){
2017-08-25 13:06:53 +01:00
mesh->indices = indices;
indices += mesh->numIndices;
2016-06-26 10:16:33 +01:00
stream->read(mesh->indices,
mesh->numIndices*2);
}
}else{
2017-08-25 13:06:53 +01:00
mesh->indices = indices;
indices += mesh->numIndices;
2016-06-26 10:16:33 +01:00
uint16 *ind = mesh->indices;
int32 numIndices = mesh->numIndices;
for(; numIndices > 0; numIndices -= 256){
int32 n = numIndices < 256 ? numIndices : 256;
stream->read(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)
{
2017-08-25 13:06:53 +01:00
MeshHeaderStream mhs;
MeshStream ms;
2016-06-26 10:16:33 +01:00
int32 indbuf[256];
2017-08-25 13:06:53 +01:00
Geometry *geo = (Geometry*)object;
mhs.flags = geo->meshHeader->flags;
mhs.numMeshes = geo->meshHeader->numMeshes;
mhs.totalIndices = geo->meshHeader->totalIndices;
stream->write(&mhs, sizeof(MeshHeaderStream));
Mesh *mesh = geo->meshHeader->getMeshes();
2016-06-26 10:16:33 +01:00
for(uint32 i = 0; i < geo->meshHeader->numMeshes; i++){
2017-08-25 13:06:53 +01:00
ms.numIndices = mesh->numIndices;
ms.matIndex = geo->matList.findIndex(mesh->material);
stream->write(&ms, sizeof(MeshStream));
2017-03-16 10:42:59 +00:00
if(geo->flags & Geometry::NATIVE){
2016-06-26 10:16:33 +01:00
assert(geo->instData != nil);
if(geo->instData->platform == PLATFORM_WDGL)
stream->write(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->write(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;
2017-03-16 10:42:59 +00:00
if(geo->flags & Geometry::NATIVE){
2016-06-26 10:16:33 +01:00
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);
2016-06-26 10:16:33 +01:00
}
2017-08-25 13:06:53 +01:00
// 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)
2016-06-26 10:16:33 +01:00
{
2017-08-25 13:06:53 +01:00
if(this->flags == MeshHeader::TRISTRIP)
return this->totalIndices - 2*this->numMeshes;
else
return this->totalIndices/3;
2016-06-26 10:16:33 +01:00
}
// 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);
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 &&
2016-06-26 10:16:33 +01:00
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);
}
}