librw/src/plugins.cpp

1079 lines
28 KiB
C++
Raw Normal View History

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <new>
#include "rwbase.h"
#include "rwplugin.h"
2015-07-11 23:48:11 +02:00
#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 {
2015-01-17 15:15:03 +01:00
//
// HAnim
//
int32 hAnimOffset;
2015-01-17 15:15:03 +01:00
static void*
createHAnim(void *object, int32 offset, int32)
2015-01-17 15:15:03 +01:00
{
HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset);
hanim->id = -1;
hanim->hierarchy = NULL;
return object;
}
static void*
destroyHAnim(void *object, int32 offset, int32)
2015-01-17 15:15:03 +01:00
{
HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset);
if(hanim->hierarchy){
HAnimHierarchy *hier = hanim->hierarchy;
delete[] (uint8*)hier->matricesUnaligned;
delete[] hier->nodeInfo;
delete hier;
}
hanim->id = -1;
hanim->hierarchy = NULL;
return object;
}
static void*
copyHAnim(void *dst, void *src, int32 offset, int32)
2015-01-17 15:15:03 +01:00
{
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)
2015-01-17 15:15:03 +01:00
{
2015-08-11 21:48:23 +02:00
int32 ver, numNodes;
2015-01-17 15:15:03 +01:00
HAnimData *hanim = PLUGINOFFSET(HAnimData, object, offset);
2015-08-11 20:57:43 +02:00
ver = stream->readI32();
assert(ver == 0x100);
2015-01-17 15:15:03 +01:00
hanim->id = stream->readI32();
numNodes = stream->readI32();
if(numNodes != 0){
HAnimHierarchy *hier = new HAnimHierarchy;
hanim->hierarchy = hier;
hier->numNodes = numNodes;
hier->flags = stream->readI32();
hier->maxInterpKeyFrameSize = stream->readI32();
hier->parentFrame = (Frame*)object;
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 = stream->readI32();
hier->nodeInfo[i].index = stream->readI32();
hier->nodeInfo[i].flags = stream->readI32();
hier->nodeInfo[i].frame = NULL;
}
}
}
static void
writeHAnim(Stream *stream, int32, void *object, int32 offset, int32)
2015-01-17 15:15:03 +01:00
{
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);
// TODO: version correct?
if(version >= 0x35000 && hanim->id == -1 && hanim->hierarchy == NULL)
2015-08-11 21:48:23 +02:00
return -1;
2015-01-17 15:15:03 +01:00
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);
}
2015-01-17 15:15:03 +01:00
void
registerHAnimPlugin(void)
2015-01-17 15:15:03 +01:00
{
hAnimOffset = Frame::registerPlugin(sizeof(HAnimData), ID_HANIMPLUGIN,
2015-01-17 15:15:03 +01:00
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);
2015-01-17 15:15:03 +01:00
}
//
// 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;
2015-09-07 23:00:28 +02:00
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){
2015-09-07 23:00:28 +02:00
mesh->indices = p;
p += mesh->numIndices;
stream->read(mesh->indices,
mesh->numIndices*2);
}
}else{
2015-09-07 23:00:28 +02:00
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);
}
2015-12-20 13:05:32 +01:00
MeshHeader::~MeshHeader(void)
{
// first mesh holds pointer to all indices
delete[] this->mesh[0].indices;
delete[] this->mesh;
}
2015-09-07 23:00:28 +02:00
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;
2015-09-19 19:28:23 +02:00
mesh++;
2015-09-07 23:00:28 +02:00
}
}
// 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);
}
2015-01-17 15:15:03 +01:00
//
// Skin
2015-01-17 15:15:03 +01:00
//
2015-08-16 16:44:59 +02:00
SkinGlobals skinGlobals = { 0, { NULL } };
2015-01-10 22:13:27 +01:00
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;
2015-01-09 20:17:32 +01:00
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);
2015-08-11 20:57:43 +02:00
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);
2015-09-08 10:29:24 +02:00
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];
2016-01-10 18:18:03 +01:00
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);
}
// 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);
2015-09-08 10:29:24 +02:00
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);
2015-09-08 10:29:24 +02:00
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;
}
2015-01-10 22:13:27 +01:00
static void
2015-07-11 23:48:11 +02:00
skinRights(void *object, int32, int32, uint32)
2015-01-10 22:13:27 +01:00
{
2015-09-19 19:28:23 +02:00
((Atomic*)object)->pipeline = skinGlobals.pipelines[rw::platform];
2015-01-10 22:13:27 +01:00
}
void
registerSkinPlugin(void)
{
2015-08-02 19:31:01 +02:00
ObjPipeline *defpipe = new ObjPipeline(PLATFORM_NULL);
2015-07-11 23:48:11 +02:00
defpipe->pluginID = ID_SKIN;
defpipe->pluginData = 1;
2015-12-14 18:52:50 +01:00
for(uint i = 0; i < nelem(skinGlobals.pipelines); i++)
2015-07-11 23:48:11 +02:00
skinGlobals.pipelines[i] = defpipe;
2015-09-19 19:28:23 +02:00
skinGlobals.pipelines[PLATFORM_PS2] =
2015-07-12 22:57:05 +02:00
ps2::makeSkinPipeline();
2015-09-19 19:28:23 +02:00
skinGlobals.pipelines[PLATFORM_OGL] =
2015-08-03 18:30:10 +02:00
gl::makeSkinPipeline();
2015-09-19 19:28:23 +02:00
skinGlobals.pipelines[PLATFORM_XBOX] =
xbox::makeSkinPipeline();
2015-09-19 19:28:23 +02:00
skinGlobals.pipelines[PLATFORM_D3D8] =
d3d8::makeSkinPipeline();
2015-09-19 19:28:23 +02:00
skinGlobals.pipelines[PLATFORM_D3D9] =
d3d9::makeSkinPipeline();
2015-01-10 22:13:27 +01:00
skinGlobals.offset = Geometry::registerPlugin(sizeof(Skin*), ID_SKIN,
createSkin,
destroySkin,
copySkin);
Geometry::registerPluginStream(ID_SKIN,
2015-01-09 20:17:32 +01:00
readSkin, writeSkin, getSizeSkin);
Atomic::registerPlugin(0, ID_SKIN, NULL, NULL, NULL);
2015-01-10 22:13:27 +01:00
Atomic::setStreamRightsCallback(ID_SKIN, skinRights);
2015-01-09 20:17:32 +01:00
}
2015-08-11 20:57:43 +02:00
void
Skin::init(int32 numBones, int32 numUsedBones, int32 numVertices)
2015-08-11 20:57:43 +02:00
{
this->numBones = numBones;
this->numUsedBones = numUsedBones;
2015-08-11 20:57:43 +02:00
uint32 size = this->numUsedBones +
this->numBones*64 +
numVertices*(16+4) + 0xF;
2015-08-11 20:57:43 +02:00
this->data = new uint8[size];
uint8 *p = this->data;
2015-08-11 20:57:43 +02:00
this->usedBones = NULL;
if(this->numUsedBones){
this->usedBones = p;
p += this->numUsedBones;
2015-08-11 20:57:43 +02:00
}
p = (uint8*)(((uintptr)p + 0xF) & ~0xF);
2015-08-11 20:57:43 +02:00
this->inverseMatrices = NULL;
if(this->numBones){
this->inverseMatrices = (float*)p;
p += 64*this->numBones;
2015-08-11 20:57:43 +02:00
}
this->indices = NULL;
if(numVertices){
this->indices = p;
p += 4*numVertices;
2015-08-11 20:57:43 +02:00
}
this->weights = NULL;
if(numVertices)
this->weights = (float*)p;
2015-08-11 20:57:43 +02:00
this->platformData = NULL;
2015-08-11 20:57:43 +02:00
}
void
Skin::findNumWeights(int32 numVertices)
2015-08-11 20:57:43 +02:00
{
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;
2015-08-11 20:57:43 +02:00
}
2015-01-17 15:15:03 +01:00
//
// MatFX
//
// Atomic
2015-01-09 20:17:32 +01:00
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)
{
*PLUGINOFFSET(int32, dst, offset) = *PLUGINOFFSET(int32, src, offset);
return dst;
}
static void
readAtomicMatFX(Stream *stream, int32, void *object, int32 offset, int32)
{
int32 flag;
stream->read(&flag, 4);
2015-08-01 23:03:10 +02:00
// printf("matfx: %d\n", flag);
2015-01-09 20:17:32 +01:00
*PLUGINOFFSET(int32, object, offset) = flag;
2015-01-10 22:13:27 +01:00
if(flag)
2015-07-11 23:48:11 +02:00
((Atomic*)object)->pipeline =
2015-09-19 19:28:23 +02:00
matFXGlobals.pipelines[rw::platform];
2015-01-09 20:17:32 +01:00
}
static void
writeAtomicMatFX(Stream *stream, int32, void *object, int32 offset, int32)
{
int32 flag;
flag = *PLUGINOFFSET(int32, object, offset);
stream->writeI32(flag);
}
static int32
getSizeAtomicMatFX(void *object, int32 offset, int32)
{
int32 flag = *PLUGINOFFSET(int32, object, offset);
// TODO: not sure which version
return flag || rw::version < 0x35000 ? 4 : -1;
2015-01-09 20:17:32 +01:00
}
2015-01-17 15:15:03 +01:00
// Material
2015-01-09 20:17:32 +01:00
2015-08-16 16:44:59 +02:00
MatFXGlobals matFXGlobals = { 0, 0, { NULL } };
2015-01-10 22:13:27 +01:00
2015-01-09 20:17:32 +01:00
// 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();
2015-01-09 20:17:32 +01:00
if(matfx->fx[i].bump.tex)
matfx->fx[i].bump.tex->destroy();
2015-01-09 20:17:32 +01:00
break;
case MatFX::ENVMAP:
if(matfx->fx[i].env.tex)
matfx->fx[i].env.tex->destroy();
2015-01-09 20:17:32 +01:00
break;
case MatFX::DUAL:
if(matfx->fx[i].dual.tex)
matfx->fx[i].dual.tex->destroy();
2015-01-09 20:17:32 +01:00
break;
}
memset(matfx, 0, sizeof(MatFX));
}
void
2016-01-09 22:01:21 +01:00
MatFX::setEffects(uint32 type)
2015-01-09 20:17:32 +01:00
{
2016-01-09 22:01:21 +01:00
if(this->type != 0 && this->type != type)
2015-01-09 20:17:32 +01:00
clearMatFX(this);
2016-01-09 22:01:21 +01:00
this->type = type;
switch(type){
2015-01-09 20:17:32 +01:00
case BUMPMAP:
case ENVMAP:
case DUAL:
case UVTRANSFORM:
2016-01-09 22:01:21 +01:00
this->fx[0].type = type;
2015-01-09 20:17:32 +01:00
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;
}
}
int32
MatFX::getEffectIndex(uint32 type)
{
for(int i = 0; i < 2; i++)
if(this->fx[i].type == type)
return i;
return -1;
}
2016-01-10 18:18:03 +01:00
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;
}
2015-01-09 20:17:32 +01:00
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());
2016-01-10 18:18:03 +01:00
for(int i = 0; i < 2; i++){
2015-01-09 20:17:32 +01:00
uint32 type = stream->readU32();
switch(type){
case MatFX::BUMPMAP:
coefficient = stream->readF32();
bumpedTex = tex = NULL;
if(stream->readI32()){
assert(findChunk(stream, ID_TEXTURE,
2015-01-09 20:17:32 +01:00
NULL, NULL));
bumpedTex = Texture::streamRead(stream);
}
if(stream->readI32()){
assert(findChunk(stream, ID_TEXTURE,
2015-01-09 20:17:32 +01:00
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();
2016-01-10 18:18:03 +01:00
fbAlpha = stream->readI32();
2015-01-09 20:17:32 +01:00
tex = NULL;
if(stream->readI32()){
assert(findChunk(stream, ID_TEXTURE,
2015-01-09 20:17:32 +01:00
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,
2015-01-09 20:17:32 +01:00
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);
2016-01-09 22:01:21 +01:00
stream->writeU32(matfx->type);
2015-01-09 20:17:32 +01:00
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
registerMatFXPlugin(void)
2015-01-09 20:17:32 +01:00
{
2015-08-02 19:31:01 +02:00
ObjPipeline *defpipe = new ObjPipeline(PLATFORM_NULL);
2015-07-11 23:48:11 +02:00
defpipe->pluginID = ID_MATFX;
defpipe->pluginData = 0;
for(uint i = 0; i < nelem(matFXGlobals.pipelines); i++)
matFXGlobals.pipelines[i] = defpipe;
2015-09-19 19:28:23 +02:00
matFXGlobals.pipelines[PLATFORM_PS2] =
2015-07-12 22:57:05 +02:00
ps2::makeMatFXPipeline();
2015-09-19 19:28:23 +02:00
matFXGlobals.pipelines[PLATFORM_OGL] =
2015-08-03 18:30:10 +02:00
gl::makeMatFXPipeline();
2015-09-19 19:28:23 +02:00
matFXGlobals.pipelines[PLATFORM_XBOX] =
xbox::makeMatFXPipeline();
2015-09-19 19:28:23 +02:00
matFXGlobals.pipelines[PLATFORM_D3D8] =
d3d8::makeMatFXPipeline();
2015-09-19 19:28:23 +02:00
matFXGlobals.pipelines[PLATFORM_D3D9] =
d3d9::makeMatFXPipeline();
2015-01-10 22:13:27 +01:00
matFXGlobals.atomicOffset =
2015-01-09 20:17:32 +01:00
Atomic::registerPlugin(sizeof(int32), ID_MATFX,
createAtomicMatFX, NULL, copyAtomicMatFX);
Atomic::registerPluginStream(ID_MATFX,
readAtomicMatFX,
writeAtomicMatFX,
getSizeAtomicMatFX);
2015-01-10 22:13:27 +01:00
matFXGlobals.materialOffset =
2015-01-09 20:17:32 +01:00
Material::registerPlugin(sizeof(MatFX*), ID_MATFX,
createMaterialMatFX, destroyMaterialMatFX,
copyMaterialMatFX);
Material::registerPluginStream(ID_MATFX,
readMaterialMatFX,
writeMaterialMatFX,
getSizeMaterialMatFX);
}
}