librw/src/skin.cpp

418 lines
10 KiB
C++

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include "rwbase.h"
#include "rwerror.h"
#include "rwplg.h"
#include "rwpipeline.h"
#include "rwobjects.h"
#include "rwanim.h"
#include "rwengine.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"
#include "gl/rwgl3plg.h"
#define PLUGIN_ID ID_SKIN
namespace rw {
SkinGlobals skinGlobals = { 0, 0, { nil } };
static void*
createSkin(void *object, int32 offset, int32)
{
*PLUGINOFFSET(Skin*, object, offset) = nil;
return object;
}
static void*
destroySkin(void *object, int32 offset, int32)
{
Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
if(skin){
rwFree(skin->data);
rwFree(skin->remapIndices);
// delete[] skin->platformData;
}
rwFree(skin);
return object;
}
static void*
copySkin(void *dst, void *src, int32 offset, int32)
{
Skin *srcskin = *PLUGINOFFSET(Skin*, src, offset);
if(srcskin == nil)
return dst;
Geometry *geometry = (Geometry*)src;
assert(geometry->instData == nil);
assert(((Geometry*)src)->numVertices == ((Geometry*)dst)->numVertices);
Skin *dstskin = rwNewT(Skin, 1, MEMDUR_EVENT | ID_SKIN);
*PLUGINOFFSET(Skin*, dst, offset) = dstskin;
dstskin->numBones = srcskin->numBones;
dstskin->numUsedBones = srcskin->numUsedBones;
dstskin->numWeights = srcskin->numWeights;
assert(0 && "can't copy skin yet");
dstskin->init(srcskin->numBones, srcskin->numUsedBones,
geometry->numVertices);
memcpy(dstskin->usedBones, srcskin->usedBones, srcskin->numUsedBones);
memcpy(dstskin->inverseMatrices, srcskin->inverseMatrices,
srcskin->numBones*64);
memcpy(dstskin->indices, srcskin->indices, geometry->numVertices*4);
memcpy(dstskin->weights, srcskin->weights, geometry->numVertices*16);
return dst;
}
Stream*
readSkinSplitData(Stream *stream, Skin *skin)
{
uint32 sz;
int8 *data;
skin->boneLimit = stream->readI32();
skin->numMeshes = stream->readI32();
skin->rleSize = stream->readI32();
if(skin->numMeshes){
sz = skin->numBones + 2*(skin->numMeshes+skin->rleSize);
data = (int8*)rwMalloc(sz, MEMDUR_EVENT | ID_SKIN);
stream->read(data, sz);
skin->remapIndices = data;
skin->rleCount = (Skin::RLEcount*)(data + skin->numBones);
skin->rle = (Skin::RLE*)(data + skin->numBones + 2*skin->numMeshes);
}
return stream;
}
Stream*
writeSkinSplitData(Stream *stream, Skin *skin)
{
stream->writeI32(skin->boneLimit);
stream->writeI32(skin->numMeshes);
stream->writeI32(skin->rleSize);
if(skin->numMeshes)
stream->write(skin->remapIndices,
skin->numBones + 2*(skin->numMeshes+skin->rleSize));
return stream;
}
int32
skinSplitDataSize(Skin *skin)
{
if(skin->numMeshes == 0)
return 12;
return 12 + skin->numBones + 2*(skin->numMeshes+skin->rleSize);
}
static Stream*
readSkin(Stream *stream, int32 len, void *object, int32 offset, int32)
{
uint8 header[4];
Geometry *geometry = (Geometry*)object;
if(geometry->instData){
// TODO: function pointers
if(geometry->instData->platform == PLATFORM_PS2)
return ps2::readNativeSkin(stream, len, object, offset);
else if(geometry->instData->platform == PLATFORM_WDGL)
return wdgl::readNativeSkin(stream, len, object, offset);
else if(geometry->instData->platform == PLATFORM_XBOX)
return xbox::readNativeSkin(stream, len, object, offset);
else{
assert(0 && "unsupported native skin platform");
return nil;
}
}
stream->read(header, 4); // numBones, numUsedBones,
// numWeights, unused
Skin *skin = rwNewT(Skin, 1, MEMDUR_EVENT | ID_SKIN);
*PLUGINOFFSET(Skin*, geometry, offset) = skin;
// numUsedBones and numWeights appear in/after 34003
// but not in/before 33002 (probably rw::version >= 0x34000)
bool oldFormat = header[1] == 0;
// Use numBones for numUsedBones to allocate data,
// find out the correct value later
if(oldFormat)
skin->init(header[0], header[0], geometry->numVertices);
else
skin->init(header[0], header[1], geometry->numVertices);
skin->numWeights = header[2];
if(!oldFormat)
stream->read(skin->usedBones, skin->numUsedBones);
if(skin->indices)
stream->read(skin->indices, geometry->numVertices*4);
if(skin->weights)
stream->read(skin->weights, geometry->numVertices*16);
for(int32 i = 0; i < skin->numBones; i++){
if(oldFormat)
stream->seek(4); // skip 0xdeaddead
stream->read(&skin->inverseMatrices[i*16], 64);
}
if(oldFormat){
skin->findNumWeights(geometry->numVertices);
skin->findUsedBones(geometry->numVertices);
}
if(!oldFormat)
readSkinSplitData(stream, skin);
return stream;
}
static Stream*
writeSkin(Stream *stream, int32 len, void *object, int32 offset, int32)
{
uint8 header[4];
Geometry *geometry = (Geometry*)object;
if(geometry->instData){
if(geometry->instData->platform == PLATFORM_PS2)
return ps2::writeNativeSkin(stream, len, object, offset);
else if(geometry->instData->platform == PLATFORM_WDGL)
return wdgl::writeNativeSkin(stream, len, object, offset);
else if(geometry->instData->platform == PLATFORM_XBOX)
return xbox::writeNativeSkin(stream, len, object, offset);
else{
assert(0 && "unsupported native skin platform");
return nil;
}
}
Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
// not sure which version introduced the new format
bool oldFormat = version < 0x34000;
header[0] = skin->numBones;
if(oldFormat){
header[1] = 0;
header[2] = 0;
}else{
header[1] = skin->numUsedBones;
header[2] = skin->numWeights;
}
header[3] = 0;
stream->write(header, 4);
if(!oldFormat)
stream->write(skin->usedBones, skin->numUsedBones);
stream->write(skin->indices, geometry->numVertices*4);
stream->write(skin->weights, geometry->numVertices*16);
for(int32 i = 0; i < skin->numBones; i++){
if(oldFormat)
stream->writeU32(0xdeaddead);
stream->write(&skin->inverseMatrices[i*16], 64);
}
if(!oldFormat)
writeSkinSplitData(stream, skin);
return stream;
}
static int32
getSizeSkin(void *object, int32 offset, int32)
{
Geometry *geometry = (Geometry*)object;
if(geometry->instData){
if(geometry->instData->platform == PLATFORM_PS2)
return ps2::getSizeNativeSkin(object, offset);
if(geometry->instData->platform == PLATFORM_WDGL)
return wdgl::getSizeNativeSkin(object, offset);
if(geometry->instData->platform == PLATFORM_XBOX)
return xbox::getSizeNativeSkin(object, offset);
if(geometry->instData->platform == PLATFORM_D3D8)
return -1;
if(geometry->instData->platform == PLATFORM_D3D9)
return -1;
assert(0 && "unsupported native skin platform");
}
Skin *skin = *PLUGINOFFSET(Skin*, object, offset);
if(skin == nil)
return -1;
int32 size = 4 + geometry->numVertices*(16+4) +
skin->numBones*64;
// not sure which version introduced the new format
if(version < 0x34000)
size += skin->numBones*4;
else
size += skin->numUsedBones + skinSplitDataSize(skin);
return size;
}
static void
skinRights(void *object, int32, int32, uint32)
{
Skin::setPipeline((Atomic*)object, 1);
}
static void*
createSkinAtm(void *object, int32 offset, int32)
{
*PLUGINOFFSET(void*, object, offset) = nil;
return object;
}
static void*
destroySkinAtm(void *object, int32 offset, int32)
{
return object;
}
static void*
copySkinAtm(void *dst, void *src, int32 offset, int32)
{
*PLUGINOFFSET(void*, dst, offset) = *PLUGINOFFSET(void*, src, offset);
return dst;
}
static void*
skinOpen(void *o, int32, int32)
{
// init dummy pipelines
ObjPipeline *defpipe = new ObjPipeline(PLATFORM_NULL);
defpipe->pluginID = ID_SKIN;
defpipe->pluginData = 1;
for(uint i = 0; i < nelem(skinGlobals.pipelines); i++)
skinGlobals.pipelines[i] = defpipe;
return o;
}
static void*
skinClose(void *o, int32, int32)
{
return o;
}
void
registerSkinPlugin(void)
{
Driver::registerPlugin(PLATFORM_NULL, 0, ID_SKIN,
skinOpen, skinClose);
ps2::initSkin();
xbox::initSkin();
d3d8::initSkin();
d3d9::initSkin();
wdgl::initSkin();
gl3::initSkin();
int32 o;
o = Geometry::registerPlugin(sizeof(Skin*), ID_SKIN,
createSkin, destroySkin, copySkin);
Geometry::registerPluginStream(ID_SKIN,
readSkin, writeSkin, getSizeSkin);
skinGlobals.geoOffset = o;
o = Atomic::registerPlugin(sizeof(HAnimHierarchy*),ID_SKIN,
createSkinAtm, destroySkinAtm, copySkinAtm);
skinGlobals.atomicOffset = o;
Atomic::setStreamRightsCallback(ID_SKIN, skinRights);
}
void
Skin::init(int32 numBones, int32 numUsedBones, int32 numVertices)
{
this->numBones = numBones;
this->numUsedBones = numUsedBones;
uint32 size = this->numUsedBones +
this->numBones*64 +
numVertices*(16+4) + 0xF;
this->data = rwNewT(uint8, size, MEMDUR_EVENT | ID_SKIN);
uint8 *p = this->data;
this->usedBones = nil;
if(this->numUsedBones){
this->usedBones = p;
p += this->numUsedBones;
}
p = (uint8*)(((uintptr)p + 0xF) & ~0xF);
this->inverseMatrices = nil;
if(this->numBones){
this->inverseMatrices = (float*)p;
p += 64*this->numBones;
}
this->indices = nil;
if(numVertices){
this->indices = p;
p += 4*numVertices;
}
this->weights = nil;
if(numVertices)
this->weights = (float*)p;
this->boneLimit = 0;
this->numMeshes = 0;
this->rleSize = 0;
this->remapIndices = nil;
this->rleCount = nil;
this->rle = nil;
this->platformData = nil;
}
//static_assert(sizeof(Skin::RLEcount) == 2, "RLEcount size");
//static_assert(sizeof(Skin::RLE) == 2, "RLE size");
void
Skin::findNumWeights(int32 numVertices)
{
this->numWeights = 1;
float *w = this->weights;
while(numVertices--){
while(w[this->numWeights] != 0.0f){
this->numWeights++;
if(this->numWeights == 4)
return;
}
w += 4;
}
}
void
Skin::findUsedBones(int32 numVertices)
{
uint8 usedTab[256];
uint8 *indices = this->indices;
float *weights = this->weights;
memset(usedTab, 0, 256);
while(numVertices--){
for(int32 i = 0; i < this->numWeights; i++){
if(weights[i] == 0.0f)
continue; // TODO: this could probably be optimized
if(usedTab[indices[i]] == 0)
usedTab[indices[i]]++;
}
indices += 4;
weights += 4;
}
this->numUsedBones = 0;
for(int32 i = 0; i < 256; i++)
if(usedTab[i])
this->usedBones[this->numUsedBones++] = i;
}
void
Skin::setPipeline(Atomic *a, int32 type)
{
(void)type;
a->pipeline = skinGlobals.pipelines[rw::platform];
}
}