diff --git a/rw.h b/rw.h index af7d6eb..a058c73 100644 --- a/rw.h +++ b/rw.h @@ -9,6 +9,7 @@ #include "src/rwanim.h" #include "src/rwengine.h" #include "src/rwplugins.h" +#include "src/rwuserdata.h" #include "src/ps2/rwps2.h" #include "src/ps2/rwps2plg.h" #include "src/d3d/rwxbox.h" diff --git a/skeleton/glfw.cpp b/skeleton/glfw.cpp index 5690a04..095a77c 100644 --- a/skeleton/glfw.cpp +++ b/skeleton/glfw.cpp @@ -153,6 +153,9 @@ keypress(GLFWwindow *window, int key, int scancode, int action, int mods) int main(int argc, char *argv[]) { + args.argc = argc; + args.argv = argv; + EventHandler(INITIALIZE, nil); engineStartParams.width = sk::globals.width; diff --git a/skeleton/skeleton.cpp b/skeleton/skeleton.cpp index d0bdb55..6fa1096 100644 --- a/skeleton/skeleton.cpp +++ b/skeleton/skeleton.cpp @@ -4,6 +4,7 @@ namespace sk { Globals globals; +Args args; bool InitRW(void) diff --git a/skeleton/skeleton.h b/skeleton/skeleton.h index dc049d7..52d40b5 100644 --- a/skeleton/skeleton.h +++ b/skeleton/skeleton.h @@ -82,6 +82,13 @@ struct Globals }; extern Globals globals; +struct Args +{ + int argc; + char **argv; +}; +extern Args args; + bool InitRW(void); void TerminateRW(void); EventStatus EventHandler(Event e, void *param); diff --git a/skeleton/win.cpp b/skeleton/win.cpp index b8cc034..cd4fd81 100644 --- a/skeleton/win.cpp +++ b/skeleton/win.cpp @@ -204,14 +204,11 @@ int WINAPI WinMain(HINSTANCE instance, HINSTANCE, PSTR cmdLine, int showCmd) { - char *argv[1] = { - "clumpview", - }; AllocConsole(); freopen("CONIN$", "r", stdin); freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); - return main(1, argv); + return main(__argc, __argv); } #endif diff --git a/src/rwbase.h b/src/rwbase.h index 86ab804..eac18d1 100644 --- a/src/rwbase.h +++ b/src/rwbase.h @@ -431,6 +431,7 @@ enum PluginID ID_SKYMIPMAP = MAKEPLUGINID(VEND_CRITERIONTK, 0x10), ID_SKIN = MAKEPLUGINID(VEND_CRITERIONTK, 0x16), ID_HANIMPLUGIN = MAKEPLUGINID(VEND_CRITERIONTK, 0x1E), + ID_USERDATA = MAKEPLUGINID(VEND_CRITERIONTK, 0x1F), ID_MATFX = MAKEPLUGINID(VEND_CRITERIONTK, 0x20), ID_PDS = MAKEPLUGINID(VEND_CRITERIONTK, 0x31), ID_ADC = MAKEPLUGINID(VEND_CRITERIONTK, 0x34), diff --git a/src/rwuserdata.h b/src/rwuserdata.h new file mode 100644 index 0000000..4bf7eb1 --- /dev/null +++ b/src/rwuserdata.h @@ -0,0 +1,83 @@ +namespace rw { + +enum UserDataType +{ + USERDATANA = 0, + USERDATAINT = 1, + USERDATAFLOAT = 2, + USERDATASTRING = 3, +}; + +struct UserDataArray +{ + char *name; + uint32 datatype; + int32 numElements; + void *data; + + int32 getInt(int32 n) { return ((int32*)this->data)[n]; } + float32 getFloat(int32 n) { return ((float32*)this->data)[n]; } + char *getString(int32 n) { return ((char**)this->data)[n]; } + void setInt(int32 n, int32 i) { ((int32*)this->data)[n] = i; } + void setFloat(int32 n, float32 f) { ((float32*)this->data)[n] = f; } + void setString(int32 n, const char *s); + + static int32 geometryAdd(Geometry *g, const char *name, int32 datatype, int32 numElements); + static void geometryRemove(Geometry *g, int32 n); + static int32 geometryGetCount(Geometry *g); + static UserDataArray *geometryGet(Geometry *g, int32 n); + static int32 geometryFindIndex(Geometry *g, const char *name); + + static int32 frameAdd(Frame *f, const char *name, int32 datatype, int32 numElements); + static void frameRemove(Frame *f, int32 n); + static int32 frameGetCount(Frame *f); + static UserDataArray *frameGet(Frame *f, int32 n); + static int32 frameFindIndex(Frame *f, const char *name); + + static int32 cameraAdd(Camera *c, const char *name, int32 datatype, int32 numElements); + static void cameraRemove(Camera *c, int32 n); + static int32 cameraGetCount(Camera *c); + static UserDataArray *cameraGet(Camera *c, int32 n); + static int32 cameraFindIndex(Camera *c, const char *name); + + static int32 lightAdd(Light *l, const char *name, int32 datatype, int32 numElements); + static void lightRemove(Light *l, int32 n); + static int32 lightGetCount(Light *l); + static UserDataArray *lightGet(Light *l, int32 n); + static int32 lightFindIndex(Light *l, const char *name); + + static int32 materialAdd(Material *m, const char *name, int32 datatype, int32 numElements); + static void materialRemove(Material *m, int32 n); + static int32 materialGetCount(Material *m); + static UserDataArray *materialGet(Material *m, int32 n); + static int32 materialFindIndex(Material *m, const char *name); + + static int32 textureAdd(Texture *t, const char *name, int32 datatype, int32 numElements); + static void textureRemove(Texture *t, int32 n); + static int32 textureGetCount(Texture *t); + static UserDataArray *textureGet(Texture *t, int32 n); + static int32 textureFindIndex(Texture *t, const char *name); +}; + +struct UserDataExtension +{ + int32 numArrays; + UserDataArray *arrays; + + // TODO: static accessors +}; + +struct UserDataGlobals +{ + int32 geometryOffset; + int32 frameOffset; + int32 cameraOffset; + int32 lightOffset; + int32 materialOffset; + int32 textureOffset; +}; +extern UserDataGlobals userDataGlobals; + +void registerUserDataPlugin(void); + +} diff --git a/src/userdata.cpp b/src/userdata.cpp new file mode 100644 index 0000000..61b3cf8 --- /dev/null +++ b/src/userdata.cpp @@ -0,0 +1,354 @@ +#include +#include +#include +#include + +#include "rwbase.h" +#include "rwerror.h" +#include "rwplg.h" +#include "rwpipeline.h" +#include "rwobjects.h" +#include "rwengine.h" +#include "rwuserdata.h" + +#define PLUGIN_ID ID_USERDATA + +namespace rw { + +UserDataGlobals userDataGlobals; + +void +UserDataArray::setString(int32 n, const char *s) +{ + int32 len; + char **sp = &((char**)this->data)[n]; + free(*sp); + len = strlen(s)+1; + *sp = (char*)malloc(len); + if(*sp) + strncpy(*sp, s, len); +} + +static void* +createUserData(void *object, int32 offset, int32) +{ + UserDataExtension *ext = PLUGINOFFSET(UserDataExtension, object, offset); + ext->numArrays = 0; + ext->arrays = nil; + return object; +} + +static void* +destroyUserData(void *object, int32 offset, int32) +{ + int32 i, j; + char **strar; + UserDataArray *a; + UserDataExtension *ext = PLUGINOFFSET(UserDataExtension, object, offset); + a = ext->arrays; + for(i = 0; i < ext->numArrays; i++){ + free(a->name); + switch(a->datatype){ + case USERDATASTRING: + strar = (char**)a->data; + for(j = 0; j < a->numElements; j++) + free(strar[j]); + /* fallthrough */ + case USERDATAINT: + case USERDATAFLOAT: + free(a->data); + break; + } + a++; + } + free(ext->arrays); + ext->numArrays = 0; + ext->arrays = nil; + return object; +} + +static void* +copyUserData(void *dst, void *src, int32 offset, int32) +{ + int32 i, j; + char **srcstrar, **dststrar; + UserDataArray *srca, *dsta; + UserDataExtension *srcext = PLUGINOFFSET(UserDataExtension, src, offset); + UserDataExtension *dstext = PLUGINOFFSET(UserDataExtension, dst, offset); + dstext->numArrays = srcext->numArrays; + dstext->arrays = (UserDataArray*)malloc(dstext->numArrays*sizeof(UserDataArray)); + srca = srcext->arrays; + dsta = srcext->arrays; + for(i = 0; i < srcext->numArrays; i++){ + int32 len = strlen(srca->name)+1; + dsta->name = (char*)malloc(len); + strncpy(dsta->name, srca->name, len); + dsta->datatype = srca->datatype; + dsta->numElements = srca->numElements; + switch(srca->datatype){ + case USERDATAINT: + dsta->data = (int32*)malloc(sizeof(int32)*dsta->numElements); + memcpy(dsta->data, srca->data, sizeof(int32)*dsta->numElements); + break; + case USERDATAFLOAT: + dsta->data = (float32*)malloc(sizeof(float32)*dsta->numElements); + memcpy(dsta->data, srca->data, sizeof(float32)*dsta->numElements); + break; + case USERDATASTRING: + dststrar = (char**)malloc(sizeof(char*)*dsta->numElements); + dsta->data = dststrar; + for(j = 0; j < dsta->numElements; j++){ + len = strlen(srcstrar[j])+1; + dststrar[j] = (char*)malloc(len); + strncpy(dststrar[j], srcstrar[j], len); + } + break; + } + srca++; + dsta++; + } + return dst; +} + +static Stream* +readUserData(Stream *stream, int32, void *object, int32 offset, int32) +{ + int32 i, j; + char **strar; + UserDataArray *a; + UserDataExtension *ext = PLUGINOFFSET(UserDataExtension, object, offset); + ext->numArrays = stream->readI32(); + ext->arrays = (UserDataArray*)malloc(ext->numArrays*sizeof(UserDataArray)); + a = ext->arrays; + for(i = 0; i < ext->numArrays; i++){ + int32 len = stream->readI32(); + a->name = (char*)malloc(len); + stream->read(a->name, len); + a->datatype = stream->readU32(); + a->numElements = stream->readI32(); + switch(a->datatype){ + case USERDATAINT: + a->data = (int32*)malloc(sizeof(int32)*a->numElements); + stream->read(a->data, sizeof(int32)*a->numElements); + break; + case USERDATAFLOAT: + a->data = (float32*)malloc(sizeof(float32)*a->numElements); + stream->read(a->data, sizeof(float32)*a->numElements); + break; + case USERDATASTRING: + strar = (char**)malloc(sizeof(char*)*a->numElements); + a->data = strar; + for(j = 0; j < a->numElements; j++){ + len = stream->readI32(); + strar[j] = (char*)malloc(len); + stream->read(strar[j], len); + } + break; + } + a++; + } + return stream; +} + +static Stream* +writeUserData(Stream *stream, int32, void *object, int32 offset, int32) +{ + int32 len; + int32 i, j; + char **strar; + UserDataArray *a; + UserDataExtension *ext = PLUGINOFFSET(UserDataExtension, object, offset); + stream->writeI32(ext->numArrays); + a = ext->arrays; + for(i = 0; i < ext->numArrays; i++){ + len = strlen(a->name)+1; + stream->writeI32(len); + stream->write(a->name, len); + stream->writeU32(a->datatype); + stream->writeI32(a->numElements); + switch(a->datatype){ + case USERDATAINT: + stream->write(a->data, sizeof(int32)*a->numElements); + break; + case USERDATAFLOAT: + stream->write(a->data, sizeof(float32)*a->numElements); + break; + case USERDATASTRING: + strar = (char**)a->data; + for(j = 0; j < a->numElements; j++){ + len = strlen(strar[j])+1; + stream->writeI32(len); + stream->write(strar[j], len); + } + break; + } + a++; + } + return stream; +} + +static int32 +getSizeUserData(void *object, int32 offset, int32) +{ + int32 len; + int32 i, j; + char **strar; + int32 size; + UserDataArray *a; + UserDataExtension *ext = PLUGINOFFSET(UserDataExtension, object, offset); + if(ext->numArrays == 0) + return 0; + size = 4; // numArrays + a = ext->arrays; + for(i = 0; i < ext->numArrays; i++){ + len = strlen(a->name)+1; + size += 4 + len + 4 + 4; // name len, name, type, numElements + switch(a->datatype){ + case USERDATAINT: + size += sizeof(int32)*a->numElements; + break; + case USERDATAFLOAT: + size += sizeof(float32)*a->numElements; + break; + case USERDATASTRING: + strar = (char**)a->data; + for(j = 0; j < a->numElements; j++){ + len = strlen(strar[j])+1; + size += 4 + len; // len and string + } + break; + } + a++; + } + return size; +} + +static int32 +add(UserDataExtension *ext, const char *name, int32 datatype, int32 numElements) +{ + int32 i; + int32 len; + int32 typesz; + UserDataArray *a; + // try to find empty slot + for(i = 0; i < ext->numArrays; i++) + if(ext->arrays[i].datatype == USERDATANA) + goto alloc; + // have to realloc + a = (UserDataArray*)malloc((ext->numArrays+1)*sizeof(UserDataArray)); + if(a == nil) + return -1; + memcpy(a, ext->arrays, ext->numArrays*sizeof(UserDataArray)); + free(ext->arrays); + ext->arrays = a; + i = ext->numArrays++; +alloc: + a = &ext->arrays[i]; + len = strlen(name)+1; + a->name = (char*)malloc(len+1); + assert(a->name); + strncpy(a->name, name, len); + a->datatype = datatype; + a->numElements = numElements; + typesz = datatype == USERDATAINT ? sizeof(int32) : + datatype == USERDATAFLOAT ? sizeof(float32) : + datatype == USERDATASTRING ? sizeof(char*) : 0; + a->data = malloc(typesz*numElements); + assert(a->data); + memset(a->data, 0, typesz*numElements); + return i; +} + +static void +remove(UserDataExtension *ext, int32 n) +{ + int32 i; + UserDataArray *a = &ext->arrays[n]; + if(a->name){ + free(a->name); + a->name = nil; + } + if(a->datatype == USERDATASTRING) + for(i = 0; i < a->numElements; i++) + free(((char**)a->data)[i]); + if(a->data){ + free(a->data); + a->data = nil; + } + a->datatype = USERDATANA; + a->numElements = 0; +} + +#define ACCESSOR(TYPE, NAME) \ +int32 \ +UserDataArray::##NAME##Add(TYPE *t, const char *name, int32 datatype, int32 numElements) \ +{ \ + return add(PLUGINOFFSET(UserDataExtension, t, userDataGlobals.##NAME##Offset), \ + name, datatype, numElements); \ +} \ +void \ +UserDataArray::##NAME##Remove(TYPE *t, int32 n) \ +{ \ + remove(PLUGINOFFSET(UserDataExtension, t, userDataGlobals.##NAME##Offset), n); \ +} \ +int32 \ +UserDataArray::##NAME##GetCount(TYPE *t) \ +{ \ + return PLUGINOFFSET(UserDataExtension, t, userDataGlobals.##NAME##Offset)->numArrays; \ +} \ +UserDataArray* \ +UserDataArray::##NAME##Get(TYPE *t, int32 n) \ +{ \ + if(n >= PLUGINOFFSET(UserDataExtension, t, userDataGlobals.##NAME##Offset)->numArrays) \ + return nil; \ + return &PLUGINOFFSET(UserDataExtension, t, userDataGlobals.##NAME##Offset)->arrays[n]; \ +} \ +int32 \ +UserDataArray::##NAME##FindIndex(TYPE *t, const char *name) \ +{ \ + int32 i; \ + UserDataExtension *ext = PLUGINOFFSET(UserDataExtension, t, userDataGlobals.##NAME##Offset); \ + for(i = 0; i < ext->numArrays; i++) \ + if(strcmp(ext->arrays[i].name, name) == 0) \ + return i; \ + return -1; \ +} + +ACCESSOR(Geometry, geometry) +ACCESSOR(Frame, frame) +ACCESSOR(Camera, camera) +ACCESSOR(Light, light) +ACCESSOR(Material, material) +ACCESSOR(Texture, texture) + +void +registerUserDataPlugin(void) +{ + // TODO: World Sector + + userDataGlobals.geometryOffset = Geometry::registerPlugin(sizeof(UserDataExtension), + ID_USERDATA, createUserData, destroyUserData, copyUserData); + Geometry::registerPluginStream(ID_USERDATA, readUserData, writeUserData, getSizeUserData); + + userDataGlobals.frameOffset = Frame::registerPlugin(sizeof(UserDataExtension), + ID_USERDATA, createUserData, destroyUserData, copyUserData); + Frame::registerPluginStream(ID_USERDATA, readUserData, writeUserData, getSizeUserData); + + userDataGlobals.cameraOffset = Camera::registerPlugin(sizeof(UserDataExtension), + ID_USERDATA, createUserData, destroyUserData, copyUserData); + Camera::registerPluginStream(ID_USERDATA, readUserData, writeUserData, getSizeUserData); + + userDataGlobals.lightOffset = Light::registerPlugin(sizeof(UserDataExtension), + ID_USERDATA, createUserData, destroyUserData, copyUserData); + Light::registerPluginStream(ID_USERDATA, readUserData, writeUserData, getSizeUserData); + + userDataGlobals.materialOffset = Material::registerPlugin(sizeof(UserDataExtension), + ID_USERDATA, createUserData, destroyUserData, copyUserData); + Material::registerPluginStream(ID_USERDATA, readUserData, writeUserData, getSizeUserData); + + userDataGlobals.textureOffset = Texture::registerPlugin(sizeof(UserDataExtension), + ID_USERDATA, createUserData, destroyUserData, copyUserData); + Texture::registerPluginStream(ID_USERDATA, readUserData, writeUserData, getSizeUserData); +} + +} diff --git a/tools/clumpview/main.cpp b/tools/clumpview/main.cpp index 124534d..37eae49 100644 --- a/tools/clumpview/main.cpp +++ b/tools/clumpview/main.cpp @@ -30,6 +30,7 @@ attachPlugins(void) rw::registerMaterialRightsPlugin(); rw::xbox::registerVertexFormatPlugin(); rw::registerSkinPlugin(); + rw::registerUserDataPlugin(); rw::registerHAnimPlugin(); rw::registerMatFXPlugin(); rw::registerUVAnimPlugin(); @@ -37,6 +38,113 @@ attachPlugins(void) return true; } +void +dumpUserData(rw::UserDataArray *ar) +{ + int i; + printf("name: %s\n", ar->name); + for(i = 0; i < ar->numElements; i++){ + switch(ar->datatype){ + case rw::USERDATAINT: + printf(" %d\n", ar->getInt(i)); + break; + case rw::USERDATAFLOAT: + printf(" %f\n", ar->getFloat(i)); + break; + case rw::USERDATASTRING: + printf(" %s\n", ar->getString(i)); + break; + } + } +} + +static rw::Frame* +dumpFrameUserDataCB(rw::Frame *f, void*) +{ + using namespace rw; + int32 i; + UserDataArray *ar; + int32 n = UserDataArray::frameGetCount(f); + for(i = 0; i < n; i++){ + ar = UserDataArray::frameGet(f, i); + dumpUserData(ar); + } + f->forAllChildren(dumpFrameUserDataCB, nil); + return f; +} + +void +dumpUserData(rw::Clump *clump) +{ + printf("Frames\n"); + dumpFrameUserDataCB(clump->getFrame(), nil); +} + +static rw::Frame* +getHierCB(rw::Frame *f, void *data) +{ + using namespace rw; + HAnimData *hd = rw::HAnimData::get(f); + if(hd->hierarchy){ + *(HAnimHierarchy**)data = hd->hierarchy; + return nil; + } + f->forAllChildren(getHierCB, data); + return f; +} + +rw::HAnimHierarchy* +getHAnimHierarchyFromClump(rw::Clump *clump) +{ + using namespace rw; + HAnimHierarchy *hier = nil; + getHierCB(clump->getFrame(), &hier); + return hier; +} + +void +setupAtomic(rw::Atomic *atomic) +{ + using namespace rw; + // just remove pipelines that we can't handle for now +// if(atomic->pipeline && atomic->pipeline->platform != rw::platform) + atomic->pipeline = NULL; + + // Attach hierarchy to atomic if we're skinned + HAnimHierarchy *hier = getHAnimHierarchyFromClump(atomic->clump); + if(hier) + Skin::setHierarchy(atomic, hier); +} + +static void +initHierFromFrames(rw::HAnimHierarchy *hier) +{ + using namespace rw; + int32 i; + for(i = 0; i < hier->numNodes; i++){ + if(hier->nodeInfo[i].frame){ + hier->matrices[hier->nodeInfo[i].index] = *hier->nodeInfo[i].frame->getLTM(); + }else + assert(0); + } +} + +void +setupClump(rw::Clump *clump) +{ + using namespace rw; + HAnimHierarchy *hier = getHAnimHierarchyFromClump(clump); + if(hier){ + hier->attach(); + initHierFromFrames(hier); + } + + FORLIST(lnk, clump->atomics){ + rw::Atomic *a = rw::Atomic::fromClump(lnk); + setupAtomic(a); + } +} + bool InitRW(void) { @@ -45,6 +153,8 @@ InitRW(void) return false; char *filename = "teapot.dff"; + if(sk::args.argc > 1) + filename = sk::args.argv[1]; rw::StreamFile in; if(in.open(filename, "rb") == NULL){ printf("couldn't open file\n"); @@ -57,12 +167,8 @@ InitRW(void) clump->getFrame()->translate(&zero, rw::COMBINEREPLACE); - FORLIST(lnk, clump->atomics){ - rw::Atomic *a = rw::Atomic::fromClump(lnk); - if(a->pipeline && a->pipeline->platform != rw::platform) - a->pipeline = NULL; - } - + dumpUserData(clump); + setupClump(clump); world = rw::World::create();