Implement perspective rendering

Armin Burgmeier 2010-02-06 18:33:30 +01:00
parent c5ec5592a0
commit 406dc7c734
10 changed files with 236 additions and 81 deletions

View File

@ -1456,5 +1456,10 @@ Drink = {
NextAction = "Walk",
}, }, def);
SetProperty("Name", "Clonk", def);
// Set perspective
SetProperty("PerspectiveR", 12000, def);
SetProperty("PerspectiveTheta", 20, def);
SetProperty("PerspectivePhi", 70, def);
}

View File

@ -88,4 +88,7 @@ public func IsToolProduct() { return 1; }
func Definition(def) {
SetProperty("Collectible", 1, def);
SetProperty("Name", "$Name$", def);
}
SetProperty("PerspectiveR", 5000, def);
SetProperty("PerspectiveTheta", 20, def);
SetProperty("PerspectivePhi", 70, def);
}

View File

@ -211,4 +211,7 @@ NextAction = "Build",
PhaseCall="Smoking",
}, }, def);
SetProperty("Name", "$Name$", def);
SetProperty("PerspectiveR", 11000, def);
SetProperty("PerspectiveTheta", 20, def);
SetProperty("PerspectivePhi", 25, def);
}

View File

@ -170,4 +170,7 @@ NextAction = "Drive",
//Animation = "Drive",
}, }, def);
SetProperty("Name", "$Name$", def);
SetProperty("PerspectiveR", 4500, def);
SetProperty("PerspectiveTheta", 25, def);
SetProperty("PerspectivePhi", 30, def);
}

View File

@ -1066,6 +1066,10 @@ void C4GraphicsOverlay::Draw(C4TargetFacet &cgo, C4Object *pForObj, int32_t iByP
else
{
C4Def *pDef = pSourceGfx->pDef;
int32_t r = pDef->GetPropertyInt(P_PerspectiveR);
int32_t theta = pDef->GetPropertyInt(P_PerspectiveTheta);
int32_t phi = pDef->GetPropertyInt(P_PerspectivePhi);
float twdt, thgt;
C4DrawTransform trf(Transform, float(iTx), float(iTy));
if(fZoomToShape)
@ -1073,11 +1077,15 @@ void C4GraphicsOverlay::Draw(C4TargetFacet &cgo, C4Object *pForObj, int32_t iByP
twdt = pForObj->Shape.Wdt;
thgt = pForObj->Shape.Hgt;
// Keep aspect ratio
if(twdt*pDef->Shape.Hgt < pDef->Shape.Wdt*thgt)
thgt = twdt*pDef->Shape.Hgt/pDef->Shape.Wdt;
else
twdt = thgt*pDef->Shape.Wdt/pDef->Shape.Hgt;
// Keep aspect ratio for nonperspective projection
// (perspective projection will not show up distorted anyway, and this would distort it)
if(r <= 0)
{
if(twdt*pDef->Shape.Hgt < pDef->Shape.Wdt*thgt)
thgt = twdt*pDef->Shape.Hgt/pDef->Shape.Wdt;
else
twdt = thgt*pDef->Shape.Wdt/pDef->Shape.Hgt;
}
float fZoom = Min(pForObj->Shape.Wdt/twdt, pForObj->Shape.Hgt/thgt);
trf.ScaleAt(fZoom/*pForObj->Shape.Wdt/twdt*/, fZoom/*pForObj->Shape.Hgt/thgt*/, float(iTx), float(iTy));
@ -1088,7 +1096,16 @@ void C4GraphicsOverlay::Draw(C4TargetFacet &cgo, C4Object *pForObj, int32_t iByP
thgt = pDef->Shape.Hgt;
}
lpDDraw->RenderMesh(*pMeshInstance, cgo.Surface, iTx - twdt/2, iTy - thgt/2, twdt, thgt, pForObj->Color, &trf);
if(r > 0)
{
lpDDraw->SetPerspective(r/1000.0f, theta, phi);
lpDDraw->RenderMesh(*pMeshInstance, cgo.Surface, iTx - twdt/2, iTy - thgt/2, twdt, thgt, pForObj->Color, &trf);
lpDDraw->UnsetPerspective();
}
else
{
lpDDraw->RenderMesh(*pMeshInstance, cgo.Surface, iTx - twdt/2, iTy - thgt/2, twdt, thgt, pForObj->Color, &trf);
}
}
}
@ -1153,7 +1170,21 @@ void C4GraphicsOverlay::DrawPicture(C4Facet &cgo, C4Object *pForObj)
}
else
{
lpDDraw->RenderMesh(*pMeshInstance, cgo.Surface, cgo.X, cgo.Y, pForObj->Shape.Wdt, pForObj->Shape.Hgt, pForObj->Color, &C4DrawTransform(Transform, cgo.X+float(pForObj->Shape.Wdt)/2, cgo.Y+float(pForObj->Shape.Hgt)/2));
C4Def *pDef = pSourceGfx->pDef;
int32_t r = pDef->GetPropertyInt(P_PerspectiveR);
int32_t theta = pDef->GetPropertyInt(P_PerspectiveTheta);
int32_t phi = pDef->GetPropertyInt(P_PerspectivePhi);
if(r > 0)
{
lpDDraw->SetPerspective(r/1000.0f, theta, phi);
lpDDraw->RenderMesh(*pMeshInstance, cgo.Surface, cgo.X, cgo.Y, pForObj->Shape.Wdt, pForObj->Shape.Hgt, pForObj->Color, &C4DrawTransform(Transform, cgo.X+float(pForObj->Shape.Wdt)/2, cgo.Y+float(pForObj->Shape.Hgt)/2));
lpDDraw->UnsetPerspective();
}
else
{
lpDDraw->RenderMesh(*pMeshInstance, cgo.Surface, cgo.X, cgo.Y, pForObj->Shape.Wdt, pForObj->Shape.Hgt, pForObj->Color, &C4DrawTransform(Transform, cgo.X+float(pForObj->Shape.Wdt)/2, cgo.Y+float(pForObj->Shape.Hgt)/2));
}
}
// cleanup
if (dwBlitMode == C4GFXBLIT_PARENT)

