#include #include #include #include #include "rwbase.h" #include "rwplugin.h" #include "rwpipeline.h" #include "rwobjects.h" #include "rwps2.h" #include "rwogl.h" #include "rwxbox.h" #include "rwd3d8.h" #include "rwd3d9.h" using namespace std; namespace rw { // // HAnim // int32 hAnimOffset; bool32 hAnimDoStream = 1; HAnimHierarchy* HAnimHierarchy::create(int32 numNodes, int32 *nodeFlags, int32 *nodeIDs, int32 flags, int32 maxKeySize) { HAnimHierarchy *hier = (HAnimHierarchy*)malloc(sizeof(*hier)); hier->numNodes = numNodes; hier->flags = flags; hier->maxInterpKeyFrameSize = maxKeySize; hier->parentFrame = NULL; hier->parentHierarchy = hier; if(hier->flags & 2) hier->matrices = hier->matricesUnaligned = NULL; else{ hier->matricesUnaligned = (float*) new uint8[hier->numNodes*64 + 15]; hier->matrices = (float*)((uintptr)hier->matricesUnaligned & ~0xF); } hier->nodeInfo = new HAnimNodeInfo[hier->numNodes]; for(int32 i = 0; i < hier->numNodes; i++){ hier->nodeInfo[i].id = nodeIDs[i]; hier->nodeInfo[i].index = i; hier->nodeInfo[i].flags = nodeFlags[i]; hier->nodeInfo[i].frame = NULL; } return hier; } void HAnimHierarchy::destroy(void) { delete[] (uint8*)this->matricesUnaligned; delete[] this->nodeInfo; free(this); } static Frame* findById(Frame *f, int32 id) { if(f == NULL) return NULL; HAnimData *hanim = HAnimData::get(f); if(hanim->id >= 0 && hanim->id == id) return f; Frame *ff = findById(f->next, id); if(ff) return ff; return findById(f->child, id); } void HAnimHierarchy::attachByIndex(int32 idx) { int32 id = this->nodeInfo[idx].id; Frame *f = findById(this->parentFrame, id); this->nodeInfo[idx].frame = f; } void HAnimHierarchy::attach(void) { for(int32 i = 0; i < this->numNodes; i++) this->attachByIndex(i); } int32 HAnimHierarchy::getIndex(int32 id) { for(int32 i = 0; i < this->numNodes; i++) if(this->nodeInfo[i].id == id) return i; return -1; } HAnimHierarchy* HAnimHierarchy::get(Frame *f) { return HAnimData::get(f)->hierarchy; } HAnimHierarchy* HAnimHierarchy::find(Frame *f) { if(f == NULL) return NULL; HAnimHierarchy *hier = HAnimHierarchy::get(f); if(hier) return hier; hier = HAnimHierarchy::find(f->next); if(hier) return hier; return HAnimHierarchy::find(f->child); } HAnimData* HAnimData::get(Frame *f) { return PLUGINOFFSET(HAnimData, f, hAnimOffset); } static void* createHAnim(void *object, int32 offset, int32) { HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset); hanim->id = -1; hanim->hierarchy = NULL; return object; } static void* destroyHAnim(void *object, int32 offset, int32) { HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset); if(hanim->hierarchy) hanim->hierarchy->destroy(); hanim->id = -1; hanim->hierarchy = NULL; return object; } static void* copyHAnim(void *dst, void *src, int32 offset, int32) { HAnimData *dsthanim = PLUGINOFFSET(HAnimData, dst, offset); HAnimData *srchanim = PLUGINOFFSET(HAnimData, src, offset); dsthanim->id = srchanim->id; // TODO dsthanim->hierarchy = NULL; return dst; } static void readHAnim(Stream *stream, int32, void *object, int32 offset, int32) { int32 ver, numNodes; HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset); ver = stream->readI32(); assert(ver == 0x100); hanim->id = stream->readI32(); numNodes = stream->readI32(); if(numNodes != 0){ int32 flags = stream->readI32(); int32 maxKeySize = stream->readI32(); int32 *nodeFlags = new int32[numNodes]; int32 *nodeIDs = new int32[numNodes]; for(int32 i = 0; i < numNodes; i++){ nodeIDs[i] = stream->readI32(); stream->readI32(); // index...unused nodeFlags[i] = stream->readI32(); } hanim->hierarchy = HAnimHierarchy::create(numNodes, nodeFlags, nodeIDs, flags, maxKeySize); hanim->hierarchy->parentFrame = (Frame*)object; delete[] nodeFlags; delete[] nodeIDs; } } static void writeHAnim(Stream *stream, int32, void *object, int32 offset, int32) { HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset); stream->writeI32(256); stream->writeI32(hanim->id); if(hanim->hierarchy == NULL){ stream->writeI32(0); return; } HAnimHierarchy *hier = hanim->hierarchy; stream->writeI32(hier->numNodes); stream->writeI32(hier->flags); stream->writeI32(hier->maxInterpKeyFrameSize); for(int32 i = 0; i < hier->numNodes; i++){ stream->writeI32(hier->nodeInfo[i].id); stream->writeI32(hier->nodeInfo[i].index); stream->writeI32(hier->nodeInfo[i].flags); } } static int32 getSizeHAnim(void *object, int32 offset, int32) { HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset); if(!hAnimDoStream || version >= 0x35000 && hanim->id == -1 && hanim->hierarchy == NULL) return 0; if(hanim->hierarchy) return 12 + 8 + hanim->hierarchy->numNodes*12; return 12; } static void hAnimFrameRead(Stream *stream, Animation *anim) { HAnimKeyFrame *frames = (HAnimKeyFrame*)anim->keyframes; for(int32 i = 0; i < anim->numFrames; i++){ frames[i].time = stream->readF32(); stream->read(frames[i].q, 4*4); stream->read(frames[i].t, 3*4); int32 prev = stream->readI32(); frames[i].prev = &frames[prev]; } } static void hAnimFrameWrite(Stream *stream, Animation *anim) { HAnimKeyFrame *frames = (HAnimKeyFrame*)anim->keyframes; for(int32 i = 0; i < anim->numFrames; i++){ stream->writeF32(frames[i].time); stream->write(frames[i].q, 4*4); stream->write(frames[i].t, 3*4); stream->writeI32(frames[i].prev - frames); } } static uint32 hAnimFrameGetSize(Animation *anim) { return anim->numFrames*(4 + 4*4 + 3*4 + 4); } void registerHAnimPlugin(void) { hAnimOffset = Frame::registerPlugin(sizeof(HAnimData), ID_HANIMPLUGIN, createHAnim, destroyHAnim, copyHAnim); Frame::registerPluginStream(ID_HANIMPLUGIN, readHAnim, writeHAnim, getSizeHAnim); AnimInterpolatorInfo *info = new AnimInterpolatorInfo; info->id = 1; info->keyFrameSize = sizeof(HAnimKeyFrame); info->customDataSize = sizeof(HAnimKeyFrame); info->streamRead = hAnimFrameRead; info->streamWrite = hAnimFrameWrite; info->streamGetSize = hAnimFrameGetSize; registerAnimInterpolatorInfo(info); } // // Geometry // // Mesh static void readMesh(Stream *stream, int32 len, void *object, int32, int32) { Geometry *geo = (Geometry*)object; int32 indbuf[256]; uint32 buf[3]; stream->read(buf, 12); geo->meshHeader = new MeshHeader; geo->meshHeader->flags = buf[0]; geo->meshHeader->numMeshes = buf[1]; geo->meshHeader->totalIndices = buf[2]; geo->meshHeader->mesh = new Mesh[geo->meshHeader->numMeshes]; Mesh *mesh = geo->meshHeader->mesh; bool hasData = len > 12+geo->meshHeader->numMeshes*8; uint16 *p = NULL; if(!(geo->geoflags & Geometry::NATIVE) || hasData) p = new uint16[geo->meshHeader->totalIndices]; for(uint32 i = 0; i < geo->meshHeader->numMeshes; i++){ stream->read(buf, 8); mesh->numIndices = buf[0]; mesh->material = geo->materialList[buf[1]]; mesh->indices = NULL; if(geo->geoflags & Geometry::NATIVE){ // OpenGL stores uint16 indices here if(hasData){ mesh->indices = p; p += mesh->numIndices; stream->read(mesh->indices, mesh->numIndices*2); } }else{ mesh->indices = p; p += mesh->numIndices; 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++; } } static void writeMesh(Stream *stream, int32, void *object, int32, int32) { Geometry *geo = (Geometry*)object; int32 indbuf[256]; uint32 buf[3]; buf[0] = geo->meshHeader->flags; buf[1] = geo->meshHeader->numMeshes; buf[2] = geo->meshHeader->totalIndices; stream->write(buf, 12); Mesh *mesh = geo->meshHeader->mesh; for(uint32 i = 0; i < geo->meshHeader->numMeshes; i++){ buf[0] = mesh->numIndices; buf[1] = findPointer((void*)mesh->material, (void**)geo->materialList, geo->numMaterials); stream->write(buf, 8); if(geo->geoflags & Geometry::NATIVE){ assert(geo->instData != NULL); if(geo->instData->platform == PLATFORM_OGL) 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++; } } static int32 getSizeMesh(void *object, int32, int32) { Geometry *geo = (Geometry*)object; if(geo->meshHeader == NULL) return -1; int32 size = 12 + geo->meshHeader->numMeshes*8; if(geo->geoflags & Geometry::NATIVE){ assert(geo->instData != NULL); if(geo->instData->platform == PLATFORM_OGL) size += geo->meshHeader->totalIndices*2; }else{ size += geo->meshHeader->totalIndices*4; } return size; } void registerMeshPlugin(void) { Geometry::registerPlugin(0, 0x50E, NULL, NULL, NULL); Geometry::registerPluginStream(0x50E, readMesh, writeMesh, getSizeMesh); } MeshHeader::~MeshHeader(void) { // first mesh holds pointer to all indices delete[] this->mesh[0].indices; delete[] this->mesh; } void MeshHeader::allocateIndices(void) { uint16 *p = new uint16[this->totalIndices]; Mesh *mesh = this->mesh; for(uint32 i = 0; i < this->numMeshes; i++){ mesh->indices = p; p += mesh->numIndices; mesh++; } } // Native Data static void* destroyNativeData(void *object, int32 offset, int32 size) { Geometry *geometry = (Geometry*)object; if(geometry->instData == NULL) return object; if(geometry->instData->platform == PLATFORM_PS2) return ps2::destroyNativeData(object, offset, size); if(geometry->instData->platform == PLATFORM_OGL) return gl::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 void 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 && libraryIDPack(header.version, header.build) == libid){ platform = stream->readU32(); stream->seek(-16); if(platform == PLATFORM_PS2) ps2::readNativeData(stream, len, object, o, s); else if(platform == PLATFORM_XBOX) xbox::readNativeData(stream, len, object, o, s); else if(platform == PLATFORM_D3D8) d3d8::readNativeData(stream, len, object, o, s); else if(platform == PLATFORM_D3D9) d3d9::readNativeData(stream, len, object, o, s); else{ fprintf(stderr, "unknown platform %d\n", platform); stream->seek(len); } }else{ stream->seek(-12); gl::readNativeData(stream, len, object, o, s); } } static void writeNativeData(Stream *stream, int32 len, void *object, int32 o, int32 s) { Geometry *geometry = (Geometry*)object; if(geometry->instData == NULL) return; if(geometry->instData->platform == PLATFORM_PS2) ps2::writeNativeData(stream, len, object, o, s); else if(geometry->instData->platform == PLATFORM_OGL) gl::writeNativeData(stream, len, object, o, s); else if(geometry->instData->platform == PLATFORM_XBOX) xbox::writeNativeData(stream, len, object, o, s); else if(geometry->instData->platform == PLATFORM_D3D8) d3d8::writeNativeData(stream, len, object, o, s); else if(geometry->instData->platform == PLATFORM_D3D9) d3d9::writeNativeData(stream, len, object, o, s); } static int32 getSizeNativeData(void *object, int32 offset, int32 size) { Geometry *geometry = (Geometry*)object; if(geometry->instData == NULL) return -1; if(geometry->instData->platform == PLATFORM_PS2) return ps2::getSizeNativeData(object, offset, size); else if(geometry->instData->platform == PLATFORM_OGL) return gl::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 -1; } void registerNativeDataPlugin(void) { Geometry::registerPlugin(0, ID_NATIVEDATA, NULL, destroyNativeData, NULL); Geometry::registerPluginStream(ID_NATIVEDATA, readNativeData, writeNativeData, getSizeNativeData); } // // Skin // SkinGlobals skinGlobals = { 0, { NULL } }; static void* createSkin(void *object, int32 offset, int32) { *PLUGINOFFSET(Skin*, object, offset) = NULL; return object; } static void* destroySkin(void *object, int32 offset, int32) { Skin *skin = *PLUGINOFFSET(Skin*, object, offset); if(skin){ delete[] skin->data; // delete[] skin->platformData; } delete skin; return object; } static void* copySkin(void *dst, void *src, int32 offset, int32) { Skin *srcskin = *PLUGINOFFSET(Skin*, src, offset); if(srcskin == NULL) return dst; Geometry *geometry = (Geometry*)src; assert(geometry->instData == NULL); assert(((Geometry*)src)->numVertices == ((Geometry*)dst)->numVertices); Skin *dstskin = new 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; } static void 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) ps2::readNativeSkin(stream, len, object, offset); else if(geometry->instData->platform == PLATFORM_OGL) gl::readNativeSkin(stream, len, object, offset); else if(geometry->instData->platform == PLATFORM_XBOX) xbox::readNativeSkin(stream, len, object, offset); else assert(0 && "unsupported native skin platform"); return; } stream->read(header, 4); // numBones, numUsedBones, numWeights, unused Skin *skin = new 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); //{ //float *mat = &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", // mat[0], mat[4], mat[8], mat[12], // mat[1], mat[5], mat[9], mat[13], // mat[2], mat[6], mat[10], mat[14], // mat[3], mat[7], mat[11], mat[15]); //} } if(oldFormat){ skin->findNumWeights(geometry->numVertices); skin->findUsedBones(geometry->numVertices); } // no split skins in GTA if(!oldFormat) stream->seek(12); } static void 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) ps2::writeNativeSkin(stream, len, object, offset); else if(geometry->instData->platform == PLATFORM_OGL) gl::writeNativeSkin(stream, len, object, offset); else if(geometry->instData->platform == PLATFORM_XBOX) xbox::writeNativeSkin(stream, len, object, offset); else assert(0 && "unsupported native skin platform"); return; } 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); } // no split skins in GTA if(!oldFormat){ uint32 buffer[3] = { 0, 0, 0}; stream->write(buffer, 12); } } 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_OGL) return gl::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 == NULL) 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 + 12; return size; } static void skinRights(void *object, int32, int32, uint32) { Skin::setPipeline((Atomic*)object, 1); } void registerSkinPlugin(void) { 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; skinGlobals.pipelines[PLATFORM_PS2] = ps2::makeSkinPipeline(); skinGlobals.pipelines[PLATFORM_OGL] = gl::makeSkinPipeline(); skinGlobals.pipelines[PLATFORM_XBOX] = xbox::makeSkinPipeline(); skinGlobals.pipelines[PLATFORM_D3D8] = d3d8::makeSkinPipeline(); skinGlobals.pipelines[PLATFORM_D3D9] = d3d9::makeSkinPipeline(); skinGlobals.offset = Geometry::registerPlugin(sizeof(Skin*), ID_SKIN, createSkin, destroySkin, copySkin); Geometry::registerPluginStream(ID_SKIN, readSkin, writeSkin, getSizeSkin); Atomic::registerPlugin(0, ID_SKIN, NULL, NULL, NULL); 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 = new uint8[size]; uint8 *p = this->data; this->usedBones = NULL; if(this->numUsedBones){ this->usedBones = p; p += this->numUsedBones; } p = (uint8*)(((uintptr)p + 0xF) & ~0xF); this->inverseMatrices = NULL; if(this->numBones){ this->inverseMatrices = (float*)p; p += 64*this->numBones; } this->indices = NULL; if(numVertices){ this->indices = p; p += 4*numVertices; } this->weights = NULL; if(numVertices) this->weights = (float*)p; this->platformData = NULL; } 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]; } // // MatFX // // Atomic static void* createAtomicMatFX(void *object, int32 offset, int32) { *PLUGINOFFSET(int32, object, offset) = 0; return object; } static void* copyAtomicMatFX(void *dst, void *src, int32 offset, int32) { if(*PLUGINOFFSET(int32, src, offset)) MatFX::enableEffects((Atomic*)dst); return dst; } static void readAtomicMatFX(Stream *stream, int32, void *object, int32 offset, int32) { if(stream->readI32()) MatFX::enableEffects((Atomic*)object); } static void writeAtomicMatFX(Stream *stream, int32, void *object, int32 offset, int32) { stream->writeI32(*PLUGINOFFSET(int32, object, offset)); } static int32 getSizeAtomicMatFX(void *object, int32 offset, int32) { int32 flag = *PLUGINOFFSET(int32, object, offset); // TODO: not sure which version return flag || rw::version < 0x34000 ? 4 : 0; } // Material MatFXGlobals matFXGlobals = { 0, 0, { NULL } }; // TODO: Frames and Matrices? static void clearMatFX(MatFX *matfx) { for(int i = 0; i < 2; i++) switch(matfx->fx[i].type){ case MatFX::BUMPMAP: if(matfx->fx[i].bump.bumpedTex) matfx->fx[i].bump.bumpedTex->destroy(); if(matfx->fx[i].bump.tex) matfx->fx[i].bump.tex->destroy(); break; case MatFX::ENVMAP: if(matfx->fx[i].env.tex) matfx->fx[i].env.tex->destroy(); break; case MatFX::DUAL: if(matfx->fx[i].dual.tex) matfx->fx[i].dual.tex->destroy(); break; } memset(matfx, 0, sizeof(MatFX)); } void MatFX::setEffects(uint32 type) { if(this->type != 0 && this->type != type) clearMatFX(this); this->type = type; switch(type){ case BUMPMAP: case ENVMAP: case DUAL: case UVTRANSFORM: this->fx[0].type = type; this->fx[1].type = NOTHING; break; case BUMPENVMAP: this->fx[0].type = BUMPMAP; this->fx[1].type = ENVMAP; break; case DUALUVTRANSFORM: this->fx[0].type = UVTRANSFORM; this->fx[1].type = DUAL; break; } } uint32 MatFX::getEffects(Material *m) { MatFX *fx = *PLUGINOFFSET(MatFX*, m, matFXGlobals.materialOffset); if(fx) return fx->type; return 0; } uint32 MatFX::getEffectIndex(uint32 type) { for(int i = 0; i < 2; i++) if(this->fx[i].type == type) return i; return -1; } void MatFX::setBumpTexture(Texture *t) { int32 i = this->getEffectIndex(BUMPMAP); if(i >= 0) this->fx[i].bump.tex = t; } void MatFX::setBumpCoefficient(float32 coef) { int32 i = this->getEffectIndex(BUMPMAP); if(i >= 0) this->fx[i].bump.coefficient = coef; } void MatFX::setEnvTexture(Texture *t) { int32 i = this->getEffectIndex(ENVMAP); if(i >= 0) this->fx[i].env.tex = t; } void MatFX::setEnvCoefficient(float32 coef) { int32 i = this->getEffectIndex(ENVMAP); if(i >= 0) this->fx[i].env.coefficient = coef; } void MatFX::setDualTexture(Texture *t) { int32 i = this->getEffectIndex(DUAL); if(i >= 0) this->fx[i].dual.tex = t; } void MatFX::setDualSrcBlend(int32 blend) { int32 i = this->getEffectIndex(DUAL); if(i >= 0) this->fx[i].dual.srcBlend = blend; } void MatFX::setDualDestBlend(int32 blend) { int32 i = this->getEffectIndex(DUAL); if(i >= 0) this->fx[i].dual.dstBlend = blend; } static void* createMaterialMatFX(void *object, int32 offset, int32) { *PLUGINOFFSET(MatFX*, object, offset) = NULL; return object; } static void* destroyMaterialMatFX(void *object, int32 offset, int32) { MatFX *matfx = *PLUGINOFFSET(MatFX*, object, offset); if(matfx){ clearMatFX(matfx); delete matfx; } return object; } static void* copyMaterialMatFX(void *dst, void *src, int32 offset, int32) { MatFX *srcfx = *PLUGINOFFSET(MatFX*, src, offset); if(srcfx == NULL) return dst; MatFX *dstfx = new MatFX; *PLUGINOFFSET(MatFX*, dst, offset) = dstfx; memcpy(dstfx, srcfx, sizeof(MatFX)); for(int i = 0; i < 2; i++) switch(dstfx->fx[i].type){ case MatFX::BUMPMAP: if(dstfx->fx[i].bump.bumpedTex) dstfx->fx[i].bump.bumpedTex->refCount++; if(dstfx->fx[i].bump.tex) dstfx->fx[i].bump.tex->refCount++; break; case MatFX::ENVMAP: if(dstfx->fx[i].env.tex) dstfx->fx[i].env.tex->refCount++; break; case MatFX::DUAL: if(dstfx->fx[i].dual.tex) dstfx->fx[i].dual.tex->refCount++; break; } return dst; } static void readMaterialMatFX(Stream *stream, int32, void *object, int32 offset, int32) { Texture *tex, *bumpedTex; float coefficient; int32 fbAlpha; int32 srcBlend, dstBlend; int32 idx; MatFX *matfx = new MatFX; memset(matfx, 0, sizeof(MatFX)); *PLUGINOFFSET(MatFX*, object, offset) = matfx; matfx->setEffects(stream->readU32()); for(int i = 0; i < 2; i++){ uint32 type = stream->readU32(); switch(type){ case MatFX::BUMPMAP: coefficient = stream->readF32(); bumpedTex = tex = NULL; if(stream->readI32()){ assert(findChunk(stream, ID_TEXTURE, NULL, NULL)); bumpedTex = Texture::streamRead(stream); } if(stream->readI32()){ assert(findChunk(stream, ID_TEXTURE, NULL, NULL)); tex = Texture::streamRead(stream); } idx = matfx->getEffectIndex(type); assert(idx >= 0); matfx->fx[idx].bump.bumpedTex = bumpedTex; matfx->fx[idx].bump.tex = tex; matfx->fx[idx].bump.coefficient = coefficient; break; case MatFX::ENVMAP: coefficient = stream->readF32(); fbAlpha = stream->readI32(); tex = NULL; if(stream->readI32()){ assert(findChunk(stream, ID_TEXTURE, NULL, NULL)); tex = Texture::streamRead(stream); } idx = matfx->getEffectIndex(type); assert(idx >= 0); matfx->fx[idx].env.tex = tex; matfx->fx[idx].env.fbAlpha = fbAlpha; matfx->fx[idx].env.coefficient = coefficient; break; case MatFX::DUAL: srcBlend = stream->readI32(); dstBlend = stream->readI32(); tex = NULL; if(stream->readI32()){ assert(findChunk(stream, ID_TEXTURE, NULL, NULL)); tex = Texture::streamRead(stream); } idx = matfx->getEffectIndex(type); assert(idx >= 0); matfx->fx[idx].dual.tex = tex; matfx->fx[idx].dual.srcBlend = srcBlend; matfx->fx[idx].dual.dstBlend = dstBlend; break; } } } static void writeMaterialMatFX(Stream *stream, int32, void *object, int32 offset, int32) { MatFX *matfx = *PLUGINOFFSET(MatFX*, object, offset); stream->writeU32(matfx->type); for(int i = 0; i < 2; i++){ stream->writeU32(matfx->fx[i].type); switch(matfx->fx[i].type){ case MatFX::BUMPMAP: stream->writeF32(matfx->fx[i].bump.coefficient); stream->writeI32(matfx->fx[i].bump.bumpedTex != NULL); if(matfx->fx[i].bump.bumpedTex) matfx->fx[i].bump.bumpedTex->streamWrite(stream); stream->writeI32(matfx->fx[i].bump.tex != NULL); if(matfx->fx[i].bump.tex) matfx->fx[i].bump.tex->streamWrite(stream); break; case MatFX::ENVMAP: stream->writeF32(matfx->fx[i].env.coefficient); stream->writeI32(matfx->fx[i].env.fbAlpha); stream->writeI32(matfx->fx[i].env.tex != NULL); if(matfx->fx[i].env.tex) matfx->fx[i].env.tex->streamWrite(stream); break; case MatFX::DUAL: stream->writeI32(matfx->fx[i].dual.srcBlend); stream->writeI32(matfx->fx[i].dual.dstBlend); stream->writeI32(matfx->fx[i].dual.tex != NULL); if(matfx->fx[i].dual.tex) matfx->fx[i].dual.tex->streamWrite(stream); break; } } } static int32 getSizeMaterialMatFX(void *object, int32 offset, int32) { MatFX *matfx = *PLUGINOFFSET(MatFX*, object, offset); if(matfx == NULL) return -1; int32 size = 4 + 4 + 4; for(int i = 0; i < 2; i++) switch(matfx->fx[i].type){ case MatFX::BUMPMAP: size += 4 + 4 + 4; if(matfx->fx[i].bump.bumpedTex) size += 12 + matfx->fx[i].bump.bumpedTex->streamGetSize(); if(matfx->fx[i].bump.tex) size += 12 + matfx->fx[i].bump.tex->streamGetSize(); break; case MatFX::ENVMAP: size += 4 + 4 + 4; if(matfx->fx[i].env.tex) size += 12 + matfx->fx[i].env.tex->streamGetSize(); break; case MatFX::DUAL: size += 4 + 4 + 4; if(matfx->fx[i].dual.tex) size += 12 + matfx->fx[i].dual.tex->streamGetSize(); break; } return size; } void MatFX::enableEffects(Atomic *atomic) { *PLUGINOFFSET(int32, atomic, matFXGlobals.atomicOffset) = 1; atomic->pipeline = matFXGlobals.pipelines[rw::platform]; } void registerMatFXPlugin(void) { ObjPipeline *defpipe = new ObjPipeline(PLATFORM_NULL); defpipe->pluginID = 0; //ID_MATFX; defpipe->pluginData = 0; for(uint i = 0; i < nelem(matFXGlobals.pipelines); i++) matFXGlobals.pipelines[i] = defpipe; matFXGlobals.pipelines[PLATFORM_PS2] = ps2::makeMatFXPipeline(); matFXGlobals.pipelines[PLATFORM_OGL] = gl::makeMatFXPipeline(); matFXGlobals.pipelines[PLATFORM_XBOX] = xbox::makeMatFXPipeline(); matFXGlobals.pipelines[PLATFORM_D3D8] = d3d8::makeMatFXPipeline(); matFXGlobals.pipelines[PLATFORM_D3D9] = d3d9::makeMatFXPipeline(); matFXGlobals.atomicOffset = Atomic::registerPlugin(sizeof(int32), ID_MATFX, createAtomicMatFX, NULL, copyAtomicMatFX); Atomic::registerPluginStream(ID_MATFX, readAtomicMatFX, writeAtomicMatFX, getSizeAtomicMatFX); matFXGlobals.materialOffset = Material::registerPlugin(sizeof(MatFX*), ID_MATFX, createMaterialMatFX, destroyMaterialMatFX, copyMaterialMatFX); Material::registerPluginStream(ID_MATFX, readMaterialMatFX, writeMaterialMatFX, getSizeMaterialMatFX); } }