#include #include #include #include #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]; } }