View File

@ -484,9 +484,11 @@ void CStdDDraw::Default()
Gamma.Default();
DefRamp.Default();
lpPrimary=lpBack=NULL;
// pClrModMap = NULL; - invalid it !fUseClrModMap anyway
// pClrModMap = NULL; - invalid if !fUseClrModMap anyway
fUseClrModMap = false;
ZoomX = 0; ZoomY = 0; Zoom = 1;
//EyeR = 1.0f; EyeTheta = EyePhi = 0.0f; invalid if !fUsePerspective anyway
fUsePerspective = false;
}
void CStdDDraw::Clear()

View File

@ -240,6 +240,8 @@ class CStdDDraw
bool fUseClrModMap; // if set, pClrModMap will be checked for color modulations
unsigned char Saturation; // if < 255, an extra filter is used to reduce the saturation
int ZoomX; int ZoomY;
float EyeR, EyeTheta, EyePhi;
bool fUsePerspective;
public:
float Zoom;
// General
@ -348,6 +350,8 @@ class CStdDDraw
void GetZoom(ZoomData *r) { r->Zoom=Zoom; r->X=ZoomX; r->Y=ZoomY; }
void ApplyZoom(float & X, float & Y);
void RemoveZoom(float & X, float & Y);
void SetPerspective(float Eye_R, float Eye_Theta, float Eye_Phi) { fUsePerspective = true; EyeR = Eye_R; EyeTheta = Eye_Theta; EyePhi = Eye_Phi; }
void UnsetPerspective() { fUsePerspective = false; }
virtual void SetTexture() = 0;
virtual void ResetTexture() = 0;

View File

