#include "inc.h" /*************** ** Generic Im3D ***************/ namespace rw { using namespace RWDEVICE; struct ClipVertex { V3d pos; float w; RGBA color; TexCoords tex; int outside; }; static ClipVertex *clipverts; static Im2DVertex *screenverts; static int32 numIm3dVerts; /* These are a bit PS2 specific unfortunately */ namespace ps2 { extern float zScaleScreen; extern float zShiftScreen; extern int32 doClipping; void setMatrix(RawMatrix *combined, Matrix *world); } void rawXform(V4d *out, V3d *in, RawMatrix *m) { out->x = in->x*m->right.x + in->y*m->up.x + in->z*m->at.x + m->pos.x; out->y = in->x*m->right.y + in->y*m->up.y + in->z*m->at.y + m->pos.y; out->z = in->x*m->right.z + in->y*m->up.z + in->z*m->at.z + m->pos.z; out->w = in->x*m->rightw + in->y*m->upw + in->z*m->atw + m->posw; } rw::V3d genXyzScale; rw::V3d genXyzOffset; void genIm3DTransform(void *vertices, int32 numVertices, Matrix *world, uint32 flags) { Im3DVertex *objverts; V4d pos; Camera *cam; int32 i; objverts = (Im3DVertex*)vertices; cam = engine->currentCamera; int32 width = cam->frameBuffer->width; int32 height = cam->frameBuffer->height; RawMatrix combined; setMatrix(&combined, world); numIm3dVerts = numVertices; // not quite what we do on VU1 #ifdef CLIP_DEBUG genXyzScale.x = width/4; genXyzScale.y = -height/4; #else genXyzScale.x = width/2; genXyzScale.y = -height/2; #endif genXyzScale.z = zScaleScreen; genXyzOffset.x = width/2; genXyzOffset.y = height/2; genXyzOffset.z = zShiftScreen; if(doClipping){ // no clipping, only transform to clip space here, more processing when rendering clipverts = rwNewT(ClipVertex, numVertices, MEMDUR_EVENT); screenverts = nil; for(i = 0; i < numVertices; i++){ rawXform(&pos, &objverts[i].position, &combined); int outside = 0; if(pos.x <= -pos.w) outside = 1; if(pos.x >= pos.w) outside = 1; if(pos.y <= -pos.w) outside = 1; if(pos.y >= pos.w) outside = 1; if(pos.z <= -pos.w) outside = 1; if(pos.z >= pos.w) outside = 1; clipverts[i].pos.x = pos.x; clipverts[i].pos.y = pos.y; clipverts[i].pos.z = pos.z; clipverts[i].w = pos.w; clipverts[i].color = objverts[i].getColor(); clipverts[i].tex.u = objverts[i].u; clipverts[i].tex.v = objverts[i].v; clipverts[i].outside = outside; } }else{ // no clipping, just transform everything to screen space directly screenverts = rwNewT(Im2DVertex, numVertices, MEMDUR_EVENT); clipverts = nil; for(i = 0; i < numVertices; i++){ rawXform(&pos, &objverts[i].position, &combined); // perspective division float32 recipZ = 1.0f/pos.w; pos.x *= recipZ; pos.y *= recipZ; pos.z *= recipZ; pos.x *= genXyzScale.x; pos.y *= genXyzScale.y; pos.z *= genXyzScale.z; pos.x += genXyzOffset.x; pos.y += genXyzOffset.y; pos.z += genXyzOffset.z; RGBA c = objverts[i].getColor(); screenverts[i].setScreenX(pos.x); screenverts[i].setScreenY(pos.y); screenverts[i].setScreenZ(pos.z); screenverts[i].setCameraZ(pos.w); screenverts[i].setRecipCameraZ(recipZ); screenverts[i].setColor(c.red, c.green, c.blue, c.alpha); screenverts[i].setU(objverts[i].u, recipZ); screenverts[i].setV(objverts[i].v, recipZ); } } } void genIm3DEnd(void) { rwFree(clipverts); clipverts = nil; rwFree(screenverts); screenverts = nil; numIm3dVerts = 0; } void addClipVertex(Im2DVertex *screen, ClipVertex *clip) { // perspective division V4d pos; pos.x = clip->pos.x; pos.y = clip->pos.y; pos.z = clip->pos.z; pos.w = clip->w; float32 recipZ = 1.0f/pos.w; pos.x *= recipZ; pos.y *= recipZ; pos.z *= recipZ; pos.x *= genXyzScale.x; pos.y *= genXyzScale.y; pos.z *= genXyzScale.z; pos.x += genXyzOffset.x; pos.y += genXyzOffset.y; pos.z += genXyzOffset.z; RGBA c = clip->color; screen->setScreenX(pos.x); screen->setScreenY(pos.y); screen->setScreenZ(pos.z); screen->setCameraZ(pos.w); screen->setRecipCameraZ(recipZ); screen->setColor(c.red, c.green, c.blue, c.alpha); screen->setU(clip->tex.u, recipZ); screen->setV(clip->tex.v, recipZ); } /* Interplated vertex, only position and barycentric coors */ struct InterVertex { // we keep w in z and interpolate the real z later V3d pos; V3d bary; }; /* interpolate clipped triangle from clipped vertices */ void interpVerts(Im2DVertex *screenVerts, InterVertex *out, int nout, ClipVertex *v0, ClipVertex *v1, ClipVertex *v2) { ClipVertex v; while(nout--){ v.pos.x = out->pos.x; v.pos.y = out->pos.y; v.w = out->pos.z; v.pos.z = out->bary.x*v0->pos.z + out->bary.y*v1->pos.z + out->bary.z*v2->pos.z; v.color.red = (int)(out->bary.x*v0->color.red + out->bary.y*v1->color.red + out->bary.z*v2->color.red); v.color.green = (int)(out->bary.x*v0->color.green + out->bary.y*v1->color.green + out->bary.z*v2->color.green); v.color.blue = (int)(out->bary.x*v0->color.blue + out->bary.y*v1->color.blue + out->bary.z*v2->color.blue); v.color.alpha = (int)(out->bary.x*v0->color.alpha + out->bary.y*v1->color.alpha + out->bary.z*v2->color.alpha); v.tex.u = out->bary.x*v0->tex.u + out->bary.y*v1->tex.u + out->bary.z*v2->tex.u; v.tex.v = out->bary.x*v0->tex.v + out->bary.y*v1->tex.v + out->bary.z*v2->tex.v; addClipVertex(screenVerts++, &v); out++; } } // sutherland-hodgman clipping static int32 clipPoly(InterVertex *in, int32 nin, InterVertex *out, Plane *plane) { int32 j; int32 nout; int32 x1, x2; float32 d1, d2, t; nout = 0; x1 = nin-1; for(j = 0; j < nin; j++){ x2 = j; d1 = dot(plane->normal, in[x1].pos) + plane->distance; d2 = dot(plane->normal, in[x2].pos) + plane->distance; if(d1 >= 0.0f) out[nout++] = in[x1]; if(d1*d2 < 0.0f){ float inv = 1.0f/(d1 - d2); out[nout].pos = scale(sub(scale(in[x2].pos, d1), scale(in[x1].pos, d2)), inv); out[nout].bary = scale(sub(scale(in[x2].bary, d1), scale(in[x1].bary, d2)), inv); nout++; } x1 = x2; } return nout; } void dumpPoly(int n, InterVertex *poly) { for(int i = 0; i < n; i++) printf("%d %f %f %f %f %f %f\n", i, poly[i].pos.x, poly[i].pos.y, poly[i].pos.z, poly[i].bary.x, poly[i].bary.y, poly[i].bary.z); } void genIm3DRenderIndexed(PrimitiveType prim, void *indices, int32 numIndices) { if(doClipping){ int i; // can only clip list here if(prim != PRIMTYPETRILIST) return; int numPrims = numIndices/3; int numClippedTris = 0; for(i = 0; i < numPrims; i++){ int i0 = ((uint16*)indices)[i*3+0]; int i1 = ((uint16*)indices)[i*3+1]; int i2 = ((uint16*)indices)[i*3+2]; if(clipverts[i0].outside || clipverts[i1].outside || clipverts[i2].outside) numClippedTris++; } if(screenverts) rwFree(screenverts); screenverts = rwNewT(Im2DVertex, numPrims*3 + numClippedTris*6, MEMDUR_EVENT); Camera *cam = (Camera*)engine->currentCamera; Plane planes[6] = { { { 0.0f, 0.0f, 1.0f }, -cam->nearPlane }, // z = -w near { { 0.0f, 0.0f, -1.0f }, cam->farPlane }, // z = w far { { 1.0f, 0.0f, 1.0f }, 0.0f }, // x = -w { { -1.0f, 0.0f, 1.0f }, 0.0f }, // x = w { { 0.0f, 1.0f, 1.0f }, 0.0f }, // y = -w { { 0.0f, -1.0f, 1.0f }, 0.0f } // y = w }; int numClippedVerts = 0; for(i = 0; i < numPrims; i++){ int i0 = ((uint16*)indices)[i*3+0]; int i1 = ((uint16*)indices)[i*3+1]; int i2 = ((uint16*)indices)[i*3+2]; ClipVertex *v0 = &clipverts[i0]; ClipVertex *v1 = &clipverts[i1]; ClipVertex *v2 = &clipverts[i2]; if(v0->outside || v1->outside || v2->outside){ // clip this triangle InterVertex buf[18]; InterVertex *in = &buf[0]; InterVertex *out = &buf[9]; in[0].pos.set(v0->pos.x, v0->pos.y, v0->w); in[0].bary.set(1.0f, 0.0f, 0.0f); in[1].pos.set(v1->pos.x, v1->pos.y, v1->w); in[1].bary.set(0.0f, 1.0f, 0.0f); in[2].pos.set(v2->pos.x, v2->pos.y, v2->w); in[2].bary.set(0.0f, 0.0f, 1.0f); int nout = 0; // Make list of new vertices if(nout = clipPoly(in, 3, out, &planes[0]), nout == 0) continue; if(nout = clipPoly(out, nout, in, &planes[1]), nout == 0) continue; if(nout = clipPoly(in, nout, out, &planes[2]), nout == 0) continue; if(nout = clipPoly(out, nout, in, &planes[3]), nout == 0) continue; if(nout = clipPoly(in, nout, out, &planes[4]), nout == 0) continue; if(nout = clipPoly(out, nout, in, &planes[5]), nout == 0) continue; out = in; Im2DVertex screenVerts[9]; interpVerts(screenVerts, out, nout, v0, v1, v2); for(int j = 0; j < nout-2; j++){ screenverts[numClippedVerts++] = screenVerts[0]; screenverts[numClippedVerts++] = screenVerts[j+1]; screenverts[numClippedVerts++] = screenVerts[j+2]; } }else{ // all good, just add addClipVertex(&screenverts[numClippedVerts++], v0); addClipVertex(&screenverts[numClippedVerts++], v1); addClipVertex(&screenverts[numClippedVerts++], v2); } } im2d::RenderPrimitive(prim, screenverts, numClippedVerts); }else im2d::RenderIndexedPrimitive(prim, screenverts, numIm3dVerts, indices, numIndices); } }