summaryrefslogtreecommitdiff
path: root/src/skin.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/skin.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/skin.cpp')
-rw-r--r--src/skin.cpp486
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];
+}
+
+}