@ -135,8 +135,8 @@ bool CStdGL::UpdateClipper()
// Set clipping plane to -1000 and 1000 so that large meshes are not
// clipped away.
glOrtho((GLdouble) iX, (GLdouble) (iX+iWdt), (GLdouble) (iY+iHgt), (GLdouble) iY, -1000.0f, 1000.0f);
//gluOrtho2D((GLdouble) iX, (GLdouble) (iX+iWdt), (GLdouble) (iY+iHgt), (GLdouble) iY);
//glOrtho((GLdouble) iX, (GLdouble) (iX+iWdt), (GLdouble) (iY+iHgt), (GLdouble) iY, -1000.0f, 1000.0f);
gluOrtho2D((GLdouble) iX, (GLdouble) (iX+iWdt), (GLdouble) (iY+iHgt), (GLdouble) iY);
//gluOrtho2D((GLdouble) 0, (GLdouble) xRes, (GLdouble) yRes, (GLdouble) yRes-iHgt);
return true;
}
@ -939,6 +939,35 @@ namespace
#endif
}
}
// Apply Zoom and Transformation to the current matrix stack. Return
// parity of the transformation.
bool ApplyZoomAndTransform(float ZoomX, float ZoomY, float Zoom, CBltTransform* pTransform)
{
// Apply zoom
glTranslatef(ZoomX, ZoomY, 0.0f);
glScalef(Zoom, Zoom, 1.0f);
glTranslatef(-ZoomX, -ZoomY, 0.0f);
// Apply transformation
if(pTransform)
{
const GLfloat transform[16] = { pTransform->mat[0], pTransform->mat[3], 0, pTransform->mat[6], pTransform->mat[1], pTransform->mat[4], 0, pTransform->mat[7], 0, 0, 1, 0, pTransform->mat[2], pTransform->mat[5], 0, pTransform->mat[8] };
glMultMatrixf(transform);
// Compute parity of the transformation matrix - if parity is swapped then
// we need to cull front faces instead of back faces.
const float det = transform[0]*transform[5]*transform[15]
+ transform[4]*transform[13]*transform[3]
+ transform[12]*transform[1]*transform[7]
- transform[0]*transform[13]*transform[7]
- transform[4]*transform[1]*transform[15]
- transform[12]*transform[5]*transform[3];
return det > 0;
}
return true;
}
}
namespace
@ -962,41 +991,11 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw
{
const StdMesh& mesh = instance.Mesh;
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_BLEND); // TODO: Shouldn't this always be enabled? - blending does not work for meshes without this though.
//glEnable(GL_CULL_FACE);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
// Apply zoom
glTranslatef(ZoomX, ZoomY, 0.0f);
glScalef(Zoom, Zoom, 1.0f);
glTranslatef(-ZoomX, -ZoomY, 0.0f);
// TODO: Initialize with OgreToClonk matrix parity
bool parity = OgreToClonkParity;
if(pTransform)
{
const GLfloat transform[16] = { pTransform->mat[0], pTransform->mat[3], 0, pTransform->mat[6], pTransform->mat[1], pTransform->mat[4], 0, pTransform->mat[7], 0, 0, 1, 0, pTransform->mat[2], pTransform->mat[5], 0, pTransform->mat[8] };
glMultMatrixf(transform);
// Compute parity of the transformation matrix - if parity is swapped then
// we need to cull front faces instead of back faces.
const float det = transform[0]*transform[5]*transform[15]
+ transform[4]*transform[13]*transform[3]
+ transform[12]*transform[1]*transform[7]
- transform[0]*transform[13]*transform[7]
- transform[4]*transform[1]*transform[15]
- transform[12]*transform[5]*transform[3];
if(det < 0) parity = !parity;
}
// Convert bounding box to clonk coordinate system
// (TODO: We should cache this, not sure where though)
// TODO: Note that this does not generally work with an arbitrary transformation this way
const StdMeshBox& box = mesh.GetBoundingBox();
StdMeshVector v1, v2;
v1.x = box.x1; v1.y = box.y1; v1.z = box.z1;
@ -1004,47 +1003,142 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw
v1 = OgreToClonk * v1; // TODO: Include translation
v2 = OgreToClonk * v2; // TODO: Include translation
// Scale so that the mesh fits in (tx,ty,twdt,thgt)
const float rx = -std::min(v1.x,v1.y) / fabs(v2.x - v1.x);
const float ry = -std::min(v1.y,v2.y) / fabs(v2.y - v1.y);
const float dx = tx + rx*twdt;
const float dy = ty + ry*thgt;
const float scx = twdt/fabs(v2.x - v1.x);
const float scy = thgt/fabs(v2.y - v1.y);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_BLEND); // TODO: Shouldn't this always be enabled? - blending does not work for meshes without this though.
//glEnable(GL_CULL_FACE);
#if 0
// Scale so that Z coordinate is between -1 and 1, otherwise parts of
// the mesh could be clipped away by the near or far clipping plane.
// This technique might also enable us not to clear the depth buffer
// after every mesh rendering - we could simply scale the first mesh
// of the scene so that it's Z coordinate is between 0 and 1, scale
// the second mesh that it is between 1 and 2, and so on.
// This of course requires an orthogonal projection so that the
// meshes don't look distorted - if we should every decide to use
// a perspective projection we need to think of something different.
// Set up projection matrix first. We do transform and Zoom with the
// projection matrix, so that lighting is applied to the untransformed/unzoomed
// mesh.
glMatrixMode(GL_PROJECTION);
glPushMatrix();
// TODO: This is currently commented out because it gets the lighting
// wrong (objects are too dark). Instead we changed the clipping plane
// to be large enough. I think this is still a got idea, though,
// when we fix the lighting. Maybe we need to adapt the normals
// somehow...
const float scz = 1.0/fabs(v2.z - v1.z);
#else
const float scz = 1.0;
#endif
// Keep aspect ratio:
//if(scx < scy) scy = scx;
//else scx = scy;
glTranslatef(dx, dy, 0.0f);
glScalef(scx, scy, scz);
if(!fUsePerspective)
{
// Orthographic projection. The orthographic projection matrix
// is already loaded in the GL matrix stack.
// Put a light source in front of the object
const GLfloat light_position[] = { 0.0f, 0.0f, v2.z + 15.0f*Zoom, 1.0f };
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHT0);
if(!ApplyZoomAndTransform(ZoomX, ZoomY, Zoom, pTransform))
parity = !parity;
// Convert from Ogre to Clonk coordinate system
glMultMatrixf(OgreToClonkGL);
// Scale so that the mesh fits in (tx,ty,twdt,thgt)
const float rx = -std::min(v1.x,v1.y) / fabs(v2.x - v1.x);
const float ry = -std::min(v1.y,v2.y) / fabs(v2.y - v1.y);
const float dx = tx + rx*twdt;
const float dy = ty + ry*thgt;
const float scx = twdt/fabs(v2.x - v1.x);
const float scy = thgt/fabs(v2.y - v1.y);
// Scale so that Z coordinate is between -1 and 1, otherwise parts of
// the mesh could be clipped away by the near or far clipping plane.
// Note that this only works in the projection matrix, otherwise
// lighting is screwed up.
// This technique might also enable us not to clear the depth buffer
// after every mesh rendering - we could simply scale the first mesh
// of the scene so that it's Z coordinate is between 0 and 1, scale
// the second mesh that it is between 1 and 2, and so on.
// This of course requires an orthogonal projection so that the
// meshes don't look distorted - if we should ever decide to use
// a perspective projection we need to think of something different.
// Take also into account that the depth is not linear but linear
// in the logarithm (if I am not mistaken), so goes like 1/z
const float scz = 1.0/fabs(v2.z - v1.z);
// Keep aspect ratio:
//if(scx < scy) scy = scx;
//else scx = scy;
glTranslatef(dx, dy, 0.0f);
glScalef(scx, scy, scz);
}
else
{
// Perspective projection. We need current viewport size.
const int iWdt=Min(iClipX2, RenderTarget->Wdt-1)-iClipX1+1;
const int iHgt=Min(iClipY2, RenderTarget->Hgt-1)-iClipY1+1;
// Get away with orthographic projection matrix currently loaded
glLoadIdentity();
// Back to GL device coordinates
glTranslatef(-1.0f, 1.0f, 0.0f);
glScalef(2.0f/iWdt, -2.0f/iHgt, 1.0f);
// Compensate for 1.0f aspect (need to do this after having applied zoom and transform)
// which is why we can't directly set it in gluPerspective
glTranslatef(twdt/2,thgt/2, 0.0f);
glScalef((twdt>thgt)?(thgt/twdt):1.0f,(twdt>thgt)?(1.0f):(twdt/thgt), 1.0f);
glTranslatef(-twdt/2,-thgt/2, 0.0f);
if(!ApplyZoomAndTransform(ZoomX, ZoomY, Zoom, pTransform))
parity = !parity;
// Move to target location
glTranslatef(tx, ty, 0.0f);
glScalef(((float)twdt)/iWdt, ((float)thgt)/iHgt, 1.0f);
// Return to Clonk coordinate frame
glScalef(iWdt/2.0, -iHgt/2.0, 1.0f);
glTranslatef(1.0f, -1.0f, 0.0f);
// Apply perspective projection. After this, x and y range from
// -1 to 1, and this is mapped into tx/ty/twdt/thgt by the above code.
// aspect is 1.0f since it needs to be applied after zoom/transform,
// this is also done above.
gluPerspective(60.0f, 1.0f, 0.1f, 100.0f);
}
// Now set up modelview matrix
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
if(!fUsePerspective)
{
// Put a light source in front of the object
const GLfloat light_position[] = { 0.0f, 0.0f, v2.z + 15.0f, 1.0f };
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHT0);
// Convert from Ogre to Clonk coordinate system
glMultMatrixf(OgreToClonkGL);
}
else
{
const float MeshX = (v1.x + v2.x)/2.0f;
const float MeshY = (v1.y + v2.y)/2.0f;
const float MeshZ = (v1.z + v2.z)/2.0f;
const float cosEyeTheta = cos(EyeTheta/180.0f*M_PI);
const float sinEyeTheta = sin(EyeTheta/180.0f*M_PI);
const float cosEyePhi = cos(EyePhi/180.0f*M_PI);
const float sinEyePhi = sin(EyePhi/180.0f*M_PI);
const float EyeX = MeshX + EyeR * sinEyePhi * cosEyeTheta;
const float EyeY = MeshY - EyeR * sinEyeTheta;
const float EyeZ = MeshZ + EyeR * cosEyePhi * cosEyeTheta;
// Up vector is unit vector in theta direction
const float UpX = -sinEyePhi * sinEyeTheta;
const float UpY = -cosEyeTheta;
const float UpZ = -cosEyePhi * sinEyeTheta;
// Apply lighting (light source at camera position)
const GLfloat light_position[] = { EyeX, EyeY, EyeZ, 1.0f };
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHT0);
// center on mesh's bounding box, so that the mesh is really in the center of the viewport
gluLookAt(EyeX, EyeY, EyeZ, MeshX, MeshY, MeshZ, UpX, UpY, UpZ);
// Fix X axis (???)
glScalef(-1.0f, 1.0f, 1.0f);
// Convert from Ogre to Clonk coordinate system
glMultMatrixf(OgreToClonkGL);
}
DWORD dwModClr = BlitModulated ? BlitModulateClr : 0xffffffff;
@ -1066,6 +1160,11 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw
RenderMeshImpl(instance, dwModClr, dwPlayerColor, parity);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glActiveTexture(GL_TEXTURE0); // switch back to default
glClientActiveTexture(GL_TEXTURE0); // switch back to default
glDepthMask(GL_TRUE);
@ -1076,8 +1175,7 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw
glDisable(GL_CULL_FACE);
//glDisable(GL_BLEND);
glShadeModel(GL_FLAT);
glPopMatrix();
// TODO: glScissor, so that we only clear the area the mesh covered.
glClear(GL_DEPTH_BUFFER_BIT);
}

View File

@ -118,6 +118,9 @@ C4StringTable::C4StringTable()
P[P_LineColors] = RegString("LineColors");
P[P_LineAttach] = RegString("LineAttach");
P[P_MouseDragImage] = RegString("MouseDragImage");
P[P_PerspectiveR] = RegString("PerspectiveR");
P[P_PerspectiveTheta] = RegString("PerspectiveTheta");
P[P_PerspectivePhi] = RegString("PerspectivePhi");
for (unsigned int i = 0; i < P_LAST; ++i) P[i]->IncRef();
}

View File

@ -174,6 +174,9 @@ P_Visibility,
P_Parallaxity,
P_LineColors,
P_LineAttach,
P_PerspectiveR,
P_PerspectiveTheta,
P_PerspectivePhi,
P_Procedure,
P_Directions,
P_FlipDir,