diff --git a/src/base.cpp b/src/base.cpp index 0ef9408..5368358 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -153,6 +153,33 @@ Matrix::invert(Matrix *m1, Matrix *m2) return matrixInvert((float32*)m1, (float32*)m2); } +void +Matrix::invertOrthonormal(Matrix *m1, Matrix *m2) +{ + m1->right.x = m2->right.x; + m1->right.y = m2->up.x; + m1->right.z = m2->at.x; + m1->up.x = m2->right.y; + m1->up.y = m2->up.y; + m1->up.z = m2->at.y; + m1->at.x = m2->right.z; + m1->at.y = m2->up.z; + m1->at.z = m2->at.z; + m1->pos.x = -(m2->pos.x*m2->right.x + + m2->pos.y*m2->right.y + + m2->pos.z*m2->right.z); + m1->pos.y = -(m2->pos.x*m2->up.x + + m2->pos.y*m2->up.y + + m2->pos.z*m2->up.z); + m1->pos.z = -(m2->pos.x*m2->at.x + + m2->pos.y*m2->at.y + + m2->pos.z*m2->at.z); + m1->rightw = 0.0f; + m1->upw = 0.0f; + m1->atw = 0.0f; + m1->posw = 1.0f; +} + void Matrix::transpose(Matrix *m1, Matrix *m2) { diff --git a/src/camera.cpp b/src/camera.cpp index 2f4b08a..a8bdbb4 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -17,28 +17,86 @@ defaultBeginUpdateCB(Camera *cam) { engine->currentCamera = cam; Frame::syncDirty(); - DRIVER->beginUpdate(cam); + engine->beginUpdate(cam); } void defaultEndUpdateCB(Camera *cam) { - DRIVER->endUpdate(cam); + engine->endUpdate(cam); } static void -cameraSync(ObjectWithFrame*) +cameraSync(ObjectWithFrame *obj) { - // TODO: calculate view matrix here (i.e. projection * inverseLTM) - // RW projection matrix looks like this: - // (cf. Camera View Matrix white paper) - // w = viewWindow width - // h = viewWindow height - // o = view offset - // - // 1/2w 0 ox/2w + 1/2 -ox/2w - // 0 -1/2h -oy/2h + 1/2 oy/2h - // 0 0 1 0 + /* + * RW projection matrix looks like this: + * (cf. Camera View Matrix white paper) + * w = viewWindow width + * h = viewWindow height + * o = view offset + * + * perspective: + * 1/2w 0 ox/2w + 1/2 -ox/2w + * 0 -1/2h -oy/2h + 1/2 oy/2h + * 0 0 1 0 + * 0 0 1 0 + * + * parallel: + * 1/2w 0 ox/2w -ox/2w + 1/2 + * 0 -1/2h -oy/2h oy/2h + 1/2 + * 0 0 1 0 + * 0 0 0 1 + * + * The view matrix transforms from world to clip space, it is however + * not used for OpenGL or D3D since transformation to camera space + * and to clip space are handled by separate matrices there. + * On these platforms the two matrices are built in the platform's + * beginUpdate function. + * On the PS2 the 1/2 translation/shear is removed again on the VU1. + * + * RW builds this matrix directly without using explicit + * inversion and matrix multiplication. + */ + + Camera *cam = (Camera*)obj; + Matrix inv, proj; + Matrix::invertOrthonormal(&inv, cam->getFrame()->getLTM()); + float32 xscl = 2.0f/cam->viewWindow.x; + float32 yscl = 2.0f/cam->viewWindow.y; + + proj.right.x = xscl; + proj.right.y = 0.0f; + proj.right.z = 0.0f; + proj.rightw = 0.0f; + + proj.up.x = 0.0f; + proj.up.y = -yscl; + proj.up.z = 0.0f; + proj.upw = 0.0f; + + if(cam->projection == Camera::PERSPECTIVE){ + proj.pos.x = -cam->viewOffset.x*xscl; + proj.pos.y = cam->viewOffset.y*yscl; + proj.pos.z = 0.0f; + proj.posw = 0.0f; + + proj.at.x = -proj.pos.x + 0.5f; + proj.at.y = -proj.pos.y + 0.5f; + proj.at.z = 1.0f; + proj.atw = 1.0f; + }else{ + proj.at.x = cam->viewOffset.x*xscl; + proj.at.y = -cam->viewOffset.y*yscl; + proj.at.z = 1.0f; + proj.atw = 0.0f; + + proj.pos.x = -proj.at.x + 0.5f; + proj.pos.y = -proj.at.y + 0.5f; + proj.pos.z = 0.0f; + proj.posw = 1.0f; + } + Matrix::mult(&cam->viewMatrix, &proj, &inv); } void @@ -127,7 +185,37 @@ Camera::destroy(void) void Camera::clear(RGBA *col, uint32 mode) { - DRIVER->clearCamera(this, col, mode); + engine->clearCamera(this, col, mode); +} + +void +calczShiftScale(Camera *cam) +{ + float32 n = cam->nearPlane; + float32 f = cam->farPlane; + float32 N = engine->zNear; + float32 F = engine->zFar; + if(cam->projection == Camera::PERSPECTIVE){ + cam->zScale = (N - F)*n*f/(f - n); + cam->zShift = (F*f - N*n)/(f - n); + }else{ + cam->zScale = (F - N)/(f -n); + cam->zShift = (N*f - F*n)/(f - n); + } +} + +void +Camera::setNearPlane(float32 near) +{ + this->nearPlane = near; + calczShiftScale(this); +} + +void +Camera::setFarPlane(float32 far) +{ + this->farPlane = far; + calczShiftScale(this); } struct CameraChunkData diff --git a/src/d3d/d3ddriver.cpp b/src/d3d/d3ddriver.cpp index 1f714c8..51938fd 100644 --- a/src/d3d/d3ddriver.cpp +++ b/src/d3d/d3ddriver.cpp @@ -184,8 +184,7 @@ beginUpdate(Camera *cam) // View Matrix Matrix inv; - // TODO: this can be simplified, or we use matrix flags.... - Matrix::invert(&inv, cam->getFrame()->getLTM()); + Matrix::invertOrthonormal(&inv, cam->getFrame()->getLTM()); // Since we're looking into positive Z, // flip X to ge a left handed view space. view[0] = -inv.right.x; @@ -207,11 +206,10 @@ beginUpdate(Camera *cam) device->SetTransform(D3DTS_VIEW, (D3DMATRIX*)view); // Projection Matrix - float32 invwx = 1.0f/this->viewWindow.x; - float32 invwy = 1.0f/this->viewWindow.y; - float32 invz = 1.0f/(this->farPlane-this->nearPlane); + float32 invwx = 1.0f/cam->viewWindow.x; + float32 invwy = 1.0f/cam->viewWindow.y; + float32 invz = 1.0f/(cam->farPlane-cam->nearPlane); - // is this all really correct? RW code looks a bit different... proj[0] = invwx; proj[1] = 0.0f; proj[2] = 0.0f; @@ -222,27 +220,22 @@ beginUpdate(Camera *cam) proj[6] = 0.0f; proj[7] = 0.0f; - if(this->projection == PERSPECTIVE){ - proj[8] = this->viewOffset.x*invwx; - proj[9] = this->viewOffset.y*invwy; - proj[10] = this->farPlane*invz; + proj[8] = cam->viewOffset.x*invwx; + proj[9] = cam->viewOffset.y*invwy; + proj[12] = -proj[8]; + proj[13] = -proj[9]; + if(cam->projection == PERSPECTIVE){ + proj[10] = cam->farPlane*invz; proj[11] = 1.0f; - proj[12] = 0.0f; - proj[13] = 0.0f; - proj[14] = -this->nearPlane*proj[10]; proj[15] = 0.0f; }else{ - proj[8] = 0.0f; - proj[9] = 0.0f; proj[10] = invz; proj[11] = 0.0f; - proj[12] = this->viewOffset.x*invwx; - proj[13] = this->viewOffset.y*invwy; - proj[14] = -this->nearPlane*proj[10]; proj[15] = 1.0f; } + proj[14] = -cam->nearPlane*proj[10]; device->SetTransform(D3DTS_PROJECTION, (D3DMATRIX*)proj); } diff --git a/src/engine.cpp b/src/engine.cpp index 1bcfe6b..2b4fde3 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -30,6 +30,14 @@ Engine::init(void) Frame::dirtyList.init(); + engine->beginUpdate = null::beginUpdate; + engine->endUpdate = null::endUpdate; + engine->clearCamera = null::clearCamera; + engine->setRenderState = null::setRenderState; + engine->getRenderState = null::getRenderState; + engine->zNear = 0.0f; // random values + engine->zFar = 1.0f; + ps2::initializePlatform(); xbox::initializePlatform(); d3d8::initializePlatform(); @@ -50,12 +58,6 @@ Driver::open(void) driver[i]->defaultPipeline = defpipe; - driver[i]->beginUpdate = null::beginUpdate; - driver[i]->endUpdate = null::endUpdate; - - driver[i]->setRenderState = null::setRenderState; - driver[i]->getRenderState = null::getRenderState; - driver[i]->rasterCreate = null::rasterCreate; driver[i]->rasterLock = null::rasterLock; driver[i]->rasterUnlock = null::rasterUnlock; diff --git a/src/gl/gl3driver.cpp b/src/gl/gl3driver.cpp index 1744a96..94a2ea8 100644 --- a/src/gl/gl3driver.cpp +++ b/src/gl/gl3driver.cpp @@ -344,8 +344,8 @@ beginUpdate(Camera *cam) float view[16], proj[16]; // View Matrix Matrix inv; - // TODO: this can be simplified, or we use matrix flags.... - Matrix::invert(&inv, cam->getFrame()->getLTM()); + // TODO: maybe use matrix flags.... + Matrix::invertOrthonormal(&inv, cam->getFrame()->getLTM()); // Since we're looking into positive Z, // flip X to ge a left handed view space. view[0] = -inv.right.x; @@ -381,18 +381,22 @@ beginUpdate(Camera *cam) proj[6] = 0.0f; proj[7] = 0.0f; + proj[8] = cam->viewOffset.x*invwx; + proj[9] = cam->viewOffset.y*invwy; + proj[12] = -proj[8]; + proj[13] = -proj[9]; if(cam->projection == Camera::PERSPECTIVE){ - proj[8] = cam->viewOffset.x*invwx; - proj[9] = cam->viewOffset.y*invwy; proj[10] = (cam->farPlane+cam->nearPlane)*invz; proj[11] = 1.0f; - proj[12] = 0.0f; - proj[13] = 0.0f; proj[14] = -2.0f*cam->nearPlane*cam->farPlane*invz; proj[15] = 0.0f; }else{ - // TODO + proj[10] = -(cam->farPlane+cam->nearPlane)*invz; + proj[11] = 0.0f; + + proj[14] = -2.0f*invz; + proj[15] = 1.0f; } setProjectionMatrix(proj); @@ -409,10 +413,12 @@ beginUpdate(Camera *cam) void initializeRender(void) { - driver[PLATFORM_GL3]->beginUpdate = beginUpdate; - driver[PLATFORM_GL3]->clearCamera = clearCamera; - driver[PLATFORM_GL3]->setRenderState = setRenderState; - driver[PLATFORM_GL3]->getRenderState = getRenderState; + engine->beginUpdate = beginUpdate; + engine->clearCamera = clearCamera; + engine->setRenderState = setRenderState; + engine->getRenderState = getRenderState; + engine->zNear = -1.0f; + engine->zFar = 1.0f; simpleShader = Shader::fromFiles("simple.vert", "simple.frag"); diff --git a/src/rwbase.h b/src/rwbase.h index 52c79e3..106de32 100644 --- a/src/rwbase.h +++ b/src/rwbase.h @@ -156,6 +156,7 @@ struct Matrix // not very pretty :/ static void mult(Matrix *m1, Matrix *m2, Matrix *m3); static bool32 invert(Matrix *m1, Matrix *m2); + static void invertOrthonormal(Matrix *m1, Matrix *m2); static void transpose(Matrix *m1, Matrix *m2); }; diff --git a/src/rwengine.h b/src/rwengine.h index 7b51613..7430831 100644 --- a/src/rwengine.h +++ b/src/rwengine.h @@ -43,31 +43,34 @@ enum BlendFunction // TODO: add more perhaps }; -// This is for platform independent things +// This is for platform independent things and the render device (of which +// there can only ever be one). // TODO: move more stuff into this struct Engine { void *currentCamera; void *currentWorld; + // Device + float32 zNear, zFar; + void (*beginUpdate)(Camera*); + void (*endUpdate)(Camera*); + void (*clearCamera)(Camera*, RGBA *col, uint32 mode); + void (*setRenderState)(int32 state, uint32 value); + uint32 (*getRenderState)(int32 state); + static void init(void); }; extern Engine *engine; -// This is for platform driver implementations +// This is for platform driver implementations which have to be available +// regardless of the render device. struct Driver { ObjPipeline *defaultPipeline; int32 rasterNativeOffset; - void (*beginUpdate)(Camera*); - void (*endUpdate)(Camera*); - void (*clearCamera)(Camera*, RGBA *col, uint32 mode); - - void (*setRenderState)(int32 state, uint32 value); - uint32 (*getRenderState)(int32 state); - void (*rasterCreate)(Raster*); uint8 *(*rasterLock)(Raster*, int32 level); void (*rasterUnlock)(Raster*, int32 level); @@ -87,10 +90,10 @@ extern Driver *driver[NUM_PLATFORMS]; #define DRIVER driver[rw::platform] inline void setRenderState(int32 state, uint32 value){ - DRIVER->setRenderState(state, value); } + engine->setRenderState(state, value); } inline uint32 getRenderState(int32 state){ - return DRIVER->getRenderState(state); } + return engine->getRenderState(state); } namespace null { void beginUpdate(Camera*); diff --git a/src/rwobjects.h b/src/rwobjects.h index 6fc0625..3aabeb8 100644 --- a/src/rwobjects.h +++ b/src/rwobjects.h @@ -535,6 +535,9 @@ struct Camera : PluginBase float32 fogPlane; int32 projection; + Matrix viewMatrix; + float32 zScale, zShift; + // clump link handled by plugin in RW Clump *clump; LLLink inClump; @@ -556,6 +559,8 @@ struct Camera : PluginBase void beginUpdate(void) { this->beginUpdateCB(this); } void endUpdate(void) { this->endUpdateCB(this); } void clear(RGBA *col, uint32 mode); + void setNearPlane(float32); + void setFarPlane(float32); static Camera *streamRead(Stream *stream); bool streamWrite(Stream *stream); uint32 streamGetSize(void);