scummvm/engines/twine/renderer/renderer.cpp

1484 lines
44 KiB
C++

/* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "twine/renderer/renderer.h"
#include "common/memstream.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "twine/menu/interface.h"
#include "twine/menu/menu.h"
#include "twine/renderer/redraw.h"
#include "twine/renderer/shadeangletab.h"
#include "twine/scene/actor.h"
#include "twine/scene/movements.h"
#include "twine/scene/grid.h"
#include "twine/shared.h"
#include "twine/twine.h"
namespace TwinE {
#define RENDERTYPE_DRAWLINE 0
#define RENDERTYPE_DRAWPOLYGON 1
#define RENDERTYPE_DRAWSPHERE 2
Renderer::Renderer(TwinEEngine *engine) : _engine(engine), shadeAngleTab3(&shadeAngleTable[384]) {
}
Renderer::~Renderer() {
free(_polyTab);
free(_polyTab2);
}
void Renderer::init(int32 w, int32 h) {
_polyTabSize = _engine->height() * 2;
_polyTab = (int16*)malloc(_polyTabSize * sizeof(int16));
_polyTab2 = (int16*)malloc(_polyTabSize * sizeof(int16));
}
int32 Renderer::projectPositionOnScreen(int32 cX, int32 cY, int32 cZ) {
if (isUsingOrthoProjection) {
projPosX = ((cX - cZ) * 24) / BRICK_SIZE + orthoProjX;
projPosY = (((cX + cZ) * 12) - cY * 30) / BRICK_SIZE + orthoProjY;
projPosZ = cZ - cY - cX;
return 1;
}
cX -= baseRotPosX;
cY -= baseRotPosY;
cZ -= baseRotPosZ;
if (cZ < 0) {
projPosX = 0;
projPosY = 0;
projPosZ = 0;
return 0;
}
int32 posZ = cZ + cameraDepthOffset;
if (posZ < 0) {
posZ = 0x7FFF;
}
projPosX = (cX * cameraScaleY) / posZ + orthoProjX;
projPosY = (-cY * cameraScaleZ) / posZ + orthoProjY;
projPosZ = posZ;
return -1;
}
void Renderer::setCameraPosition(int32 x, int32 y, int32 depthOffset, int32 scaleY, int32 scaleZ) {
orthoProjX = x;
orthoProjY = y;
cameraDepthOffset = depthOffset;
cameraScaleY = scaleY;
cameraScaleZ = scaleZ;
isUsingOrthoProjection = false;
}
void Renderer::setBaseTranslation(int32 x, int32 y, int32 z) {
baseTransPosX = x;
baseTransPosY = y;
baseTransPosZ = z;
}
void Renderer::setOrthoProjection(int32 x, int32 y, int32 z) {
orthoProjX = x;
orthoProjY = y;
orthoProjZ = z;
isUsingOrthoProjection = true;
}
void Renderer::baseMatrixTranspose() {
const Matrix tmpMatrix = baseMatrix;
baseMatrix.row1[0] = tmpMatrix.row1[0];
baseMatrix.row1[1] = tmpMatrix.row2[0];
baseMatrix.row1[2] = tmpMatrix.row3[0];
baseMatrix.row2[0] = tmpMatrix.row1[1];
baseMatrix.row2[1] = tmpMatrix.row2[1];
baseMatrix.row2[2] = tmpMatrix.row3[1];
baseMatrix.row3[0] = tmpMatrix.row1[2];
baseMatrix.row3[1] = tmpMatrix.row2[2];
baseMatrix.row3[2] = tmpMatrix.row3[2];
}
void Renderer::setBaseRotation(int32 x, int32 y, int32 z, bool transpose) {
const double Xradians = (double)((ANGLE_90 - x) % ANGLE_360) * 2 * M_PI / ANGLE_360;
const double Yradians = (double)((ANGLE_90 - y) % ANGLE_360) * 2 * M_PI / ANGLE_360;
const double Zradians = (double)((ANGLE_90 - z) % ANGLE_360) * 2 * M_PI / ANGLE_360;
baseMatrix.row1[0] = (int32)(sin(Zradians) * sin(Yradians) * SCENE_SIZE_HALFF);
baseMatrix.row1[1] = (int32)(-cos(Zradians) * SCENE_SIZE_HALFF);
baseMatrix.row1[2] = (int32)(sin(Zradians) * cos(Yradians) * SCENE_SIZE_HALFF);
baseMatrix.row2[0] = (int32)(cos(Zradians) * sin(Xradians) * SCENE_SIZE_HALFF);
baseMatrix.row2[1] = (int32)(sin(Zradians) * sin(Xradians) * SCENE_SIZE_HALFF);
baseMatrix.row3[0] = (int32)(cos(Zradians) * cos(Xradians) * SCENE_SIZE_HALFF);
baseMatrix.row3[1] = (int32)(sin(Zradians) * cos(Xradians) * SCENE_SIZE_HALFF);
int32 matrixElem = baseMatrix.row2[0];
baseMatrix.row2[0] = (int32)(sin(Yradians) * matrixElem + SCENE_SIZE_HALFF * cos(Yradians) * cos(Xradians));
baseMatrix.row2[2] = (int32)(cos(Yradians) * matrixElem - SCENE_SIZE_HALFF * sin(Yradians) * cos(Xradians));
matrixElem = baseMatrix.row3[0];
baseMatrix.row3[0] = (int32)(sin(Yradians) * matrixElem - SCENE_SIZE_HALFF * sin(Xradians) * cos(Yradians));
baseMatrix.row3[2] = (int32)(cos(Yradians) * matrixElem + SCENE_SIZE_HALFF * sin(Xradians) * sin(Yradians));
if (transpose) {
baseMatrixTranspose();
}
getBaseRotationPosition(baseTransPosX, baseTransPosY, baseTransPosZ);
baseRotPosX = destX;
baseRotPosY = destY;
baseRotPosZ = destZ;
}
void Renderer::getBaseRotationPosition(int32 x, int32 y, int32 z) {
destX = (baseMatrix.row1[0] * x + baseMatrix.row1[1] * y + baseMatrix.row1[2] * z) / SCENE_SIZE_HALF;
destY = (baseMatrix.row2[0] * x + baseMatrix.row2[1] * y + baseMatrix.row2[2] * z) / SCENE_SIZE_HALF;
destZ = (baseMatrix.row3[0] * x + baseMatrix.row3[1] * y + baseMatrix.row3[2] * z) / SCENE_SIZE_HALF;
}
void Renderer::getCameraAnglePositions(int32 x, int32 y, int32 z) {
destX = (baseMatrix.row1[0] * x + baseMatrix.row2[0] * y + baseMatrix.row3[0] * z) / SCENE_SIZE_HALF;
destY = (baseMatrix.row1[1] * x + baseMatrix.row2[1] * y + baseMatrix.row3[1] * z) / SCENE_SIZE_HALF;
destZ = (baseMatrix.row1[2] * x + baseMatrix.row2[2] * y + baseMatrix.row3[2] * z) / SCENE_SIZE_HALF;
}
void Renderer::translateGroup(int32 x, int32 y, int32 z) {
destX = (shadeMatrix.row1[0] * x + shadeMatrix.row1[1] * y + shadeMatrix.row1[2] * z) / SCENE_SIZE_HALF;
destY = (shadeMatrix.row2[0] * x + shadeMatrix.row2[1] * y + shadeMatrix.row2[2] * z) / SCENE_SIZE_HALF;
destZ = destY;
}
void Renderer::setCameraAngle(int32 transPosX, int32 transPosY, int32 transPosZ, int32 rotPosX, int32 rotPosY, int32 rotPosZ, int32 param6) {
baseTransPosX = transPosX;
baseTransPosY = transPosY;
baseTransPosZ = transPosZ;
setBaseRotation(rotPosX, rotPosY, rotPosZ);
baseRotPosZ += param6;
getCameraAnglePositions(baseRotPosX, baseRotPosY, baseRotPosZ);
baseTransPosX = destX;
baseTransPosY = destY;
baseTransPosZ = destZ;
}
void Renderer::applyRotation(Matrix *targetMatrix, const Matrix *currentMatrix) {
Matrix matrix1;
Matrix matrix2;
if (renderAngleX) {
int32 angle = renderAngleX;
int32 angleVar2 = shadeAngleTable[ClampAngle(angle)];
angle += ANGLE_90;
int32 angleVar1 = shadeAngleTable[ClampAngle(angle)];
matrix1.row1[0] = currentMatrix->row1[0];
matrix1.row2[0] = currentMatrix->row2[0];
matrix1.row3[0] = currentMatrix->row3[0];
matrix1.row1[1] = (currentMatrix->row1[2] * angleVar2 + currentMatrix->row1[1] * angleVar1) / SCENE_SIZE_HALF;
matrix1.row1[2] = (currentMatrix->row1[2] * angleVar1 - currentMatrix->row1[1] * angleVar2) / SCENE_SIZE_HALF;
matrix1.row2[1] = (currentMatrix->row2[2] * angleVar2 + currentMatrix->row2[1] * angleVar1) / SCENE_SIZE_HALF;
matrix1.row2[2] = (currentMatrix->row2[2] * angleVar1 - currentMatrix->row2[1] * angleVar2) / SCENE_SIZE_HALF;
matrix1.row3[1] = (currentMatrix->row3[2] * angleVar2 + currentMatrix->row3[1] * angleVar1) / SCENE_SIZE_HALF;
matrix1.row3[2] = (currentMatrix->row3[2] * angleVar1 - currentMatrix->row3[1] * angleVar2) / SCENE_SIZE_HALF;
} else {
matrix1 = *currentMatrix;
}
if (renderAngleZ) {
int32 angle = renderAngleZ;
int32 angleVar2 = shadeAngleTable[ClampAngle(angle)];
angle += ANGLE_90;
int32 angleVar1 = shadeAngleTable[ClampAngle(angle)];
matrix2.row1[2] = matrix1.row1[2];
matrix2.row2[2] = matrix1.row2[2];
matrix2.row3[2] = matrix1.row3[2];
matrix2.row1[0] = (matrix1.row1[1] * angleVar2 + matrix1.row1[0] * angleVar1) / SCENE_SIZE_HALF;
matrix2.row1[1] = (matrix1.row1[1] * angleVar1 - matrix1.row1[0] * angleVar2) / SCENE_SIZE_HALF;
matrix2.row2[0] = (matrix1.row2[1] * angleVar2 + matrix1.row2[0] * angleVar1) / SCENE_SIZE_HALF;
matrix2.row2[1] = (matrix1.row2[1] * angleVar1 - matrix1.row2[0] * angleVar2) / SCENE_SIZE_HALF;
matrix2.row3[0] = (matrix1.row3[1] * angleVar2 + matrix1.row3[0] * angleVar1) / SCENE_SIZE_HALF;
matrix2.row3[1] = (matrix1.row3[1] * angleVar1 - matrix1.row3[0] * angleVar2) / SCENE_SIZE_HALF;
} else {
matrix2 = matrix1;
}
if (renderAngleY) {
int32 angle = renderAngleY;
int32 angleVar2 = shadeAngleTable[ClampAngle(angle)];
angle += ANGLE_90;
int32 angleVar1 = shadeAngleTable[ClampAngle(angle)];
targetMatrix->row1[1] = matrix2.row1[1];
targetMatrix->row2[1] = matrix2.row2[1];
targetMatrix->row3[1] = matrix2.row3[1];
targetMatrix->row1[0] = (matrix2.row1[0] * angleVar1 - matrix2.row1[2] * angleVar2) / SCENE_SIZE_HALF;
targetMatrix->row1[2] = (matrix2.row1[0] * angleVar2 + matrix2.row1[2] * angleVar1) / SCENE_SIZE_HALF;
targetMatrix->row2[0] = (matrix2.row2[0] * angleVar1 - matrix2.row2[2] * angleVar2) / SCENE_SIZE_HALF;
targetMatrix->row2[2] = (matrix2.row2[0] * angleVar2 + matrix2.row2[2] * angleVar1) / SCENE_SIZE_HALF;
targetMatrix->row3[0] = (matrix2.row3[0] * angleVar1 - matrix2.row3[2] * angleVar2) / SCENE_SIZE_HALF;
targetMatrix->row3[2] = (matrix2.row3[0] * angleVar2 + matrix2.row3[2] * angleVar1) / SCENE_SIZE_HALF;
} else {
*targetMatrix = matrix2;
}
}
void Renderer::applyPointsRotation(const pointTab *pointsPtr, int32 numPoints, pointTab *destPoints, const Matrix *rotationMatrix) {
int32 numOfPoints2 = numPoints;
do {
const int32 tmpX = pointsPtr->x;
const int32 tmpY = pointsPtr->y;
const int32 tmpZ = pointsPtr->z;
destPoints->x = ((rotationMatrix->row1[0] * tmpX + rotationMatrix->row1[1] * tmpY + rotationMatrix->row1[2] * tmpZ) / SCENE_SIZE_HALF) + destX;
destPoints->y = ((rotationMatrix->row2[0] * tmpX + rotationMatrix->row2[1] * tmpY + rotationMatrix->row2[2] * tmpZ) / SCENE_SIZE_HALF) + destY;
destPoints->z = ((rotationMatrix->row3[0] * tmpX + rotationMatrix->row3[1] * tmpY + rotationMatrix->row3[2] * tmpZ) / SCENE_SIZE_HALF) + destZ;
destPoints++;
pointsPtr++;
} while (--numOfPoints2);
}
void Renderer::processRotatedElement(Matrix *targetMatrix, const pointTab *pointsPtr, int32 rotZ, int32 rotY, int32 rotX, const elementEntry *elemPtr, ModelData *modelData) {
int32 firstPoint = elemPtr->firstPoint / sizeof(pointTab);
int32 numOfPoints2 = elemPtr->numOfPoints;
renderAngleX = rotX;
renderAngleY = rotY;
renderAngleZ = rotZ;
const Matrix *currentMatrix;
// if its the first point
if (elemPtr->baseElement == -1) {
currentMatrix = &baseMatrix;
destX = 0;
destY = 0;
destZ = 0;
} else {
const int32 pointIdx = elemPtr->basePoint / sizeof(pointTab);
const int32 matrixIndex = elemPtr->baseElement;
assert(matrixIndex >= 0 && matrixIndex < ARRAYSIZE(matricesTable));
currentMatrix = &matricesTable[matrixIndex];
destX = modelData->computedPoints[pointIdx].x;
destY = modelData->computedPoints[pointIdx].y;
destZ = modelData->computedPoints[pointIdx].z;
}
applyRotation(targetMatrix, currentMatrix);
if (!numOfPoints2) {
warning("RENDER WARNING: No points in this model!");
}
applyPointsRotation(&pointsPtr[firstPoint], numOfPoints2, &modelData->computedPoints[firstPoint], targetMatrix);
}
void Renderer::applyPointsTranslation(const pointTab *pointsPtr, int32 numPoints, pointTab *destPoints, const Matrix *translationMatrix) {
int32 numOfPoints2 = numPoints;
do {
const int32 tmpX = pointsPtr->x + renderAngleZ;
const int32 tmpY = pointsPtr->y + renderAngleY;
const int32 tmpZ = pointsPtr->z + renderAngleX;
destPoints->x = ((translationMatrix->row1[0] * tmpX + translationMatrix->row1[1] * tmpY + translationMatrix->row1[2] * tmpZ) / SCENE_SIZE_HALF) + destX;
destPoints->y = ((translationMatrix->row2[0] * tmpX + translationMatrix->row2[1] * tmpY + translationMatrix->row2[2] * tmpZ) / SCENE_SIZE_HALF) + destY;
destPoints->z = ((translationMatrix->row3[0] * tmpX + translationMatrix->row3[1] * tmpY + translationMatrix->row3[2] * tmpZ) / SCENE_SIZE_HALF) + destZ;
destPoints++;
pointsPtr++;
} while (--numOfPoints2);
}
void Renderer::processTranslatedElement(Matrix *targetMatrix, const pointTab *pointsPtr, int32 rotX, int32 rotY, int32 rotZ, const elementEntry *elemPtr, ModelData *modelData) {
renderAngleX = rotX;
renderAngleY = rotY;
renderAngleZ = rotZ;
if (elemPtr->baseElement == -1) { // base point
destX = 0;
destY = 0;
destZ = 0;
*targetMatrix = baseMatrix;
} else { // dependent
const int pointsIdx = elemPtr->basePoint / 6;
destX = modelData->computedPoints[pointsIdx].x;
destY = modelData->computedPoints[pointsIdx].y;
destZ = modelData->computedPoints[pointsIdx].z;
const int32 matrixIndex = elemPtr->baseElement;
assert(matrixIndex >= 0 && matrixIndex < ARRAYSIZE(matricesTable));
*targetMatrix = matricesTable[matrixIndex];
}
applyPointsTranslation(&pointsPtr[elemPtr->firstPoint / sizeof(pointTab)], elemPtr->numOfPoints, &modelData->computedPoints[elemPtr->firstPoint / sizeof(pointTab)], targetMatrix);
}
void Renderer::setLightVector(int32 angleX, int32 angleY, int32 angleZ) {
// TODO: RECHECK THIS
/*_cameraAngleX = angleX;
_cameraAngleY = angleY;
_cameraAngleZ = angleZ;*/
renderAngleX = angleX;
renderAngleY = angleY;
renderAngleZ = angleZ;
applyRotation(&shadeMatrix, &baseMatrix);
translateGroup(0, 0, 59);
lightPos.x = destX;
lightPos.y = destY;
lightPos.z = destZ;
}
FORCEINLINE int16 clamp(int16 x, int16 a, int16 b) {
return x < a ? a : (x > b ? b : x);
}
void Renderer::computeBoundingBox(Vertex *vertices, int32 numVertices, int &vleft, int &vright, int &vtop, int &vbottom) const {
vleft = vtop = SCENE_SIZE_MAX;
vright = vbottom = SCENE_SIZE_MIN;
for (int32 i = 0; i < numVertices; i++) {
vertices[i].x = clamp(vertices[i].x, 0, _engine->width() - 1);
vertices[i].y = clamp(vertices[i].y, 0, _engine->height() - 1);
const int vertexX = vertices[i].x;
vleft = MIN(vleft, vertexX);
vright = MAX(vright, vertexX);
const int vertexY = vertices[i].y;
vtop = MIN(vtop, vertexY);
vbottom = MAX(vbottom, vertexY);
}
}
void Renderer::computePolygons(int16 polyRenderType, Vertex *vertices, int32 numVertices, int &vleft, int &vright, int &vtop, int &vbottom) {
if (numVertices <= 0) {
return;
}
computeBoundingBox(vertices, numVertices, vleft, vright, vtop, vbottom);
uint8 vertexParam1 = vertices[numVertices - 1].colorIndex;
int16 currentVertexX = vertices[numVertices - 1].x;
int16 currentVertexY = vertices[numVertices - 1].y;
for (int32 nVertex = 0; nVertex < numVertices; nVertex++) {
const int16 oldVertexY = currentVertexY;
const int16 oldVertexX = currentVertexX;
const uint8 oldVertexParam = vertexParam1;
vertexParam1 = vertices[nVertex].colorIndex;
const uint8 vertexParam2 = vertexParam1;
currentVertexX = vertices[nVertex].x;
currentVertexY = vertices[nVertex].y;
// drawLine(oldVertexX,oldVertexY,currentVertexX,currentVertexY,255);
if (currentVertexY == oldVertexY) {
continue;
}
const int8 up = currentVertexY < oldVertexY;
int8 direction = up ? -1 : 1;
const int16 vsize = ABS(currentVertexY - oldVertexY);
const int16 hsize = ABS(currentVertexX - oldVertexX);
int16 cvalue;
int16 cdelta;
int16 ypos;
float xpos;
if (direction * oldVertexX > direction * currentVertexX) { // if we are going up right
xpos = currentVertexX;
ypos = currentVertexY;
cvalue = (vertexParam2 * 256) + ((oldVertexParam - vertexParam2) * 256) % vsize;
cdelta = ((oldVertexParam - vertexParam2) * 256) / vsize;
direction = -direction; // we will draw by going down the tab
} else {
xpos = oldVertexX;
ypos = oldVertexY;
cvalue = (oldVertexParam * 256) + ((vertexParam2 - oldVertexParam) * 256) % vsize;
cdelta = ((vertexParam2 - oldVertexParam) * 256) / vsize;
}
const int32 polyTabIndex = ypos + (up ? _engine->height() : 0);
int16 *outPtr = &_polyTab[polyTabIndex]; // outPtr is the output ptr in the renderTab
float slope = (float)hsize / (float)vsize;
slope = up ? -slope : slope;
for (int32 i = 0; i < vsize + 2; i++) {
if (outPtr - _polyTab < _polyTabSize) {
if (outPtr - _polyTab > 0) {
*outPtr = xpos;
}
}
outPtr += direction;
xpos += slope;
}
if (polyRenderType >= POLYGONTYPE_GOURAUD) { // we must compute the color progression
int16 *outPtr2 = &_polyTab2[polyTabIndex];
for (int32 i = 0; i < vsize + 2; i++) {
if (outPtr2 - _polyTab2 < _polyTabSize) {
if (outPtr2 - _polyTab2 > 0) {
*outPtr2 = cvalue;
}
}
outPtr2 += direction;
cvalue += cdelta;
}
}
}
}
void Renderer::renderPolygonsCopper(uint8 *out, int vtop, int32 vsize, int32 color) const {
const int16 *ptr1 = &_polyTab[vtop];
int32 currentLine = vtop;
do {
if (currentLine >= 0 && currentLine < _engine->height()) {
int16 start = ptr1[0];
int16 stop = ptr1[_engine->height()];
ptr1++;
int32 hsize = stop - start;
if (hsize >= 0) {
uint16 mask = 0x43DB;
uint16 dx = (uint8)color;
dx |= 0x300;
hsize++;
const int32 startCopy = start;
for (int32 j = startCopy; j < hsize + startCopy; j++) {
start += mask;
start = (start & 0xFF00) | ((start & 0xFF) & (uint8)(dx / 256));
start = (start & 0xFF00) | ((start & 0xFF) + (dx & 0xFF));
if (j >= 0 && j < _engine->width()) {
out[j] = start & 0xFF;
}
mask = (mask * 4) | (mask / SCENE_SIZE_HALF);
mask++;
}
}
}
out += _engine->width();
currentLine++;
} while (--vsize);
}
void Renderer::renderPolygonsBopper(uint8 *out, int vtop, int32 vsize, int32 color) const {
const int16 *ptr1 = &_polyTab[vtop];
int32 currentLine = vtop;
do {
if (currentLine >= 0 && currentLine < _engine->height()) {
int16 start = ptr1[0];
int16 stop = ptr1[_engine->height()];
ptr1++;
int32 hsize = stop - start;
if (hsize >= 0) {
hsize++;
for (int32 j = start; j < hsize + start; j++) {
if ((start + (vtop % 1)) & 1) {
if (j >= 0 && j < _engine->width()) {
out[j] = color;
}
}
}
}
}
out += _engine->width();
currentLine++;
} while (--vsize);
}
void Renderer::renderPolygonsFlat(uint8 *out, int vtop, int32 vsize, int32 color) const {
const int16 *ptr1 = &_polyTab[vtop];
int32 currentLine = vtop;
do {
if (currentLine >= 0 && currentLine < _engine->height()) {
int16 start = ptr1[0];
int16 stop = ptr1[_engine->height()];
ptr1++;
int32 hsize = stop - start;
if (hsize >= 0) {
hsize++;
for (int32 j = start; j < hsize + start; j++) {
if (j >= 0 && j < _engine->width()) {
out[j] = color;
}
}
}
}
out += _engine->width();
currentLine++;
} while (--vsize);
}
void Renderer::renderPolygonsTele(uint8 *out, int vtop, int32 vsize, int32 color) const {
const int16 *ptr1 = &_polyTab[vtop];
int bx = (uint16)color << 16;
int32 renderLoop = vsize;
do {
int16 start;
int16 stop;
int32 hsize;
while (1) {
start = ptr1[0];
stop = ptr1[_engine->height()];
ptr1++;
hsize = stop - start;
if (hsize) {
break;
}
uint8 *out2 = start + out;
*out2 = ((unsigned short)(bx / 24)) & 0x0F;
color = *(out2 + 1);
out += _engine->width();
--renderLoop;
if (!renderLoop) {
return;
}
}
if (stop >= start) {
hsize++;
bx = (unsigned short)(color / 16);
uint8 *out2 = start + out;
int ax = (bx & 0xF0) * 256;
bx = bx * 256;
ax += (bx & 0x0F);
ax -= bx;
ax++;
ax = ax >> 16;
ax = ax / hsize;
uint16 temp = (ax & 0xF0);
temp = temp / 256;
temp += (ax & 0x0F);
ax = temp;
uint16 dx = ax;
ax = (ax & 0x0F) + (bx & 0xF0);
hsize++;
if (hsize & 1) {
ax = 0; // not sure about this
}
int32 j = hsize >> 1;
while (1) {
*(out2++) = ax & 0x0F;
ax += dx;
--j;
if (!j) {
break;
}
*(out2++) = ax & 0x0F;
ax += dx;
}
}
out += _engine->width();
--renderLoop;
} while (renderLoop);
}
// FIXME: buggy
void Renderer::renderPolygonsTras(uint8 *out, int vtop, int32 vsize, int32 color) const {
const int16 *ptr1 = &_polyTab[vtop];
do {
unsigned short int bx;
int16 start = ptr1[0];
int16 stop = ptr1[_engine->height()];
ptr1++;
int32 hsize = stop - start;
if (hsize >= 0) {
hsize++;
uint8 *out2 = start + out;
if (hsize / 2 < 0) {
bx = color & 0xFF;
bx = bx * 256;
bx += color & 0xFF;
for (int32 j = 0; j < hsize; j++) {
*(out2) = (*(out2)&0x0F0F) | bx;
}
} else {
*(out2) = (*(out2)&0x0F) | color;
out2++;
}
}
out += _engine->width();
} while (--vsize);
}
// FIXME: buggy
void Renderer::renderPolygonTrame(uint8 *out, int vtop, int32 vsize, int32 color) const {
const int16 *ptr1 = &_polyTab[vtop];
unsigned char bh = 0;
int32 currentLine = vtop;
do {
if (currentLine >= 0 && currentLine < _engine->height()) {
int16 start = ptr1[0];
int16 stop = ptr1[_engine->height()];
ptr1++;
int32 hsize = stop - start;
if (hsize >= 0) {
hsize++;
uint8 *out2 = start + out;
hsize /= 2;
if (hsize > 1) {
uint16 ax;
bh ^= 1;
ax = (uint16)(*out2);
ax &= 1;
if (ax ^ bh) {
out2++;
}
for (int32 j = 0; j < hsize; j++) {
*(out2) = (uint8)color;
out2 += 2;
}
}
}
}
out += _engine->width();
currentLine++;
} while (--vsize);
}
void Renderer::renderPolygonsGouraud(uint8 *out, int vtop, int32 vsize, int32 color) const {
const int16 *ptr1 = &_polyTab[vtop];
const int16 *ptr2 = &_polyTab2[vtop];
int32 renderLoop = vsize;
int32 currentLine = vtop;
do {
if (currentLine >= 0 && currentLine < _engine->height()) {
uint16 startColor = ptr2[0];
uint16 stopColor = ptr2[_engine->height()];
int16 colorSize = stopColor - startColor;
int16 stop = ptr1[_engine->height()]; // stop
int16 start = ptr1[0]; // start
ptr1++;
uint8 *out2 = start + out;
int32 hsize = stop - start;
//varf2 = ptr2[_engine->height()];
//varf3 = ptr2[0];
ptr2++;
//varf4 = (float)((int32)varf2 - (int32)varf3);
if (hsize == 0) {
if (start >= 0 && start < _engine->width()) {
*out2 = ((startColor + stopColor) / 2) / 256; // moyenne des 2 couleurs
}
} else if (hsize > 0) {
if (hsize == 1) {
if (start >= -1 && start < _engine->width() - 1) {
*(out2 + 1) = stopColor / 256;
}
if (start >= 0 && start < _engine->width()) {
*(out2) = startColor / 256;
}
} else if (hsize == 2) {
if (start >= -2 && start < _engine->width() - 2) {
*(out2 + 2) = stopColor / 256;
}
if (start >= -1 && start < _engine->width() - 1) {
*(out2 + 1) = ((startColor + stopColor) / 2) / 256;
}
if (start >= 0 && start < _engine->width()) {
*(out2) = startColor / 256;
}
} else {
int32 currentXPos = start;
colorSize /= hsize;
hsize++;
if (hsize % 2) {
hsize /= 2;
if (currentXPos >= 0 && currentXPos < _engine->width()) {
*(out2) = startColor / 256;
}
out2++;
currentXPos++;
startColor += colorSize;
} else {
hsize /= 2;
}
do {
if (currentXPos >= 0 && currentXPos < _engine->width()) {
*(out2) = startColor / 256;
}
currentXPos++;
startColor += colorSize;
if (currentXPos >= 0 && currentXPos < _engine->width()) {
*(out2 + 1) = startColor / 256;
}
currentXPos++;
out2 += 2;
startColor += colorSize;
} while (--hsize);
}
}
}
out += _engine->width();
currentLine++;
} while (--renderLoop);
}
void Renderer::renderPolygonsDither(uint8 *out, int vtop, int32 vsize, int32 color) const {
const int16 *ptr1 = &_polyTab[vtop];
const int16 *ptr2 = &_polyTab2[vtop];
int32 renderLoop = vsize;
int32 currentLine = vtop;
do {
if (currentLine >= 0 && currentLine < _engine->height()) {
int16 stop = ptr1[_engine->height()]; // stop
int16 start = ptr1[0]; // start
ptr1++;
int32 hsize = stop - start;
if (hsize >= 0) {
uint16 startColor = ptr2[0];
uint16 stopColor = ptr2[_engine->height()];
int32 currentXPos = start;
uint8 *out2 = start + out;
ptr2++;
if (hsize == 0) {
if (currentXPos >= 0 && currentXPos < _engine->width()) {
*(out2) = (uint8)(((startColor + stopColor) / 2) / 256);
}
} else {
int16 colorSize = stopColor - startColor;
if (hsize == 1) {
uint16 currentColor = startColor;
hsize++;
hsize /= 2;
currentColor &= 0xFF;
currentColor += startColor;
if (currentXPos >= 0 && currentXPos < _engine->width()) {
*(out2) = currentColor / 256;
}
currentColor &= 0xFF;
startColor += colorSize;
currentColor = ((currentColor & (0xFF00)) | ((((currentColor & 0xFF) << (hsize & 0xFF))) & 0xFF));
currentColor += startColor;
currentXPos++;
if (currentXPos >= 0 && currentXPos < _engine->width()) {
*(out2 + 1) = currentColor / 256;
}
} else if (hsize == 2) {
uint16 currentColor = startColor;
hsize++;
hsize /= 2;
currentColor &= 0xFF;
colorSize /= 2;
currentColor = ((currentColor & (0xFF00)) | ((((currentColor & 0xFF) << (hsize & 0xFF))) & 0xFF));
currentColor += startColor;
if (currentXPos >= 0 && currentXPos < _engine->width()) {
*(out2) = currentColor / 256;
}
out2++;
currentXPos++;
startColor += colorSize;
currentColor &= 0xFF;
currentColor += startColor;
if (currentXPos >= 0 && currentXPos < _engine->width()) {
*(out2) = currentColor / 256;
}
currentColor &= 0xFF;
startColor += colorSize;
currentColor = ((currentColor & (0xFF00)) | ((((currentColor & 0xFF) << (hsize & 0xFF))) & 0xFF));
currentColor += startColor;
currentXPos++;
if (currentXPos >= 0 && currentXPos < _engine->width()) {
*(out2 + 1) = currentColor / 256;
}
} else {
uint16 currentColor = startColor;
colorSize /= hsize;
hsize++;
if (hsize % 2) {
hsize /= 2;
currentColor &= 0xFF;
currentColor = ((currentColor & (0xFF00)) | ((((currentColor & 0xFF) << (hsize & 0xFF))) & 0xFF));
currentColor += startColor;
if (currentXPos >= 0 && currentXPos < _engine->width()) {
*(out2) = currentColor / 256;
}
out2++;
currentXPos++;
} else {
hsize /= 2;
}
do {
currentColor &= 0xFF;
currentColor += startColor;
if (currentXPos >= 0 && currentXPos < _engine->width()) {
*(out2) = currentColor / 256;
}
currentXPos++;
currentColor &= 0xFF;
startColor += colorSize;
currentColor = ((currentColor & (0xFF00)) | ((((currentColor & 0xFF) << (hsize & 0xFF))) & 0xFF));
currentColor += startColor;
if (currentXPos >= 0 && currentXPos < _engine->width()) {
*(out2 + 1) = currentColor / 256;
}
currentXPos++;
out2 += 2;
startColor += colorSize;
} while (--hsize);
}
}
}
}
out += _engine->width();
currentLine++;
} while (--renderLoop);
}
void Renderer::renderPolygonsMarble(uint8 *out, int vtop, int32 vsize, int32 color) const {
}
void Renderer::renderPolygons(const CmdRenderPolygon &polygon, Vertex *vertices) {
int vleft = 0;
int vright = 0;
int vtop = 0;
int vbottom = 0;
computePolygons(polygon.renderType, vertices, polygon.numVertices, vleft, vright, vtop, vbottom);
uint8 *out = (uint8 *)_engine->frontVideoBuffer.getBasePtr(0, vtop);
const int32 vsize = vbottom - vtop + 1;
switch (polygon.renderType) {
case POLYGONTYPE_FLAT:
renderPolygonsFlat(out, vtop, vsize, polygon.colorIndex);
break;
case POLYGONTYPE_COPPER:
renderPolygonsCopper(out, vtop, vsize, polygon.colorIndex);
break;
case POLYGONTYPE_BOPPER:
renderPolygonsBopper(out, vtop, vsize, polygon.colorIndex);
break;
case POLYGONTYPE_TELE:
renderPolygonsTele(out, vtop, vsize, polygon.colorIndex);
break;
case POLYGONTYPE_TRAS:
renderPolygonsTras(out, vtop, vsize, polygon.colorIndex);
break;
case POLYGONTYPE_TRAME:
renderPolygonTrame(out, vtop, vsize, polygon.colorIndex);
break;
case POLYGONTYPE_GOURAUD:
renderPolygonsGouraud(out, vtop, vsize, polygon.colorIndex);
break;
case POLYGONTYPE_DITHER:
renderPolygonsDither(out, vtop, vsize, polygon.colorIndex);
break;
case POLYGONTYPE_MARBLE:
renderPolygonsMarble(out, vtop, vsize, polygon.colorIndex);
break;
default:
warning("RENDER WARNING: Unsupported render type %d", polygon.renderType);
break;
}
}
void Renderer::circleFill(int32 x, int32 y, int32 radius, uint8 color) {
radius += 1;
for (int32 currentLine = -radius; currentLine <= radius; currentLine++) {
double width;
if (ABS(currentLine) != radius) {
width = ABS(sin(acos((float)currentLine / (float)radius)) * radius);
} else {
width = 0.0;
}
_engine->_interface->drawLine((int32)(x - width), currentLine + y, (int32)(x + width), currentLine + y, color);
}
}
uint8 *Renderer::prepareSpheres(Common::MemoryReadStream &stream, int32 &numOfPrimitives, RenderCommand **renderCmds, uint8 *renderBufferPtr, ModelData *modelData) {
int16 numSpheres = stream.readSint16LE();
if (numSpheres <= 0) {
return renderBufferPtr;
}
numOfPrimitives += numSpheres;
do {
CmdRenderSphere *sphere = (CmdRenderSphere *)renderBufferPtr;
stream.skip(1);
sphere->colorIndex = stream.readByte();
stream.skip(2);
sphere->radius = stream.readUint16LE();
const int16 centerOffset = stream.readUint16LE();
const int16 centerIndex = centerOffset / 6;
sphere->x = modelData->flattenPoints[centerIndex].x;
sphere->y = modelData->flattenPoints[centerIndex].y;
(*renderCmds)->depth = modelData->flattenPoints[centerIndex].z;
(*renderCmds)->renderType = RENDERTYPE_DRAWSPHERE;
(*renderCmds)->dataPtr = renderBufferPtr;
(*renderCmds)++;
renderBufferPtr += sizeof(CmdRenderSphere);
} while (--numSpheres);
return renderBufferPtr;
}
uint8 *Renderer::prepareLines(Common::MemoryReadStream &stream, int32 &numOfPrimitives, RenderCommand **renderCmds, uint8 *renderBufferPtr, ModelData *modelData) {
int16 numLines = stream.readSint16LE();
if (numLines <= 0) {
return renderBufferPtr;
}
numOfPrimitives += numLines;
do {
CmdRenderLine *lineCoordinatesPtr = (CmdRenderLine *)renderBufferPtr;
lineCoordinatesPtr->colorIndex = stream.readByte();
stream.skip(3);
const int32 point1Index = stream.readSint16LE() / 6;
const int32 point2Index = stream.readSint16LE() / 6;
lineCoordinatesPtr->x1 = modelData->flattenPoints[point1Index].x;
lineCoordinatesPtr->y1 = modelData->flattenPoints[point1Index].y;
lineCoordinatesPtr->x2 = modelData->flattenPoints[point2Index].x;
lineCoordinatesPtr->y2 = modelData->flattenPoints[point2Index].y;
(*renderCmds)->depth = MAX(modelData->flattenPoints[point1Index].z, modelData->flattenPoints[point2Index].z);
(*renderCmds)->renderType = RENDERTYPE_DRAWLINE;
(*renderCmds)->dataPtr = renderBufferPtr;
(*renderCmds)++;
renderBufferPtr += sizeof(CmdRenderLine);
} while (--numLines);
return renderBufferPtr;
}
uint8 *Renderer::preparePolygons(Common::MemoryReadStream &stream, int32 &numOfPrimitives, RenderCommand **renderCmds, uint8 *renderBufferPtr, ModelData *modelData) {
int16 numPolygons = stream.readSint16LE();
if (numPolygons <= 0) {
return renderBufferPtr;
}
int16 primitiveCounter = numPolygons; // the number of primitives = the number of polygons
do { // loop that load all the polygons
const uint8 renderType = stream.readByte();
const uint8 numVertices = stream.readByte();
assert(numVertices <= 16);
const int16 colorIndex = stream.readSint16LE();
int16 bestDepth = -32000;
CmdRenderPolygon *destinationPolygon = (CmdRenderPolygon *)renderBufferPtr;
destinationPolygon->numVertices = numVertices;
renderBufferPtr += sizeof(CmdRenderPolygon);
Vertex *const vertices = (Vertex *)renderBufferPtr;
renderBufferPtr += destinationPolygon->numVertices * sizeof(Vertex);
Vertex *vertex = vertices;
int16 counter = destinationPolygon->numVertices;
// TODO: RECHECK coordinates axis
if (renderType >= 9) {
destinationPolygon->renderType = renderType - 2;
destinationPolygon->colorIndex = colorIndex;
do {
const int16 shadeEntry = stream.readSint16LE();
const int16 shadeValue = colorIndex + modelData->shadeTable[shadeEntry];
const int16 vertexOffset = stream.readSint16LE();
const int16 vertexIndex = vertexOffset / 6;
const pointTab *point = &modelData->flattenPoints[vertexIndex];
vertex->colorIndex = shadeValue;
vertex->x = point->x;
vertex->y = point->y;
bestDepth = MAX(bestDepth, point->z);
++vertex;
} while (--counter > 0);
} else {
if (renderType >= POLYGONTYPE_GOURAUD) {
// only 1 shade value is used
destinationPolygon->renderType = renderType - POLYGONTYPE_GOURAUD;
const int16 shadeEntry = stream.readSint16LE();
const int16 shadeValue = colorIndex + modelData->shadeTable[shadeEntry];
destinationPolygon->colorIndex = shadeValue;
} else {
// no shade is used
destinationPolygon->renderType = renderType;
destinationPolygon->colorIndex = colorIndex;
}
do {
const int16 vertexOffset = stream.readSint16LE();
const int16 vertexIndex = vertexOffset / 6;
const pointTab *point = &modelData->flattenPoints[vertexIndex];
vertex->colorIndex = destinationPolygon->colorIndex;
vertex->x = point->x;
vertex->y = point->y;
bestDepth = MAX(bestDepth, point->z);
++vertex;
} while (--counter > 0);
}
numOfPrimitives++;
(*renderCmds)->depth = bestDepth;
(*renderCmds)->renderType = RENDERTYPE_DRAWPOLYGON;
(*renderCmds)->dataPtr = (uint8 *)destinationPolygon;
(*renderCmds)++;
} while (--primitiveCounter);
return renderBufferPtr;
}
const Renderer::RenderCommand *Renderer::depthSortRenderCommands(int32 numOfPrimitives) {
Common::sort(&_renderCmds[0], &_renderCmds[numOfPrimitives], [] (const RenderCommand &lhs, const RenderCommand &rhs) {return lhs.depth > rhs.depth;});
return _renderCmds;
}
bool Renderer::renderModelElements(int32 numOfPrimitives, const uint8 *polygonPtr, RenderCommand **renderCmds, ModelData *modelData) {
// TODO: proper size
Common::MemoryReadStream stream(polygonPtr, 100000);
uint8 *renderBufferPtr = renderCoordinatesBuffer;
renderBufferPtr = preparePolygons(stream, numOfPrimitives, renderCmds, renderBufferPtr, modelData);
renderBufferPtr = prepareLines(stream, numOfPrimitives, renderCmds, renderBufferPtr, modelData);
renderBufferPtr = prepareSpheres(stream, numOfPrimitives, renderCmds, renderBufferPtr, modelData);
if (numOfPrimitives == 0) {
_engine->_redraw->renderRect.right = -1;
_engine->_redraw->renderRect.bottom = -1;
_engine->_redraw->renderRect.left = -1;
_engine->_redraw->renderRect.top = -1;
return false;
}
const RenderCommand *cmds = depthSortRenderCommands(numOfPrimitives);
int16 primitiveCounter = numOfPrimitives;
do {
int16 type = cmds->renderType;
uint8 *pointer = cmds->dataPtr;
switch (type) {
case RENDERTYPE_DRAWLINE: {
const CmdRenderLine *lineCoords = (const CmdRenderLine *)pointer;
const int32 x1 = lineCoords->x1;
const int32 y1 = lineCoords->y1;
const int32 x2 = lineCoords->x2;
const int32 y2 = lineCoords->y2;
_engine->_interface->drawLine(x1, y1, x2, y2, lineCoords->colorIndex);
break;
}
case RENDERTYPE_DRAWPOLYGON: {
const CmdRenderPolygon *header = (const CmdRenderPolygon *)pointer;
Vertex *vertices = (Vertex *)(pointer + sizeof(CmdRenderPolygon));
renderPolygons(*header, vertices);
break;
}
case RENDERTYPE_DRAWSPHERE: {
CmdRenderSphere *sphere = (CmdRenderSphere *)pointer;
int32 radius = sphere->radius;
//if (isUsingOrthoProjection) {
radius = (radius * 34) / 512;
//} else {
// radius = (radius * cameraScaleY) / (cameraDepthOffset + *(const int16 *)pointer); // TODO: this does not make sense.
//}
radius += 3;
if (sphere->x + radius > _engine->_redraw->renderRect.right) {
_engine->_redraw->renderRect.right = sphere->x + radius;
}
if (sphere->x - radius < _engine->_redraw->renderRect.left) {
_engine->_redraw->renderRect.left = sphere->x - radius;
}
if (sphere->y + radius > _engine->_redraw->renderRect.bottom) {
_engine->_redraw->renderRect.bottom = sphere->y + radius;
}
if (sphere->y - radius < _engine->_redraw->renderRect.top) {
_engine->_redraw->renderRect.top = sphere->y - radius;
}
radius -= 3;
circleFill(sphere->x, sphere->y, radius, sphere->colorIndex);
break;
}
default:
break;
}
cmds++;
} while (--primitiveCounter);
return true;
}
bool Renderer::renderAnimatedModel(ModelData *modelData, const uint8 *bodyPtr, RenderCommand *renderCmds) {
const int32 numVertices = Model::getNumVertices(bodyPtr);
const int32 numBones = Model::getNumBones(bodyPtr);
const pointTab *pointsPtr = (const pointTab *)Model::getVerticesBaseData(bodyPtr);
Matrix *modelMatrix = &matricesTable[0];
const elementEntry *bonesPtr0 = (const elementEntry *)Model::getBonesBaseData(bodyPtr, 0);
processRotatedElement(modelMatrix, pointsPtr, renderAngleX, renderAngleY, renderAngleZ, bonesPtr0, modelData);
int32 numOfPrimitives = 0;
if (numBones - 1 != 0) {
numOfPrimitives = numBones - 1;
modelMatrix = &matricesTable[1];
int boneIdx = 1;
do {
const elementEntry *bonesPtr = (const elementEntry *)Model::getBonesBaseData(bodyPtr, boneIdx);
int16 boneType = bonesPtr->flag;
if (boneType == 0) {
processRotatedElement(modelMatrix, pointsPtr, bonesPtr->rotateX, bonesPtr->rotateY, bonesPtr->rotateZ, bonesPtr, modelData);
} else if (boneType == 1) {
processTranslatedElement(modelMatrix, pointsPtr, bonesPtr->rotateX, bonesPtr->rotateY, bonesPtr->rotateZ, bonesPtr, modelData);
}
++modelMatrix;
++boneIdx;
} while (--numOfPrimitives);
}
numOfPrimitives = numVertices;
const pointTab *pointPtr = &modelData->computedPoints[0];
pointTab *pointPtrDest = &modelData->flattenPoints[0];
if (isUsingOrthoProjection) { // use standard projection
do {
const int32 coX = pointPtr->x + renderX;
const int32 coY = pointPtr->y + renderY;
const int32 coZ = -(pointPtr->z + renderZ);
pointPtrDest->x = (coX + coZ) * 24 / BRICK_SIZE + orthoProjX;
pointPtrDest->y = (((coX - coZ) * 12) - coY * 30) / BRICK_SIZE + orthoProjY;
pointPtrDest->z = coZ - coX - coY;
if (pointPtrDest->x < _engine->_redraw->renderRect.left) {
_engine->_redraw->renderRect.left = pointPtrDest->x;
}
if (pointPtrDest->x > _engine->_redraw->renderRect.right) {
_engine->_redraw->renderRect.right = pointPtrDest->x;
}
if (pointPtrDest->y < _engine->_redraw->renderRect.top) {
_engine->_redraw->renderRect.top = pointPtrDest->y;
}
if (pointPtrDest->y > _engine->_redraw->renderRect.bottom) {
_engine->_redraw->renderRect.bottom = pointPtrDest->y;
}
pointPtr++;
pointPtrDest++;
} while (--numOfPrimitives);
} else {
do {
int32 coX = pointPtr->x + renderX;
int32 coY = pointPtr->y + renderY;
int32 coZ = -(pointPtr->z + renderZ);
coZ += cameraDepthOffset;
if (coZ <= 0) {
coZ = 0x7FFFFFFF;
}
// X projection
{
coX = orthoProjX + ((coX * cameraScaleY) / coZ);
if (coX > 0xFFFF) {
coX = 0x7FFF;
}
pointPtrDest->x = coX;
if (pointPtrDest->x < _engine->_redraw->renderRect.left) {
_engine->_redraw->renderRect.left = pointPtrDest->x;
}
if (pointPtrDest->x > _engine->_redraw->renderRect.right) {
_engine->_redraw->renderRect.right = pointPtrDest->x;
}
}
// Y projection
{
coY = orthoProjY + ((-coY * cameraScaleZ) / coZ);
if (coY > 0xFFFF) {
coY = 0x7FFF;
}
pointPtrDest->y = coY;
if (pointPtrDest->y < _engine->_redraw->renderRect.top)
_engine->_redraw->renderRect.top = pointPtrDest->y;
if (pointPtrDest->y > _engine->_redraw->renderRect.bottom)
_engine->_redraw->renderRect.bottom = pointPtrDest->y;
}
// Z projection
{
if (coZ > 0xFFFF) {
coZ = 0x7FFF;
}
pointPtrDest->z = coZ;
}
pointPtr++;
pointPtrDest++;
} while (--numOfPrimitives);
}
int32 numOfShades = Model::getNumShades(bodyPtr);
if (numOfShades) { // process normal data
uint16 *currentShadeDestination = (uint16 *)modelData->shadeTable;
Matrix *lightMatrix = &matricesTable[0];
numOfPrimitives = numBones;
int shadeIndex = 0;
int boneIdx = 0;
do { // for each element
numOfShades = Model::getNumShadesBone(bodyPtr, boneIdx);
if (numOfShades) {
int32 numShades = numOfShades;
shadeMatrix = *lightMatrix * lightPos;
do { // for each normal
const uint8 *shadePtr = Model::getShadesBaseData(bodyPtr, shadeIndex);
const int16 *colPtr = (const int16 *)shadePtr;
const int16 col1 = *((const int16 *)colPtr++);
const int16 col2 = *((const int16 *)colPtr++);
const int16 col3 = *((const int16 *)colPtr++);
int32 color = 0;
color += shadeMatrix.row1[0] * col1 + shadeMatrix.row1[1] * col2 + shadeMatrix.row1[2] * col3;
color += shadeMatrix.row2[0] * col1 + shadeMatrix.row2[1] * col2 + shadeMatrix.row2[2] * col3;
color += shadeMatrix.row3[0] * col1 + shadeMatrix.row3[1] * col2 + shadeMatrix.row3[2] * col3;
int32 shade = 0;
if (color > 0) {
color >>= 14;
const uint8 *tmpShadePtr = (const uint8 *)shadePtr;
color /= *((const uint16 *)(tmpShadePtr + 6));
shade = (uint16)color;
}
*currentShadeDestination = shade;
currentShadeDestination++;
++shadeIndex;
} while (--numShades);
}
++boneIdx;
++lightMatrix;
} while (--numOfPrimitives);
}
return renderModelElements(numOfPrimitives, Model::getPolygonData(bodyPtr), &renderCmds, modelData);
}
void Renderer::prepareIsoModel(uint8 *bodyPtr) { // loadGfxSub
Model *bodyHeader = (Model *)bodyPtr;
// This function should only be called ONCE, otherwise it corrupts the model data.
// The following code implements an unused flag to indicate that a model was already processed.
if (bodyHeader->bodyFlag.alreadyPrepared) {
return;
}
bodyHeader->bodyFlag.alreadyPrepared = 1;
// no animation applicable
if (!Model::isAnimated(bodyPtr)) {
return;
}
uint8 *bonesBase = Model::getBonesBaseData(bodyPtr);
const int16 numBones = Model::getNumBones(bodyPtr);
// set up bone indices
for (int32 i = 0; i < numBones; i++) {
bonesBase += sizeof(elementEntry);
elementEntry *ee = (elementEntry *)bonesBase;
ee->baseElement = ee->baseElement / sizeof(elementEntry);
}
}
bool Renderer::renderIsoModel(int32 x, int32 y, int32 z, int32 angleX, int32 angleY, int32 angleZ, const uint8 *bodyPtr) {
renderAngleX = angleX;
renderAngleY = angleY;
renderAngleZ = angleZ;
// model render size reset
_engine->_redraw->renderRect.left = SCENE_SIZE_MAX;
_engine->_redraw->renderRect.top = SCENE_SIZE_MAX;
_engine->_redraw->renderRect.right = SCENE_SIZE_MIN;
_engine->_redraw->renderRect.bottom = SCENE_SIZE_MIN;
if (isUsingOrthoProjection) {
renderX = x;
renderY = y;
renderZ = z;
} else {
getBaseRotationPosition(x, y, z);
renderX = destX - baseRotPosX;
renderY = destY - baseRotPosY; // RECHECK
renderZ = destZ - baseRotPosZ;
}
if (!Model::isAnimated(bodyPtr)) {
error("Unsupported unanimated model render!");
}
// restart at the beginning of the renderTable
return renderAnimatedModel(&_modelData, bodyPtr, _renderCmds);
}
void Renderer::renderBehaviourModel(const Common::Rect &rect, int32 y, int32 angle, const uint8 *bodyPtr) {
renderBehaviourModel(rect.left, rect.top, rect.right, rect.bottom, y, angle, bodyPtr);
}
void Renderer::renderBehaviourModel(int32 boxLeft, int32 boxTop, int32 boxRight, int32 boxBottom, int32 y, int32 angle, const uint8 *bodyPtr) {
const int32 ypos = (boxBottom + boxTop) / 2;
const int32 xpos = (boxRight + boxLeft) / 2;
setOrthoProjection(xpos, ypos, 0);
_engine->_interface->setClip(Common::Rect(boxLeft, boxTop, boxRight, boxBottom));
if (angle == -1) {
ActorMoveStruct &move = _engine->_menu->moveMenu;
const int16 newAngle = move.getRealAngle(_engine->lbaTime);
if (move.numOfStep == 0) {
_engine->_movements->setActorAngleSafe(newAngle, newAngle - ANGLE_90, ANGLE_17, &move);
}
renderIsoModel(0, y, 0, ANGLE_0, newAngle, ANGLE_0, bodyPtr);
} else {
renderIsoModel(0, y, 0, ANGLE_0, angle, ANGLE_0, bodyPtr);
}
}
void Renderer::renderInventoryItem(int32 x, int32 y, const uint8 *bodyPtr, int32 angle, int32 param) {
setCameraPosition(x, y, 128, 200, 200);
setCameraAngle(0, 0, 0, 60, 0, 0, param);
renderIsoModel(0, 0, 0, ANGLE_0, angle, ANGLE_0, bodyPtr);
}
} // namespace TwinE