// Copyright (C) 1996 Keith Whitwell. // This file may only be copied under the terms of the GNU Library General // Public License - see the file COPYING in the lib3d distribution. #include #include #include #include #include #include TexturePipeline::ClipFunc TexturePipeline::Intersect[] = { intersectZ1, intersectZ2, intersectY1, intersectY2, intersectX1, intersectX2 }; extern float D; class Texture_VertexNormalData { public: float sumDotProd; }; class Texture_PolygonData { public: bool backface; }; class Texture_VertexData : public TexturePipelineData { public: Vector3 cvv; uint outcodes; float u,v; }; TexturePipeline::TexturePipeline() : sizeNpool(256), npool(new Texture_VertexNormalData[sizeNpool]), nrVpool(0), sizeVpool(256), vpool(new Texture_VertexData[sizeVpool]), nrPpool(0), sizePpool(256), ppool(new Texture_PolygonData[sizePpool]), pv(new (TexturePipelineData*)[MAX_CLIPPED_VERTICES]) { } TexturePipeline::~TexturePipeline() { delete npool; delete vpool; delete ppool; delete pv; } void TexturePipeline::registerModel( Model& model ) { stitchModel( model ); // Ensure we have room for temporary vertices. uint maxVertices = nrVertices + MAX_CLIPPED_VERTICES; if (sizeVpool < maxVertices) { while ((sizeVpool *= 2) < maxVertices); delete [] vpool; vpool = new Texture_VertexData[sizeVpool]; } if (sizePpool < nrPolygons) { while ((sizePpool *= 2) < nrPolygons); delete [] ppool; ppool = new Texture_PolygonData[sizePpool]; } if (sizeNpool < nrVertexNormals) { while ((sizeNpool *= 2) < nrVertexNormals); delete [] npool; npool = new Texture_VertexNormalData[sizeNpool]; } } void TexturePipeline::render(Model &model, Viewport &viewport, const Light *, uint, uint clipPlanes, uint flags) { stitchModel(model); renderFlags = flags; thisFrame++; have_backface_info = false; clip = false; if ( !clipPlanes ) { transform( viewport ); } else if ( !transformForClipping( viewport ) ) { return; } if (!clip) { cullAndRenderPolygons( viewport ); } else { cullBackFaces(); clipAndRenderPolygons( viewport ); } } void TexturePipeline::cullBackFaces() { Vector3 tmp; have_backface_info = true; const Polygon *poly = polygons; Texture_PolygonData *pd = ppool; int i = nrPolygons; do { tmp.sub(vertices[poly->vertex0].model, *objectViewPos); pd->backface = (dot(tmp, polygonNormals[poly->normal].model) < 0); pd++; poly++; } while (--i); } // Transform for rendering without clipping void TexturePipeline::transform( Viewport &viewport ) { Texture_VertexData *vp = vpool; const Vertex *v = vertices; int j = nrVertices; nrVpool = j; // Transform the first vertex here. vp->w = vp->device.project( *objectToDevice, v->model ); vp->uw = v->u * vp->w; vp->vw = v->v * vp->w; //vp->intensity = int(npool[v->normal].sumDotProd); xmax = vp->device.v[X]; xmin = vp->device.v[X]; ymax = vp->device.v[Y]; ymin = vp->device.v[Y]; vp++; v++; j--; // Transform the remaining vertices. do { vp->w = vp->device.project( *objectToDevice, v->model ); vp->uw = v->u * vp->w; vp->vw = v->v * vp->w; //vp->intensity = int(npool[v->normal].sumDotProd); if (xmax < vp->device.v[X]) xmax = vp->device.v[X]; if (xmin > vp->device.v[X]) xmin = vp->device.v[X]; if (ymax < vp->device.v[Y]) ymax = vp->device.v[Y]; if (ymin > vp->device.v[Y]) ymin = vp->device.v[Y]; vp++; v++; } while (--j); viewport.setDirty( xmin, ymin, xmax, ymax ); } // Render void TexturePipeline::renderPolygons( Viewport &viewport ) { for (int j = nrPolygons ; j-- ; ) { const Polygon &poly = polygons[j]; if (!ppool[j].backface) { pv[0] = &vpool[poly.vertex0]; pv[1] = &vpool[poly.vertex1]; pv[2] = &vpool[poly.vertex2]; viewport.textureTriangleZb(pv, *texture); } } } // Backface cull and render void TexturePipeline::cullAndRenderPolygons( Viewport &viewport ) { for (int j = nrPolygons ; j-- ; ) { const Polygon &poly = polygons[j]; pv[0] = &vpool[poly.vertex0]; pv[1] = &vpool[poly.vertex1]; pv[2] = &vpool[poly.vertex2]; bool cw =( ((pv[1]->device.v[1] - pv[0]->device.v[1]) * (pv[2]->device.v[0] - pv[0]->device.v[0])) >= ((pv[1]->device.v[0] - pv[0]->device.v[0]) * (pv[2]->device.v[1] - pv[0]->device.v[1]))); if (cw) { viewport.textureTriangleZb(pv, *texture); } } } // Transform for clipping bool TexturePipeline::transformForClipping( Viewport &viewport ) { Texture_VertexData *vp = vpool; const Vertex *v = vertices; int j = nrVertices; nrVpool = j; uint in = 0; xmax = 0; xmin = 1000000; // arbitary bignum. ymax = 0; ymin = 1000000; do { vp->u = v->u; vp->v = v->v; //vp->intensity = int(npool[v->normal].sumDotProd); vp->cvv.mul( *objectToCvv, v->model ); vp->outcodes = vp->cvv.computeOutcodes(D); if ( !vp->outcodes ) { in++; vp->w = vp->device.project( *cvvToDevice, vp->cvv ); vp->uw = v->u * vp->w; vp->vw = v->v * vp->w; if (xmax < vp->device.v[X]) xmax = vp->device.v[X]; if (xmin > vp->device.v[X]) xmin = vp->device.v[X]; if (ymax < vp->device.v[Y]) ymax = vp->device.v[Y]; if (ymin > vp->device.v[Y]) ymin = vp->device.v[Y]; } vp++; v++; } while (--j); clip = (in != nrVertices); if (!clip) viewport.setDirty( xmin, ymin, xmax, ymax ); return in != 0; } // Clip and Render void TexturePipeline::clipAndRenderPolygons( Viewport &viewport ) { const Polygon *poly = polygons; Texture_PolygonData *pp = ppool; int j = nrPolygons; do { if (!pp->backface) { // optimistic assignment of pv[] - good for the teapot // program, but what about real usage? pv[0] = &vpool[poly->vertex0]; uint oc0 = vpool[poly->vertex0].outcodes; pv[1] = &vpool[poly->vertex1]; uint oc1 = vpool[poly->vertex1].outcodes; pv[2] = &vpool[poly->vertex2]; uint oc2 = vpool[poly->vertex2].outcodes; if ((oc0&oc1&oc2) == 0) { uint intersections = oc0|oc1|oc2; if (intersections == 0) { viewport.textureTriangleZb(pv, *texture); } else { uint nr = nrVpool; if (clipPolygon(*poly, intersections)) { viewport.texturePolygonZb(nrClippedVertices, pv, *texture); } nrVpool = nr; } } } poly++; pp++; } while (--j); viewport.setDirty( xmin, ymin, xmax, ymax ); } // Clipping in the truncated pyramid CVV. // // bool TexturePipeline::clipPolygon(const Polygon &poly, uint intersections ) { // Clip against a truncated pyramid Texture_VertexData *tmp_pool[MAX_CLIPPED_VERTICES]; Texture_VertexData **from = tmp_pool; Texture_VertexData **to = (Texture_VertexData **)pv; int fromCount = 3; from[0] = &vpool[poly.vertex0]; from[1] = &vpool[poly.vertex1]; from[2] = &vpool[poly.vertex2]; int cb = 1; int plane = 0; for (; plane < 6; plane++, cb *= 2) { if ((cb & intersections) == 0) continue; int toCount = 0; int j = fromCount-1; int i = 0; int flagJ = from[j]->outcodes & cb; do { int flagI = from[i]->outcodes & cb; if (flagI ^ flagJ) { to[toCount] = &vpool[nrVpool++]; (*Intersect[plane])(*from[i], *from[j], *to[toCount]); to[toCount]->outcodes = to[toCount]->cvv.computeOutcodes(D); toCount++; } if (!flagI) { to[toCount++] = from[i]; } flagJ = flagI; j = i++; } while ( i < fromCount ); if (toCount == 0) return false; fromCount = toCount; Texture_VertexData **tmp = from; from = to; to = tmp; } // Transform new vertices to device space. The others are already // done. // for (int i = 0 ; i < fromCount ; i++ ) { Texture_VertexData &v = *from[i]; pv[i] = from[i]; v.w = v.device.project( *cvvToDevice, v.cvv ); v.uw = v.u * v.w; v.vw = v.v * v.w; if (xmax < v.device.v[X]) xmax = v.device.v[X]; if (xmin > v.device.v[X]) xmin = v.device.v[X]; if (ymax < v.device.v[Y]) ymax = v.device.v[Y]; if (ymin > v.device.v[Y]) ymin = v.device.v[Y]; } nrClippedVertices = fromCount; return true; } void TexturePipeline::intersectZ1(const Texture_VertexData& a, const Texture_VertexData& b, Texture_VertexData &out ) { // Intersect all four components with Z=1 Vector3 d; d.sub(b.cvv,a.cvv); float t = (1-a.cvv.v[Z]) / d.v[Z]; out.cvv.v[X] = a.cvv.v[X] + t*d.v[X]; out.cvv.v[Y] = a.cvv.v[Y] + t*d.v[Y]; out.cvv.v[Z] = 1; out.intensity = a.intensity + int(t*(b.intensity-a.intensity)); out.u = a.u + (t*(b.u-a.u)); out.v = a.v + (t*(b.v-a.v)); } void TexturePipeline::intersectZ2(const Texture_VertexData& a, const Texture_VertexData& b, Texture_VertexData &out ) { // Intersect all four components with Z=::D Vector3 d; d.sub(b.cvv,a.cvv); float t = (::D-a.cvv.v[Z]) / d.v[Z]; out.cvv.v[X] = a.cvv.v[X] + t*d.v[X]; out.cvv.v[Y] = a.cvv.v[Y] + t*d.v[Y]; out.cvv.v[Z] = ::D; out.intensity = a.intensity + int(t*(b.intensity-a.intensity)); out.u = a.u + (t*(b.u-a.u)); out.v = a.v + (t*(b.v-a.v)); } void TexturePipeline::intersectY1(const Texture_VertexData& a, const Texture_VertexData& b, Texture_VertexData &out ) { // Intersect with Y=Z Vector3 d; d.sub(b.cvv,a.cvv); float t = (a.cvv.v[Y]-a.cvv.v[Z]) / (d.v[Z]-d.v[Y]); out.cvv.v[X] = a.cvv.v[X] + t*d.v[X]; out.cvv.v[Y] = a.cvv.v[Y] + t*d.v[Y]; out.cvv.v[Z] = out.cvv.v[Y]; out.intensity = a.intensity + int(t*(b.intensity-a.intensity)); out.u = a.u + (t*(b.u-a.u)); out.v = a.v + (t*(b.v-a.v)); } void TexturePipeline::intersectY2(const Texture_VertexData& a, const Texture_VertexData& b, Texture_VertexData &out ) { // Intersect with Y=-Z Vector3 d; d.sub(b.cvv,a.cvv); float t = -(a.cvv.v[Y]+a.cvv.v[Z]) / (d.v[Z]+d.v[Y]); out.cvv.v[X] = a.cvv.v[X] + t*d.v[X]; out.cvv.v[Y] = a.cvv.v[Y] + t*d.v[Y]; out.cvv.v[Z] = - out.cvv.v[Y]; out.intensity = a.intensity + int(t*(b.intensity-a.intensity)); out.u = a.u + (t*(b.u-a.u)); out.v = a.v + (t*(b.v-a.v)); } void TexturePipeline::intersectX1(const Texture_VertexData& a, const Texture_VertexData& b, Texture_VertexData &out ) { // Intersect with X=Z Vector3 d; d.sub(b.cvv,a.cvv); float t = (a.cvv.v[X]-a.cvv.v[Z]) / (d.v[Z]-d.v[X]); out.cvv.v[X] = a.cvv.v[X] + t*d.v[X]; out.cvv.v[Y] = a.cvv.v[Y] + t*d.v[Y]; out.cvv.v[Z] = out.cvv.v[X]; out.intensity = a.intensity + int(t*(b.intensity-a.intensity)); out.u = a.u + (t*(b.u-a.u)); out.v = a.v + (t*(b.v-a.v)); } void TexturePipeline::intersectX2(const Texture_VertexData& a, const Texture_VertexData& b, Texture_VertexData &out ) { // Intersect with X=-Z Vector3 d; d.sub(b.cvv,a.cvv); float t = -(a.cvv.v[X]+a.cvv.v[Z]) / (d.v[Z]+d.v[X]); out.cvv.v[X] = a.cvv.v[X] + t*d.v[X]; out.cvv.v[Y] = a.cvv.v[Y] + t*d.v[Y]; out.cvv.v[Z] = - out.cvv.v[X]; out.intensity = a.intensity + int(t*(b.intensity-a.intensity)); out.u = a.u + (t*(b.u-a.u)); out.v = a.v + (t*(b.v-a.v)); }