diff options
Diffstat (limited to 'src/frame.cpp')
| -rw-r--r-- | src/frame.cpp | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/src/frame.cpp b/src/frame.cpp new file mode 100644 index 0000000..1832f00 --- /dev/null +++ b/src/frame.cpp @@ -0,0 +1,470 @@ +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "rwbase.h" +#include "rwerror.h" +#include "rwplg.h" +#include "rwpipeline.h" +#include "rwobjects.h" +#include "rwengine.h" + +#define PLUGIN_ID ID_FRAMELIST + +namespace rw { + +int32 Frame::numAllocated; + +PluginList Frame::s_plglist(sizeof(Frame)); +static void *frameOpen(void *object, int32 offset, int32 size) { engine->frameDirtyList.init(); return object; } +static void *frameClose(void *object, int32 offset, int32 size) { return object; } + +void +Frame::registerModule(void) +{ + Engine::registerPlugin(0, ID_FRAMEMODULE, frameOpen, frameClose); +} + +Frame* +Frame::create(void) +{ + Frame *f = (Frame*)rwMalloc(s_plglist.size, MEMDUR_EVENT | ID_FRAMELIST); + if(f == nil){ + RWERROR((ERR_ALLOC, s_plglist.size)); + return nil; + } + numAllocated++; + f->object.init(Frame::ID, 0); + f->objectList.init(); + f->child = nil; + f->next = nil; + f->root = f; + f->matrix.setIdentity(); + f->ltm.setIdentity(); + s_plglist.construct(f); + return f; +} + +Frame* +Frame::cloneHierarchy(void) +{ + Frame *frame = this->cloneAndLink(); + this->purgeClone(); + return frame; +} +void +Frame::destroy(void) +{ + FORLIST(lnk, this->objectList) + ObjectWithFrame::fromFrame(lnk)->setFrame(nil); + s_plglist.destruct(this); + if(this->getParent()) + this->removeChild(); + if(this->object.privateFlags & Frame::HIERARCHYSYNC) + this->inDirtyList.remove(); + for(Frame *f = this->child; f; f = f->next) + f->object.parent = nil; + rwFree(this); + numAllocated--; +} + +void +Frame::destroyHierarchy(void) +{ + Frame *next; + for(Frame *child = this->child; child; child = next){ + next = child->next; + child->destroyHierarchy(); + } + assert(this->objectList.isEmpty()); + s_plglist.destruct(this); + if(this->object.privateFlags & Frame::HIERARCHYSYNC) + this->inDirtyList.remove(); + rwFree(this); +} + +Frame* +Frame::addChild(Frame *child, bool32 append) +{ + Frame *c; + if(child->getParent()) + child->removeChild(); + if(append){ + if(this->child == nil) + this->child = child; + else{ + for(c = this->child; c->next; c = c->next); + c->next = child; + } + child->next = nil; + }else{ + child->next = this->child; + this->child = child; + } + child->object.parent = this; + child->root = this->root; + for(c = child->child; c; c = c->next) + c->setHierarchyRoot(this); + // If the child was a root, remove from dirty list + if(child->object.privateFlags & Frame::HIERARCHYSYNC){ + child->inDirtyList.remove(); + child->object.privateFlags &= ~Frame::HIERARCHYSYNC; + } + this->updateObjects(); + return this; +} + +Frame* +Frame::removeChild(void) +{ + Frame *parent = this->getParent(); + Frame *child = parent->child; + if(child == this) + parent->child = this->next; + else{ + while(child->next != this) + child = child->next; + child->next = this->next; + } + this->object.parent = this->next = nil; + // give the hierarchy a new root + this->setHierarchyRoot(this); + this->updateObjects(); + return this; +} + +Frame* +Frame::forAllChildren(Callback cb, void *data) +{ + Frame *next; + for(Frame *f = this->child; f; f = next){ + next = f->next; + if(cb(f, data) == nil) + return this; + } + return this; +} + +static Frame* +countCB(Frame *f, void *count) +{ + (*(int32*)count)++; + f->forAllChildren(countCB, count); + return f; +} + +int32 +Frame::count(void) +{ + int32 count = 1; + this->forAllChildren(countCB, (void*)&count); + return count; +} + +/* + * Synching is a bit complicated. If anything in the hierarchy is not synched, + * the root of the hierarchy is marked with the HIERARCHYSYNC flags. + * Every unsynched frame is marked with the SUBTREESYNC flags. + * If the LTM is not synched, the LTM flags are set. + * If attached objects need synching, the OBJ flags are set. + */ + +/* Synch just LTM matrices in a hierarchy */ +static void +syncLTMRecurse(Frame *frame, uint8 hierarchyFlags) +{ + for(; frame; frame = frame->next){ + // If frame is dirty or any parent was dirty, update LTM + hierarchyFlags |= frame->object.privateFlags; + if(hierarchyFlags & Frame::SUBTREESYNCLTM){ + Matrix::mult(&frame->ltm, &frame->matrix, + &frame->getParent()->ltm); + frame->object.privateFlags &= ~Frame::SUBTREESYNCLTM; + } + // And synch all children + syncLTMRecurse(frame->child, hierarchyFlags); + } +} + +/* Synch just objects in a hierarchy */ +static void +syncObjRecurse(Frame *frame) +{ + for(; frame; frame = frame->next){ + // Synch attached objects + FORLIST(lnk, frame->objectList) + ObjectWithFrame::fromFrame(lnk)->sync(); + frame->object.privateFlags &= ~Frame::SUBTREESYNCOBJ; + // And synch all children + syncObjRecurse(frame->child); + } +} + +/* Synch LTM and objects */ +static void +syncRecurse(Frame *frame, uint8 hierarchyFlags) +{ + for(; frame; frame = frame->next){ + // If frame is dirty or any parent was dirty, update LTM + hierarchyFlags |= frame->object.privateFlags; + if(hierarchyFlags & Frame::SUBTREESYNCLTM) + Matrix::mult(&frame->ltm, &frame->matrix, + &frame->getParent()->ltm); + // Synch attached objects + FORLIST(lnk, frame->objectList) + ObjectWithFrame::fromFrame(lnk)->sync(); + frame->object.privateFlags &= ~Frame::SUBTREESYNC; + // And synch all children + syncRecurse(frame->child, hierarchyFlags); + } +} + +/* Sync the LTMs of the hierarchy of which 'this' is the root */ +void +Frame::syncHierarchyLTM(void) +{ + // Sync root's LTM + if(this->object.privateFlags & Frame::SUBTREESYNCLTM) + this->ltm = this->matrix; + // ...and children + syncLTMRecurse(this->child, this->object.privateFlags); + // all clean now + this->object.privateFlags &= ~Frame::SYNCLTM; +} + +Matrix* +Frame::getLTM(void) +{ + if(this->root->object.privateFlags & Frame::HIERARCHYSYNCLTM) + this->root->syncHierarchyLTM(); + return &this->ltm; +} + +/* Synch all dirty frames; LTMs and objects */ +void +Frame::syncDirty(void) +{ + Frame *frame; + FORLIST(lnk, engine->frameDirtyList){ + frame = LLLinkGetData(lnk, Frame, inDirtyList); + if(frame->object.privateFlags & Frame::HIERARCHYSYNCLTM){ + // Sync root's LTM + if(frame->object.privateFlags & Frame::SUBTREESYNCLTM) + frame->ltm = frame->matrix; + // Synch attached objects + FORLIST(lnk, frame->objectList) + ObjectWithFrame::fromFrame(lnk)->sync(); + // ...and children + syncRecurse(frame->child, frame->object.privateFlags); + }else{ + // LTMs are clean, just synch objects + FORLIST(lnk, frame->objectList) + ObjectWithFrame::fromFrame(lnk)->sync(); + syncObjRecurse(frame->child); + } + // all clean now + frame->object.privateFlags &= ~(Frame::SYNCLTM | Frame::SYNCOBJ); + } + engine->frameDirtyList.init(); +} + +void +Frame::rotate(const V3d *axis, float32 angle, CombineOp op) +{ + this->matrix.rotate(axis, angle, op); + updateObjects(); +} + +void +Frame::rotate(const Quat *q, CombineOp op) +{ + this->matrix.rotate(*q, op); + updateObjects(); +} + +void +Frame::translate(const V3d *trans, CombineOp op) +{ + this->matrix.translate(trans, op); + updateObjects(); +} + +void +Frame::scale(const V3d *scl, CombineOp op) +{ + this->matrix.scale(scl, op); + updateObjects(); +} + +void +Frame::transform(const Matrix *mat, CombineOp op) +{ + this->matrix.transform(mat, op); + updateObjects(); +} + +void +Frame::updateObjects(void) +{ + // Mark root as dirty and insert into dirty list if necessary + if((this->root->object.privateFlags & HIERARCHYSYNC) == 0) + engine->frameDirtyList.add(&this->root->inDirtyList); + this->root->object.privateFlags |= HIERARCHYSYNC; + // Mark subtree as dirty as well + this->object.privateFlags |= SUBTREESYNC; +} + +void +Frame::setHierarchyRoot(Frame *root) +{ + this->root = root; + for(Frame *child = this->child; child; child = child->next) + child->setHierarchyRoot(root); +} + +static Frame* +cloneRecurse(Frame *old, Frame *newroot) +{ + Frame *frame = Frame::create(); + if(newroot == nil) + newroot = frame; + frame->object.copy(&old->object); + frame->matrix = old->matrix; + frame->root = newroot; + old->root = frame; // Remember cloned frame + for(Frame *child = old->child; child; child = child->next){ + Frame *clonedchild = cloneRecurse(child, newroot); + clonedchild->next = frame->child; + frame->child = clonedchild; + clonedchild->object.parent = frame; + } + Frame::s_plglist.copy(frame, old); + return frame; +} + +// Clone a frame hierarchy. Link cloned frames into Frame::root of the originals. +Frame* +Frame::cloneAndLink(void) +{ + Frame *newhier = cloneRecurse(this, nil); + if(newhier){ + // frame is not in dirty list so important to get this flag right + newhier->object.privateFlags &= ~HIERARCHYSYNC; + newhier->updateObjects(); + } + return newhier; +} + +// Remove links to cloned frames from hierarchy. +void +Frame::purgeClone(void) +{ + Frame *parent = this->getParent(); + this->setHierarchyRoot(parent ? parent->root : this); +} + +struct FrameStreamData +{ + V3d right, up, at, pos; + int32 parent; + int32 matflag; +}; + +FrameList_* +FrameList_::streamRead(Stream *stream) +{ + FrameStreamData buf; + this->numFrames = 0; + this->frames = nil; + if(!findChunk(stream, ID_STRUCT, nil, nil)){ + RWERROR((ERR_CHUNK, "STRUCT")); + return nil; + } + this->numFrames = stream->readI32(); + this->frames = (Frame**)rwMalloc(this->numFrames*sizeof(Frame*), MEMDUR_EVENT | ID_FRAMELIST); + if(this->frames == nil){ + RWERROR((ERR_ALLOC, this->numFrames*sizeof(Frame*))); + return nil; + } + for(int32 i = 0; i < this->numFrames; i++){ + Frame *f; + stream->read32(&buf, sizeof(buf)); + this->frames[i] = f = Frame::create(); + if(f == nil){ + // TODO: clean up frames? + rwFree(this->frames); + return nil; + } + f->matrix.right = buf.right; + f->matrix.up = buf.up; + f->matrix.at = buf.at; + f->matrix.pos = buf.pos; + f->matrix.optimize(); + // RW always removes identity flag + f->matrix.flags &= ~Matrix::IDENTITY; + if(buf.parent >= 0) + this->frames[buf.parent]->addChild(f, rw::streamAppendFrames); + } + for(int32 i = 0; i < this->numFrames; i++) + Frame::s_plglist.streamRead(stream, this->frames[i]); + return this; +} + +void +FrameList_::streamWrite(Stream *stream) +{ + FrameStreamData buf; + + int size = 0, structsize = 0; + structsize = 4 + this->numFrames*sizeof(FrameStreamData); + size += 12 + structsize; + for(int32 i = 0; i < this->numFrames; i++) + size += 12 + Frame::s_plglist.streamGetSize(this->frames[i]); + + writeChunkHeader(stream, ID_FRAMELIST, size); + writeChunkHeader(stream, ID_STRUCT, structsize); + stream->writeU32(this->numFrames); + for(int32 i = 0; i < this->numFrames; i++){ + Frame *f = this->frames[i]; + buf.right = f->matrix.right; + buf.up = f->matrix.up; + buf.at = f->matrix.at; + buf.pos = f->matrix.pos; + buf.parent = findPointer(f->getParent(), (void**)this->frames, + this->numFrames); + buf.matflag = 0; //f->matflag; + stream->write32(&buf, sizeof(buf)); + } + for(int32 i = 0; i < this->numFrames; i++) + Frame::s_plglist.streamWrite(stream, this->frames[i]); +} + +static Frame* +sizeCB(Frame *f, void *size) +{ + *(int32*)size += Frame::s_plglist.streamGetSize(f); + f->forAllChildren(sizeCB, size); + return f; +} + +uint32 +FrameList_::streamGetSize(Frame *f) +{ + int32 numFrames = f->count(); + uint32 size = 12 + 12 + 4 + numFrames*(sizeof(FrameStreamData)+12); + sizeCB(f, (void*)&size); + return size; +} + +Frame** +makeFrameList(Frame *frame, Frame **flist) +{ + *flist++ = frame; + if(frame->next) + flist = makeFrameList(frame->next, flist); + if(frame->child) + flist = makeFrameList(frame->child, flist); + return flist; +} + +} |
