/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "trecision/actor.h" #include "trecision/animtype.h" #include "trecision/graphics.h" #include "trecision/renderer3d.h" #include "trecision/trecision.h" #include "trecision/video.h" namespace Trecision { #define SHADOWVERTSNUM 42 static const int16 _shadowVerts[SHADOWVERTSNUM] = { 6, 15, 23, 24, 32, 78, 80, 81, 83, 86, 90, 99, 107, 108, 116, 155, 157, 158, 160, 164, 168, 169, 173, 174, 187, 188, 192, 193, 213, 215, 227, 229, 235, 238, 249, 250, 252, 253, 299, 306, 330, 336 }; #define SHADOWFACESNUM 48 const int16 _shadowFaces[SHADOWFACESNUM][3] = { {22, 21, 5}, {7, 5, 22}, {7, 19, 5}, {5, 2, 19}, {27, 24, 16}, {27, 16, 18}, {18, 16, 9}, {18, 13, 9}, {13, 9, 2}, {3, 19, 12}, {25, 26, 17}, {17, 15, 25}, {17, 19, 15}, {15, 12, 19}, {20, 23, 8}, {8, 6, 20}, {6, 9, 3}, {3, 8, 6}, {12, 3, 4}, {4, 11, 12}, {35, 4, 11}, {13, 2, 1}, {1, 14, 13}, {14, 37, 1}, {1, 34, 37}, {31, 36, 37}, {37, 30, 31}, {29, 34, 35}, {35, 29, 28}, {36, 11, 31}, {30, 37, 14}, {29, 1, 34}, {28, 4, 35}, {36, 10, 35}, {35, 32, 10}, {37, 0, 34}, {37, 33, 0}, {0, 33, 39}, {39, 40, 0}, {10, 38, 32}, {32, 41, 38}, {36, 35, 34}, {36, 37, 35}, {11, 36, 35}, {38, 40, 41}, {41, 38, 39}, {2, 19, 13}, {3, 9, 12} }; Renderer3D::Renderer3D(TrecisionEngine *vm) : _vm(vm) { _zBuffer = new int16[ZBUFFERSIZE / 2]; _minXClip = 0; _minYClip = 0; _maxXClip = 0; _maxYClip = 0; _zBufStartX = 0; _zBufStartY = 0; _zBufWid = 0; _shadowLightNum = 0; _totalShadowVerts = 0; // data for the triangle routines for (int i = 0; i < 480; ++i) { _lEdge[i] = 0; _rEdge[i] = 0; _lColor[i] = 0; _rColor[i] = 0; _lZ[i] = 0; _rZ[i] = 0; _lTextX[i] = 0; _rTextX[i] = 0; _lTextY[i] = 0; _rTextY[i] = 0; } for (int i = 0; i < 10; ++i) _shadowIntens[i] = 0; for (int i = 0; i < MAXVERTEX; ++i) { _vVertex[i].clear(); _shVertex[i].clear(); } } Renderer3D::~Renderer3D() { delete[] _zBuffer; } void Renderer3D::textureTriangle(int32 x1, int32 y1, int32 z1, int32 c1, int32 tx1, int32 ty1, int32 x2, int32 y2, int32 z2, int32 c2, int32 tx2, int32 ty2, int32 x3, int32 y3, int32 z3, int32 c3, int32 tx3, int32 ty3, const STexture *t) { if (y1 > _maxYClip) y1 = _maxYClip; if (y1 < _minYClip) y1 = _minYClip; int16 yBottom = y1; int16 yTop = y1; const uint8 *texture = t->_texture; if (yBottom > y2) { if (y2 < _minYClip) y2 = _minYClip; yBottom = y2; } if (yTop < y2) { if (y2 > _maxYClip) y2 = _maxYClip; yTop = y2; } if (yBottom > y3) { if (y3 < _minYClip) y3 = _minYClip; yBottom = y3; } if (yTop < y3) { if (y3 > _maxYClip) y3 = _maxYClip; yTop = y3; } for (int16 y = yBottom; y < yTop; ++y) { _lEdge[y] = _maxXClip; _rEdge[y] = _minXClip; } // scan the edges of the triangle textureScanEdge(x1, y1, z1, c1, tx1, ty1, x2, y2, z2, c2, tx2, ty2); textureScanEdge(x2, y2, z2, c2, tx2, ty2, x3, y3, z3, c3, tx3, ty3); textureScanEdge(x3, y3, z3, c3, tx3, ty3, x1, y1, z1, c1, tx1, ty1); // Gouraud fill the horizontal scanlines for (int16 y = yBottom; y < yTop; ++y) { int32 el = _lEdge[y]; if (el < _minXClip) el = _minXClip; int32 er = _rEdge[y]; if (er > _maxXClip) er = _maxXClip; // edge right - edge left int16 dx = er - el; if (dx > 0) { // color of left edge of horizontal scanline int32 cl = _lColor[y]; // slope dc/_dx int32 mc = ((int16)(_rColor[y] - cl) << 8) / dx; // zbuffer of left edge of horizontal scanline int32 zl = _lZ[y]; // slope _dz/_dx int32 mz = ((int32)(_rZ[y] - zl) << 16) / dx; // texture x of left edge of horizontal scanline int32 olx = _lTextX[y]; // slope dty/_dx int32 mtx = ((int32)(_rTextX[y] - olx) << 16) / dx; // texture y of left edge of horizontal scanline int32 oly = _lTextY[y]; // slope dty/_dx int32 mty = ((int32)(_rTextY[y] - oly) << 16) / dx; // pointer to zbuffer int16 *z = _zBuffer + (y - _zBufStartY) * _zBufWid + (el - _zBufStartX); uint16 x = el; zl <<= 16; cl <<= 8; olx <<= 16; oly <<= 16; // loop through every pixel in horizontal scanline while (dx) { const int32 screenOffset = zl >> 16; if (*z > screenOffset) { const uint16 textureX = (uint16)(cl >> 9); const uint16 textureY = texture[(olx >> 16) + t->_dx * (oly >> 16)]; _vm->_graphicsMgr->drawTexturePixel(textureX, textureY, x, y); *z = (int16)screenOffset; } ++x; // increase screen x ++z; // increase zbuffer zl += mz; // increase the zbuffer by _dz/_dx cl += mc; // increase the color by dc/_dx olx += mtx; oly += mty; --dx; // pixel to do -- } } } } void Renderer3D::textureScanEdge(int32 x1, int32 y1, int32 z1, int32 c1, int32 tx1, int32 ty1, int32 x2, int32 y2, int32 z2, int32 c2, int32 tx2, int32 ty2) { // make sure that edge goes from top to bottom int16 dy = y2 - y1; if (dy < 0) { SWAP(y1, y2); SWAP(x1, x2); SWAP(c1, c2); SWAP(z1, z2); SWAP(tx1, tx2); SWAP(ty1, ty2); dy = -dy; } if (dy == 0) dy = 1; // initialize for stepping int32 mx = ((x2 - x1) << 16) / dy; // dx/dy int32 mz = ((z2 - z1) << 16) / dy; // dz/dy int32 mc = ((c2 - c1) << 8) / dy; // dc/dy int32 mtx = ((tx2 - tx1) << 16) / dy; int32 mty = ((ty2 - ty1) << 16) / dy; x1 <<= 16; // starting x coordinate z1 <<= 16; // starting z coordinate c1 <<= 8; // starting c color tx1 <<= 16; ty1 <<= 16; // step through edge and record color values along the way for (int32 count = y1; count < y2; ++count) { int16 x = (uint16)(x1 >> 16); if (x < _lEdge[count]) { _lEdge[count] = x; _lZ[count] = (int16)(z1 >> 16); _lTextX[count] = (uint16)(tx1 >> 16); _lTextY[count] = (uint16)(ty1 >> 16); _lColor[count] = (uint8)(c1 >> 8); } if (x > _rEdge[count]) { _rEdge[count] = x; _rZ[count] = (int16)(z1 >> 16); _rTextX[count] = (uint16)(tx1 >> 16); _rTextY[count] = (uint16)(ty1 >> 16); _rColor[count] = (uint8)(c1 >> 8); } x1 += mx; // x = x + dx/dy c1 += mc; // c = c + dc/dy z1 += mz; // z = z + dz/dy tx1 += mtx; ty1 += mty; } } void Renderer3D::shadowTriangle(int32 x1, int32 y1, int32 x2, int32 y2, int32 x3, int32 y3, uint8 cv, int32 zv) { if (y1 > _maxYClip) y1 = _maxYClip; if (y1 < _minYClip) y1 = _minYClip; int16 yBottom = y1; int16 yTop = y1; if (yBottom > y2) { if (y2 < _minYClip) y2 = _minYClip; yBottom = y2; } if (yTop < y2) { if (y2 > _maxYClip) y2 = _maxYClip; yTop = y2; } if (yBottom > y3) { if (y3 < _minYClip) y3 = _minYClip; yBottom = y3; } if (yTop < y3) { if (y3 > _maxYClip) y3 = _maxYClip; yTop = y3; } for (int16 y = yBottom; y < yTop; ++y) { _lEdge[y] = _maxXClip; _rEdge[y] = _minXClip; } // scan the edges of the triangle shadowScanEdge(x1, y1, x2, y2); shadowScanEdge(x2, y2, x3, y3); shadowScanEdge(x3, y3, x1, y1); // gouraud fill the horizontal scanlines for (int16 y = yBottom; y < yTop; ++y) { // coordinate of left edge of horizontal scanline int32 el = _lEdge[y]; if (el < _minXClip) el = _minXClip; // coordinate of right edge of horizontal scanline int32 er = _rEdge[y]; if (er > _maxXClip) er = _maxXClip; // edge right - edge left int16 dx = er - el; if (dx > 0) { // screen offset int16 x = el; int16 *zBufferPtr = _zBuffer + (y - _zBufStartY) * _zBufWid + (el - _zBufStartX); // loop through every pixel in horizontal scanline while (dx) { if (*zBufferPtr != zv) { _vm->_graphicsMgr->shadow(x, y, cv); *zBufferPtr = zv; } ++x; // increase screen x ++zBufferPtr; // increase zbuffer --dx; // pixel to do -- } } } } void Renderer3D::shadowScanEdge(int32 x1, int32 y1, int32 x2, int32 y2) { // make sure that edge goes from top to bottom int16 dy = y2 - y1; if (dy < 0) { SWAP(y1, y2); SWAP(x1, x2); dy = -dy; } if (dy == 0) dy = 1; // initialize for stepping int32 mx = ((x2 - x1) << 16) / dy; // slope dx/dy x1 <<= 16; // starting x coordinate // step through edge and record color values along the way for (int32 count = y1; count < y2; ++count) { int16 x = (int16)(x1 >> 16); if (x < _lEdge[count]) _lEdge[count] = x; if (x > _rEdge[count]) _rEdge[count] = x; x1 += mx; // x = x + dx/dy } } /** * Initialize a 3D Room */ void Renderer3D::init3DRoom() { _vm->_cx = (MAXX - 1) / 2; _vm->_cy = (MAXY - 1) / 2; for (int c = 0; c < ZBUFFERSIZE / 2; ++c) _zBuffer[c] = 0x7FFF; } void Renderer3D::resetZBuffer(Common::Rect area) { if (!area.isValidRect()) return; int size = area.width() * area.height(); if (size * 2 > ZBUFFERSIZE) warning("Warning: _zBuffer size %d!\n", size * 2); int16 *d = _zBuffer; for (int i = 0; i < size; ++i) *d++ = 0x7FFF; } /** * Change the clipping area */ void Renderer3D::setClipping(int16 x1, int16 y1, int16 x2, int16 y2) { _minXClip = x1; _minYClip = y1; _maxXClip = x2; _maxYClip = y2; } void Renderer3D::setZBufferRegion(int16 sx, int16 sy, int16 dx) { _zBufStartX = sx; _zBufStartY = sy; _zBufWid = dx; } /** * Determines whether a triangle has clockwise * or counterclockwise vertices */ int8 Renderer3D::clockWise(int16 x1, int16 y1, int16 x2, int16 y2, int16 x3, int16 y3) { x2 -= x1; y2 -= y1; x3 -= x1; y3 -= y1; int32 a1 = ((int32)x2) * y3; int32 a2 = ((int32)y2) * x3; if (a1 > a2) return 1; // clockwise if (a1 < a2) return -1; // counterclockwise a1 = ((int32)x2) * x3; a2 = ((int32)y2) * y3; if (a1 < 0 || a2 < 0) return -1; a1 = ((int32)x2) * x2 + ((int32)y2) * y2; a2 = ((int32)x3) * x3 + ((int32)y3) * y3; if (a1 < a2) return 1; return 0; } void Renderer3D::calcCharacterPoints() { Actor *actor = _vm->_actor; SCamera *camera = actor->_camera; SLight *light = actor->_light; int vertexNum = actor->_vertexNum; if (actor->_curAction > hLAST) error("Error in drawCharacter() - _curAction > hLAST"); int cfp = 0; int cur = 0; while (cur < actor->_curAction) cfp += defActionLen[cur++]; if (actor->_curAction == hWALKOUT) cfp = 1; cfp += actor->_curFrame; if (actor->_curAction == hLAST) cfp = 0; actor->_vertex = &actor->_characterArea[cfp * actor->_vertexNum]; _shadowLightNum = 0; _totalShadowVerts = 0; // camera matrix float e10 = camera->_e1[0]; float e11 = camera->_e1[1]; float e12 = camera->_e1[2]; float e20 = camera->_e2[0]; float e21 = camera->_e2[1]; float e22 = camera->_e2[2]; float e30 = camera->_e3[0]; float e31 = camera->_e3[1]; float e32 = camera->_e3[2]; // Light directions float l0 = 0.0f; float l1 = 0.0f; float l2 = 0.0f; actor->_area[0] = 32000; actor->_area[1] = -32000; actor->_area[2] = 32000; actor->_area[3] = -32000; actor->_area[4] = 32000; actor->_area[5] = -32000; float t = (actor->_theta * M_PI * 2) / 360.0; float cost = cos(t); float sint = sin(t); // Put all vertices in dark color for (int i = 0; i < MAXVERTEX; ++i) _vVertex[i]._angle = 180; float dist; float tx = 0.0f; float ty = 0.0f; float tz = 0.0f; float pa0, pa1, pa2; for (uint32 i = 0; i < actor->_lightNum; ++i) { // if off lint == 0 // if it has a shadow lint & 0x80 int lint = light->_inten & 0x7F; if (lint) { // if it's not turned off tx = light->_x - actor->_px - actor->_dx; // computes direction vector tz = light->_z - actor->_pz - actor->_dz; // between light and actor ty = light->_y; if (light->_position) { // if it's attenuated dist = sqrt(tx * tx + ty * ty + tz * tz); // Distance light <--> actor // adjust light intensity due to the distance if (_vm->floatComp(dist, light->_outr) == 1) // if it's out of range it's off lint = 0; else if (_vm->floatComp(dist, light->_inr) == 1) // if it's inside the circle it's decreased lint = (int)((float)lint * (light->_outr - dist) / (light->_outr - light->_inr)); } } if (lint) { // If it's still on // Light rotates around the actor l0 = tx * cost - tz * sint; l2 = tx * sint + tz * cost; l1 = ty; t = sqrt(l0 * l0 + l1 * l1 + l2 * l2); l0 /= t; l1 /= t; l2 /= t; // Adjust light intensity according to the spot tx = (float)light->_fallOff; if (light->_fallOff) { // for light spot only ty = (float)light->_hotspot; pa0 = light->_dx * cost - light->_dz * sint; pa1 = light->_dy; pa2 = light->_dx * sint + light->_dz * cost; t = sqrt(pa0 * pa0 + pa1 * pa1 + pa2 * pa2); pa0 /= t; pa1 /= t; pa2 /= t; tz = acos((pa0 * l0) + (pa1 * l1) + (pa2 * l2)) * 360.0 / (M_PI * 2); tz = CLIP(tz, 0.f, 180.f); // tx falloff // ty hotspot // tz current angle _shadowIntens[_shadowLightNum] = SHADOWAMBIENT; if (_vm->floatComp(tz, tx) == 1) { // tz > tx - if it's out of the falloff lint = 0; _shadowIntens[_shadowLightNum] = 0; } else if (_vm->floatComp(tz, ty) == 1) { // tz > ty - if it's between the falloff and the hotspot lint = (int)((float)lint * (tx - tz) / (tx - ty)); _shadowIntens[_shadowLightNum] = (int)((float)_shadowIntens[_shadowLightNum] * (tx - tz) / (tx - ty)); } } } if ((light->_inten & 0x80) && lint) { // if it's shadowed and still on // casts shadow vertices for (int j = 0; j < SHADOWVERTSNUM; ++j) { pa0 = actor->_vertex[_shadowVerts[j]]._x; pa1 = actor->_vertex[_shadowVerts[j]]._y; pa2 = actor->_vertex[_shadowVerts[j]]._z; _shVertex[vertexNum + _totalShadowVerts + j]._x = pa0 - (pa1 * l0); _shVertex[vertexNum + _totalShadowVerts + j]._z = pa2 - (pa1 * l2); _shVertex[vertexNum + _totalShadowVerts + j]._y = 0; } // per default all shadows are equally faint // _shadowIntens[_shadowLightNum] = SHADOWAMBIENT; ++_shadowLightNum; _totalShadowVerts += SHADOWVERTSNUM; } if (lint) { // if still on // adapts the light vector o its intensity t = (float)(lint) / 127.0; l0 = l0 * t; l1 = l1 * t; l2 = l2 * t; SVertex *curVertex = actor->_vertex; for (int j = 0; j < vertexNum; ++j) { pa0 = curVertex->_nx; pa1 = curVertex->_ny; pa2 = curVertex->_nz; lint = (int)((acos(pa0 * l0 + pa1 * l1 + pa2 * l2) * 360.0) / M_PI); lint = CLIP(lint, 0, 180); _vVertex[j]._angle -= (180 - lint); ++curVertex; } } ++light; } // rearranged light values so they can be viewed for (int i = 0; i < vertexNum; ++i) _vVertex[i]._angle = CLIP(_vVertex[i]._angle, 0, 180); // Calculate the distance of the character from the room tx = camera->_ex - actor->_px; ty = camera->_ey; tz = camera->_ez - actor->_pz; dist = tx * e30 + ty * e31 + tz * e32; SVertex *curVertex = actor->_vertex; for (int i = 0; i < vertexNum + _totalShadowVerts; ++i) { if (i < vertexNum) { l0 = curVertex->_x; l1 = curVertex->_z; pa1 = ty - curVertex->_y; } else { l0 = _shVertex[i]._x; l1 = _shVertex[i]._z; pa1 = ty - _shVertex[i]._y; } pa0 = tx - (l0 * cost + l1 * sint); // rotate _curVertex pa2 = tz - (-l0 * sint + l1 * cost); l0 = pa0 * e10 + pa1 * e11 + pa2 * e12; // project _curVertex l1 = pa0 * e20 + pa1 * e21 + pa2 * e22; l2 = pa0 * e30 + pa1 * e31 + pa2 * e32; int x2d = _vm->_cx + (int)((l0 * camera->_fovX) / l2); int y2d = _vm->_cy + (int)((l1 * camera->_fovY) / l2); _vVertex[i]._x = x2d; _vVertex[i]._y = y2d; _vVertex[i]._z = (int32)((dist - l2) * 128.0); actor->_area[0] = MIN(x2d, actor->_area[0]); actor->_area[1] = MAX(x2d, actor->_area[1]); actor->_area[2] = MIN(y2d, actor->_area[2]); actor->_area[3] = MAX(y2d, actor->_area[3]); actor->_area[4] = MIN(_vVertex[i]._z, actor->_area[4]); actor->_area[5] = MAX(_vVertex[i]._z, actor->_area[5]); ++curVertex; } actor->_area[4] = (int)dist; actor->_area[5] = (int)dist; // vertex clipping if (actor->_area[0] <= _minXClip + 1) { actor->_area[0] = _minXClip; } else { --actor->_area[0]; } if (actor->_area[1] >= _maxXClip - 1) { actor->_area[1] = _maxXClip; } else { ++actor->_area[1]; } if (actor->_area[2] <= _minYClip + 1) { actor->_area[2] = _minYClip; } else { --actor->_area[2]; } if (actor->_area[3] >= _maxYClip - 1) { actor->_area[3] = _maxYClip; } else { ++actor->_area[3]; } if (actor->_curAction == hLAST) // exit displacer actor->_area[2] = actor->_area[3] - (((actor->_area[3] - actor->_area[2]) * actor->_curFrame) / defActionLen[hLAST]); // set zbuffer vars setZBufferRegion(actor->_area[0], actor->_area[2], actor->_area[1] - actor->_area[0]); } void Renderer3D::drawCharacterFaces() { Actor *actor = _vm->_actor; STexture *textures = actor->_textures; SFace *face = actor->_face; int vertexNum = actor->_vertexNum; if (actor->_curAction == hLAST) setClipping(0, actor->_area[2], MAXX, actor->_area[3]); for (int i = 0; i < _shadowLightNum; ++i) { for (int j = 0; j < SHADOWFACESNUM; ++j) { int p0 = _shadowFaces[j][0] + vertexNum + i * SHADOWVERTSNUM; int p1 = _shadowFaces[j][1] + vertexNum + i * SHADOWVERTSNUM; int p2 = _shadowFaces[j][2] + vertexNum + i * SHADOWVERTSNUM; int px0 = _vVertex[p0]._x; int py0 = _vVertex[p0]._y; int px1 = _vVertex[p1]._x; int py1 = _vVertex[p1]._y; int px2 = _vVertex[p2]._x; int py2 = _vVertex[p2]._y; shadowTriangle(px0, py0, px1, py1, px2, py2, 127 - _shadowIntens[i], (int16)(0x7FF0 + i)); } } for (uint i = 0; i < actor->_faceNum; ++i) { int p0 = face->_a; int p1 = face->_b; int p2 = face->_c; int px0 = _vVertex[p0]._x; int py0 = _vVertex[p0]._y; int px1 = _vVertex[p1]._x; int py1 = _vVertex[p1]._y; int px2 = _vVertex[p2]._x; int py2 = _vVertex[p2]._y; if (clockWise(px0, py0, px1, py1, px2, py2) > 0) { uint16 textureId = face->_mat; if (textureId < MAXMAT && textures[textureId].isActive()) { textureTriangle(px0, py0, _vVertex[p0]._z, _vVertex[p0]._angle, actor->_textureCoord[i][0][0], actor->_textureCoord[i][0][1], px1, py1, _vVertex[p1]._z, _vVertex[p1]._angle, actor->_textureCoord[i][1][0], actor->_textureCoord[i][1][1], px2, py2, _vVertex[p2]._z, _vVertex[p2]._angle, actor->_textureCoord[i][2][0], actor->_textureCoord[i][2][1], &textures[textureId]); } } ++face; } int p0 = 0; for (int i = _zBufStartY; i < actor->_area[3]; ++i) { for (int j = 1; j < _zBufWid; ++j) { int py1 = (_zBuffer[p0] >= 0x7FF0) * 0x8000; int py2 = (_zBuffer[p0 + 1] >= 0x7FF0) * 0x8000; int p1 = _zBuffer[p0] < 0x7FFF; int p2 = _zBuffer[p0 + 1] < 0x7FFF; if (p1 != p2) { _vm->_graphicsMgr->pixelAliasing(j + _zBufStartX, i); // if the first is the character if (p1) _zBuffer[p0] = 0x00BF | py1; else _zBuffer[p0] = 0x003F | py2; if (j + 1 < _zBufWid) { ++p0; ++j; // if the second is the character if (p2) _zBuffer[p0] = 0x00BF | py2; else _zBuffer[p0] = 0x003F | py1; } } else { // set value alpha max if (p1) _zBuffer[p0] = 0x00FF | py1; else _zBuffer[p0] = 0x0000 | py1; } ++p0; // if it's the last of the line if (j == _zBufWid - 1) { if (p2) _zBuffer[p0] = 0x00FF | py2; else _zBuffer[p0] = 0x0000 | py2; } } ++p0; } if (actor->_curAction == hLAST) setClipping(0, TOP, MAXX, AREA + TOP); } /** * Draw the character */ void Renderer3D::drawCharacter(uint8 flag) { if (!_vm->_flagShowCharacter) return; // Compute pointer to frame if (flag & CALCPOINTS) calcCharacterPoints(); if (flag & DRAWFACES) drawCharacterFaces(); } } // End of namespace Trecision