diff options
| author | claude-bot <[email protected]> | 2026-07-13 12:40:03 +0000 |
|---|---|---|
| committer | claude-bot <[email protected]> | 2026-07-13 12:40:03 +0000 |
| commit | 847962910f0bff071f3bf07c9abb87764fb6cac3 (patch) | |
| tree | ddcd429e134c7fd5f72ddc97ced175de8d66fcd0 /src/skin.cpp | |
| download | librw-master.tar.gz librw-master.zip | |
Snapshot for re3/reVC vendoring, per @lzcnt.
Source: https://github.com/aap/librw (master).
Diffstat (limited to 'src/skin.cpp')
| -rw-r--r-- | src/skin.cpp | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/src/skin.cpp b/src/skin.cpp new file mode 100644 index 0000000..abaa548 --- /dev/null +++ b/src/skin.cpp @@ -0,0 +1,486 @@ +#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 "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 }, 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->read8(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->write8(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->read8(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->read8(skin->usedBones, skin->numUsedBones); + if(skin->indices) + stream->read8(skin->indices, geometry->numVertices*4); + if(skin->weights) + stream->read32(skin->weights, geometry->numVertices*16); + for(int32 i = 0; i < skin->numBones; i++){ + if(oldFormat) + stream->seek(4); // skip 0xdeaddead + stream->read32(&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->write8(header, 4); + if(!oldFormat) + stream->write8(skin->usedBones, skin->numUsedBones); + stream->write8(skin->indices, geometry->numVertices*4); + stream->write32(skin->weights, geometry->numVertices*16); + for(int32 i = 0; i < skin->numBones; i++){ + if(oldFormat) + stream->writeU32(0xdeaddead); + stream->write32(&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 Stream* +readSkinLegacy(Stream *stream, int32 len, void *object, int32, int32) +{ + Atomic *atomic = (Atomic*)object; + Geometry *geometry = atomic->geometry; + + int32 numBones = stream->readI32(); + int32 numVertices = stream->readI32(); + assert(numVertices == geometry->numVertices); + + Skin *skin = rwNewT(Skin, 1, MEMDUR_EVENT | ID_SKIN); + *PLUGINOFFSET(Skin*, geometry, skinGlobals.geoOffset) = skin; + skin->init(numBones, numBones, numVertices); + skin->legacyType = 1; + skin->numWeights = 4; + + stream->read8(skin->indices, numVertices*4); + stream->read32(skin->weights, numVertices*16); + + HAnimHierarchy *hier = HAnimHierarchy::create(numBones, nil, nil, 0, 36); + + for(int i = 0; i < numBones; i++){ + hier->nodeInfo[i].id = stream->readI32(); + hier->nodeInfo[i].index = stream->readI32(); + hier->nodeInfo[i].flags = stream->readI32() & 3; +// printf("%d %d %d %d\n", i, hier->nodeInfo[i].id, hier->nodeInfo[i].index, hier->nodeInfo[i].flags); + stream->read32(&skin->inverseMatrices[i*16], 64); + + Matrix mat; + Matrix::invert(&mat, (Matrix*)&skin->inverseMatrices[i*16]); +// printf("[ [ %8.4f, %8.4f, %8.4f, %8.4f ]\n" +// " [ %8.4f, %8.4f, %8.4f, %8.4f ]\n" +// " [ %8.4f, %8.4f, %8.4f, %8.4f ]\n" +// " [ %8.4f, %8.4f, %8.4f, %8.4f ] ]\n" +// " %08x == flags\n", +// mat.right.x, mat.up.x, mat.at.x, mat.pos.x, +// mat.right.y, mat.up.y, mat.at.y, mat.pos.y, +// mat.right.z, mat.up.z, mat.at.z, mat.pos.z, +// 0.0f, 0.0f, 0.0f, 1.0f, +// mat.flags); + } + Frame *frame = atomic->getFrame()->child; + assert(frame->next == nil); // in old files atomic is above hierarchy it seems + assert(frame->count() == numBones); // assuming one frame per node this should also be true + HAnimData::get(frame)->hierarchy = hier; + hier->parentFrame = frame; + + return stream; +} + +static void +skinRights(void *object, int32, int32, uint32) +{ + Skin::setPipeline((Atomic*)object, 1); +} + +static void +skinAlways(void *object, int32, int32) +{ + Atomic *atomic = (Atomic*)object; + Geometry *geo = atomic->geometry; + if(geo == nil) return; + Skin *skin = Skin::get(geo); + if(skin == nil) return; + 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 + skinGlobals.dummypipe = ObjPipeline::create(); + skinGlobals.dummypipe->pluginID = ID_SKIN; + skinGlobals.dummypipe->pluginData = 1; + for(uint i = 0; i < nelem(skinGlobals.pipelines); i++) + skinGlobals.pipelines[i] = skinGlobals.dummypipe; + return o; +} + +static void* +skinClose(void *o, int32, int32) +{ + for(uint i = 0; i < nelem(skinGlobals.pipelines); i++) + if(skinGlobals.pipelines[i] == skinGlobals.dummypipe) + matFXGlobals.pipelines[i] = nil; + skinGlobals.dummypipe->destroy(); + skinGlobals.dummypipe = nil; + 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::registerPluginStream(ID_SKIN, readSkinLegacy, nil, nil); + Atomic::setStreamRightsCallback(ID_SKIN, skinRights); + Atomic::setStreamAlwaysCallback(ID_SKIN, skinAlways); +} + +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; + this->legacyType = 0; +} + + + +//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]; +} + +} |
