Files
scummvm/engines/twine/renderer.cpp
T

1753 lines
47 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.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "twine/actor.h"
#include "twine/interface.h"
#include "twine/menu.h"
#include "twine/movements.h"
#include "twine/redraw.h"
#include "twine/shadeangletab.h"
#include "twine/twine.h"
namespace TwinE {
#define RENDERTYPE_DRAWLINE 0
#define RENDERTYPE_DRAWPOLYGON 1
#define RENDERTYPE_DRAWSPHERE 2
int32 Renderer::projectPositionOnScreen(int32 cX, int32 cY, int32 cZ) {
if (!isUsingOrhoProjection) {
cX -= baseRotPosX;
cY -= baseRotPosY;
cZ -= baseRotPosZ;
if (cZ >= 0) {
int32 posZ = cZ + cameraPosX;
if (posZ < 0)
posZ = 0x7FFF;
projPosX = (cX * cameraPosY) / posZ + orthoProjX;
projPosY = (-cY * cameraPosZ) / posZ + orthoProjY;
projPosZ = posZ;
return -1;
}
projPosX = 0;
projPosY = 0;
projPosZ = 0;
return 0;
}
projPosX = ((cX - cZ) * 24) / 512 + orthoProjX;
projPosY = (((cX + cZ) * 12) - cY * 30) / 512 + orthoProjY;
projPosZ = cZ - cY - cX;
return 1;
}
void Renderer::setCameraPosition(int32 x, int32 y, int32 cX, int32 cY, int32 cZ) {
orthoProjX = x;
orthoProjY = y;
cameraPosX = cX;
cameraPosY = cY;
cameraPosZ = cZ;
isUsingOrhoProjection = 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;
isUsingOrhoProjection = true;
}
void Renderer::getBaseRotationPosition(int32 x, int32 y, int32 z) {
destX = (baseMatrix[0] * x + baseMatrix[1] * y + baseMatrix[2] * z) >> 14;
destY = (baseMatrix[3] * x + baseMatrix[4] * y + baseMatrix[5] * z) >> 14;
destZ = (baseMatrix[6] * x + baseMatrix[7] * y + baseMatrix[8] * z) >> 14;
}
void Renderer::setBaseRotation(int32 x, int32 y, int32 z) {
shadeAngleTab3 = &shadeAngleTable[384];
double Xradians = (double)((256 - x) % 1024) * 2 * M_PI / 1024;
double Yradians = (double)((256 - y) % 1024) * 2 * M_PI / 1024;
double Zradians = (double)((256 - z) % 1024) * 2 * M_PI / 1024;
baseMatrix[0] = (int32)(sin(Zradians) * sin(Yradians) * 16384);
baseMatrix[1] = (int32)(-cos(Zradians) * 16384);
baseMatrix[2] = (int32)(sin(Zradians) * cos(Yradians) * 16384);
baseMatrix[3] = (int32)(cos(Zradians) * sin(Xradians) * 16384);
baseMatrix[4] = (int32)(sin(Zradians) * sin(Xradians) * 16384);
baseMatrix[6] = (int32)(cos(Zradians) * cos(Xradians) * 16384);
baseMatrix[7] = (int32)(sin(Zradians) * cos(Xradians) * 16384);
int32 matrixElem = baseMatrix[3];
baseMatrix[3] = (int32)(sin(Yradians) * matrixElem + 16384 * cos(Yradians) * cos(Xradians));
baseMatrix[5] = (int32)(cos(Yradians) * matrixElem - 16384 * sin(Yradians) * cos(Xradians));
matrixElem = baseMatrix[6];
baseMatrix[6] = (int32)(sin(Yradians) * matrixElem - 16384 * sin(Xradians) * cos(Yradians));
baseMatrix[8] = (int32)(cos(Yradians) * matrixElem + 16384 * sin(Xradians) * sin(Yradians));
getBaseRotationPosition(baseTransPosX, baseTransPosY, baseTransPosZ);
baseRotPosX = destX;
baseRotPosY = destY;
baseRotPosZ = destZ;
}
void Renderer::getCameraAnglePositions(int32 x, int32 y, int32 z) {
destX = (baseMatrix[0] * x + baseMatrix[3] * y + baseMatrix[6] * z) >> 14;
destY = (baseMatrix[1] * x + baseMatrix[4] * y + baseMatrix[7] * z) >> 14;
destZ = (baseMatrix[2] * x + baseMatrix[5] * y + baseMatrix[8] * z) >> 14;
}
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(int32 *targetMatrix, const int32 *currentMatrix) {
int32 matrix1[9];
int32 matrix2[9];
if (renderAngleX) {
int32 angle = renderAngleX;
int32 angleVar2 = shadeAngleTable[ClampAngle(angle)];
angle += 256;
int32 angleVar1 = shadeAngleTable[ClampAngle(angle)];
matrix1[0] = currentMatrix[0];
matrix1[3] = currentMatrix[3];
matrix1[6] = currentMatrix[6];
matrix1[1] = (currentMatrix[2] * angleVar2 + currentMatrix[1] * angleVar1) >> 14;
matrix1[2] = (currentMatrix[2] * angleVar1 - currentMatrix[1] * angleVar2) >> 14;
matrix1[4] = (currentMatrix[5] * angleVar2 + currentMatrix[4] * angleVar1) >> 14;
matrix1[5] = (currentMatrix[5] * angleVar1 - currentMatrix[4] * angleVar2) >> 14;
matrix1[7] = (currentMatrix[8] * angleVar2 + currentMatrix[7] * angleVar1) >> 14;
matrix1[8] = (currentMatrix[8] * angleVar1 - currentMatrix[7] * angleVar2) >> 14;
} else {
for (int32 i = 0; i < 9; i++) {
matrix1[i] = currentMatrix[i];
}
}
if (renderAngleZ) {
int32 angle = renderAngleZ;
int32 angleVar2 = shadeAngleTable[ClampAngle(angle)];
angle += 256;
int32 angleVar1 = shadeAngleTable[ClampAngle(angle)];
matrix2[2] = matrix1[2];
matrix2[5] = matrix1[5];
matrix2[8] = matrix1[8];
matrix2[0] = (matrix1[1] * angleVar2 + matrix1[0] * angleVar1) >> 14;
matrix2[1] = (matrix1[1] * angleVar1 - matrix1[0] * angleVar2) >> 14;
matrix2[3] = (matrix1[4] * angleVar2 + matrix1[3] * angleVar1) >> 14;
matrix2[4] = (matrix1[4] * angleVar1 - matrix1[3] * angleVar2) >> 14;
matrix2[6] = (matrix1[7] * angleVar2 + matrix1[6] * angleVar1) >> 14;
matrix2[7] = (matrix1[7] * angleVar1 - matrix1[6] * angleVar2) >> 14;
} else {
for (int32 i = 0; i < 9; i++) {
matrix2[i] = matrix1[i];
}
}
if (renderAngleY) {
int32 angle = renderAngleY;
int32 angleVar2 = shadeAngleTable[ClampAngle(angle)];
angle += 256;
int32 angleVar1 = shadeAngleTable[ClampAngle(angle)];
targetMatrix[1] = matrix2[1];
targetMatrix[4] = matrix2[4];
targetMatrix[7] = matrix2[7];
targetMatrix[0] = (matrix2[0] * angleVar1 - matrix2[2] * angleVar2) >> 14;
targetMatrix[2] = (matrix2[0] * angleVar2 + matrix2[2] * angleVar1) >> 14;
targetMatrix[3] = (matrix2[3] * angleVar1 - matrix2[5] * angleVar2) >> 14;
targetMatrix[5] = (matrix2[3] * angleVar2 + matrix2[5] * angleVar1) >> 14;
targetMatrix[6] = (matrix2[6] * angleVar1 - matrix2[8] * angleVar2) >> 14;
targetMatrix[8] = (matrix2[6] * angleVar2 + matrix2[8] * angleVar1) >> 14;
} else {
for (int32 i = 0; i < 9; i++) {
targetMatrix[i] = matrix2[i];
}
}
}
void Renderer::applyPointsRotation(const pointTab *pointsPtr, int32 numPoints, pointTab *destPoints, const int32 *rotationMatrix) {
int32 numOfPoints2 = numPoints;
do {
const int16 tmpX = pointsPtr->x;
const int16 tmpY = pointsPtr->y;
const int16 tmpZ = pointsPtr->z;
destPoints->x = ((rotationMatrix[0] * tmpX + rotationMatrix[1] * tmpY + rotationMatrix[2] * tmpZ) >> 14) + destX;
destPoints->y = ((rotationMatrix[3] * tmpX + rotationMatrix[4] * tmpY + rotationMatrix[5] * tmpZ) >> 14) + destY;
destPoints->z = ((rotationMatrix[6] * tmpX + rotationMatrix[7] * tmpY + rotationMatrix[8] * tmpZ) >> 14) + destZ;
destPoints++;
pointsPtr++;
} while (--numOfPoints2);
}
void Renderer::processRotatedElement(int32 *targetMatrix, const uint8 *pointsPtr, int32 rotZ, int32 rotY, int32 rotX, const elementEntry *elemPtr) { // unsigned char * elemPtr) // loadPart
int32 firstPoint = elemPtr->firstPoint;
int32 numOfPoints2 = elemPtr->numOfPoints;
renderAngleX = rotX;
renderAngleY = rotY;
renderAngleZ = rotZ;
if (firstPoint % sizeof(pointTab)) {
error("RENDER ERROR: invalid firstPoint in process_rotated_element func");
}
//baseElement = *((unsigned short int*)elemPtr+6);
const int16 baseElement = elemPtr->baseElement;
const int32 *currentMatrix;
// if its the first point
if (baseElement == -1) {
currentMatrix = baseMatrix;
destX = 0;
destY = 0;
destZ = 0;
} else {
int32 pointIdx = (elemPtr->basePoint) / sizeof(pointTab);
currentMatrix = &matricesTable[baseElement / sizeof(int32)];
destX = computedPoints[pointIdx].x;
destY = computedPoints[pointIdx].y;
destZ = computedPoints[pointIdx].z;
}
applyRotation(targetMatrix, currentMatrix);
if (!numOfPoints2) {
warning("RENDER WARNING: No points in this model!");
}
applyPointsRotation((const pointTab*)(pointsPtr + firstPoint), numOfPoints2, &computedPoints[firstPoint / sizeof(pointTab)], targetMatrix);
}
void Renderer::applyPointsTranslation(const pointTab *pointsPtr, int32 numPoints, pointTab *destPoints, const int32 *translationMatrix) {
int32 numOfPoints2 = numPoints;
do {
const int16 tmpX = pointsPtr->x + renderAngleZ;
const int16 tmpY = pointsPtr->y + renderAngleY;
const int16 tmpZ = pointsPtr->z + renderAngleX;
destPoints->x = ((translationMatrix[0] * tmpX + translationMatrix[1] * tmpY + translationMatrix[2] * tmpZ) >> 14) + destX;
destPoints->y = ((translationMatrix[3] * tmpX + translationMatrix[4] * tmpY + translationMatrix[5] * tmpZ) >> 14) + destY;
destPoints->z = ((translationMatrix[6] * tmpX + translationMatrix[7] * tmpY + translationMatrix[8] * tmpZ) >> 14) + destZ;
destPoints++;
pointsPtr++;
} while (--numOfPoints2);
}
void Renderer::processTranslatedElement(int32 *targetMatrix, const uint8 *pointsPtr, int32 rotX, int32 rotY, int32 rotZ, const elementEntry *elemPtr) {
renderAngleX = rotX;
renderAngleY = rotY;
renderAngleZ = rotZ;
if (elemPtr->baseElement == -1) { // base point
destX = 0;
destY = 0;
destZ = 0;
int32 *dest = targetMatrix;
for (int32 i = 0; i < 9; i++) {
dest[i] = baseMatrix[i];
}
} else { // dependent
const int pointsIdx = elemPtr->basePoint / 6;
destX = computedPoints[pointsIdx].x;
destY = computedPoints[pointsIdx].y;
destZ = computedPoints[pointsIdx].z;
const int32 *source = &matricesTable[elemPtr->baseElement / sizeof(int32)];
int32 *dest = targetMatrix;
for (int32 i = 0; i < 9; i++) {
dest[i] = source[i];
}
}
applyPointsTranslation((const pointTab*)(pointsPtr + elemPtr->firstPoint), elemPtr->numOfPoints, &computedPoints[elemPtr->firstPoint / sizeof(pointTab)], targetMatrix);
}
void Renderer::translateGroup(int16 ax, int16 bx, int16 cx) {
int32 ebp = ax;
int32 ebx = bx;
int32 ecx = cx;
int32 edi = shadeMatrix[0];
int32 eax = shadeMatrix[1];
edi *= ebp;
eax *= ebx;
edi += eax;
eax = shadeMatrix[2];
eax *= ecx;
eax += edi;
eax >>= 14;
destX = eax;
edi = shadeMatrix[3];
eax = shadeMatrix[4];
edi *= ebp;
eax *= ebx;
edi += eax;
eax = shadeMatrix[5];
eax *= ecx;
eax += edi;
eax >>= 14;
destY = eax;
ebp *= shadeMatrix[6];
ebx *= shadeMatrix[7];
ecx *= shadeMatrix[8];
ebx += ebp;
ebx += ecx;
ebx >>= 14;
destZ = eax;
}
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);
lightX = destX;
lightY = destY;
lightZ = destZ;
}
FORCEINLINE int16 clamp(int16 x, int16 a, int16 b) {
return x < a ? a : (x > b ? b : x);
}
void Renderer::computePolygons(int16 polyRenderType, vertexData *vertices, int32 numVertices, int &vleft, int &vright, int &vtop, int &vbottom) {
vleft = vtop = 32767;
vright = vbottom = -32768;
for (int32 i = 0; i < numVertices; i++) {
vertices[i].x = clamp(vertices[i].x, 0, SCREEN_WIDTH - 1);
int16 vertexX = vertices[i].x;
if (vertexX < vleft) {
vleft = vertexX;
}
if (vertexX > vright) {
vright = vertexX;
}
vertices[i].y = clamp(vertices[i].y, 0, SCREEN_HEIGHT - 1);
int16 vertexY = vertices[i].y;
if (vertexY < vtop) {
vtop = vertexY;
}
if (vertexY > vbottom) {
vbottom = vertexY;
}
}
uint8 vertexParam1 = vertices[numVertices - 1].param;
uint8 vertexParam2 = vertexParam1;
int16 currentVertexX = vertices[numVertices - 1].x;
int16 currentVertexY = vertices[numVertices - 1].y;
for (int32 nVertex = 0; nVertex < numVertices; nVertex++) {
int16 oldVertexY = currentVertexY;
int16 oldVertexX = currentVertexX;
uint8 oldVertexParam = vertexParam1;
vertexParam1 = vertexParam2 = vertices[nVertex].param;
currentVertexX = vertices[nVertex].x;
currentVertexY = vertices[nVertex].y;
// drawLine(oldVertexX,oldVertexY,currentVertexX,currentVertexY,255);
if (currentVertexY == oldVertexY) {
continue;
}
int8 up = currentVertexY < oldVertexY;
int8 direction = up ? -1 : 1;
int16 vsize = ABS(currentVertexY - oldVertexY);
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 << 8) + ((oldVertexParam - vertexParam2) << 8) % vsize;
cdelta = ((oldVertexParam - vertexParam2) << 8) / vsize;
direction = -direction; // we will draw by going down the tab
} else {
xpos = oldVertexX;
ypos = oldVertexY;
cvalue = (oldVertexParam << 8) + ((vertexParam2 - oldVertexParam) << 8) % vsize;
cdelta = ((vertexParam2 - oldVertexParam) << 8) / vsize;
}
int16 *outPtr = &polyTab[ypos + (up ? SCREEN_HEIGHT : 0)]; // 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 < ARRAYSIZE(polyTab)) {
if (outPtr - polyTab > 0) {
*outPtr = xpos;
}
}
outPtr += direction;
xpos += slope;
}
if (polyRenderType >= POLYGONTYPE_GOURAUD) { // we must compute the color progression
int16 *outPtr2 = &polyTab2[ypos + (up ? SCREEN_HEIGHT : 0)];
for (int32 i = 0; i < vsize + 2; i++) {
if (outPtr2 - polyTab2 < ARRAYSIZE(polyTab2)) {
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 < SCREEN_HEIGHT) {
int16 start = ptr1[0];
int16 stop = ptr1[SCREEN_HEIGHT];
ptr1++;
int32 hsize = stop - start;
if (hsize >= 0) {
uint16 mask = 0x43DB;
uint16 dx;
int32 startCopy;
dx = (uint8)color;
dx |= 0x300;
hsize++;
startCopy = start;
for (int32 j = startCopy; j < hsize + startCopy; j++) {
start += mask;
start = (start & 0xFF00) | ((start & 0xFF) & (uint8)(dx >> 8));
start = (start & 0xFF00) | ((start & 0xFF) + (dx & 0xFF));
if (j >= 0 && j < SCREEN_WIDTH) {
out[j] = start & 0xFF;
}
mask = (mask << 2) | (mask >> 14);
mask++;
}
}
}
out += SCREEN_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 < SCREEN_HEIGHT) {
int16 start = ptr1[0];
int16 stop = ptr1[SCREEN_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 < SCREEN_WIDTH) {
out[j] = color;
}
}
}
}
}
out += SCREEN_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 < SCREEN_HEIGHT) {
int16 stop = ptr1[SCREEN_HEIGHT];
int16 start = ptr1[0];
ptr1++;
int32 hsize = stop - start;
if (hsize >= 0) {
hsize++;
for (int32 j = start; j < hsize + start; j++) {
if (j >= 0 && j < SCREEN_WIDTH) {
out[j] = color;
}
}
}
}
out += SCREEN_WIDTH;
currentLine++;
} while (--vsize);
}
void Renderer::renderPolygonsTele(uint8 *out, int vtop, int32 vsize, int32 color) const {
const int16 *ptr1 = &polyTab[vtop];
int ax;
int bx;
unsigned short int dx;
unsigned short int temp;
bx = (unsigned short)color << 0x10;
int32 renderLoop = vsize;
do {
int16 start;
int16 stop;
int32 hsize;
while (1) {
start = ptr1[0];
stop = ptr1[SCREEN_HEIGHT];
ptr1++;
hsize = stop - start;
if (hsize) {
break;
}
uint8 *out2 = start + out;
*out2 = ((unsigned short)(bx >> 0x18)) & 0x0F;
color = *(out2 + 1);
out += SCREEN_WIDTH;
--renderLoop;
if (!renderLoop) {
return;
}
}
if (stop >= start) {
hsize++;
bx = (unsigned short)(color >> 0x10);
uint8 *out2 = start + out;
ax = (bx & 0xF0) << 8;
bx = bx << 8;
ax += (bx & 0x0F);
ax -= bx;
ax++;
ax = ax >> 16;
ax = ax / hsize;
temp = (ax & 0xF0);
temp = temp >> 8;
temp += (ax & 0x0F);
ax = temp;
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 += SCREEN_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[SCREEN_HEIGHT];
ptr1++;
int32 hsize = stop - start;
if (hsize >= 0) {
hsize++;
uint8 *out2 = start + out;
if ((hsize >> 1) < 0) {
bx = color & 0xFF;
bx = bx << 8;
bx += color & 0xFF;
for (int32 j = 0; j < hsize; j++) {
*(out2) = (*(out2)&0x0F0F) | bx;
}
} else {
*(out2) = (*(out2)&0x0F) | color;
out2++;
}
}
out += SCREEN_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 < SCREEN_HEIGHT) {
int16 start = ptr1[0];
int16 stop = ptr1[SCREEN_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 += SCREEN_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 < SCREEN_HEIGHT) {
uint16 startColor = ptr2[0];
uint16 stopColor = ptr2[SCREEN_HEIGHT];
int16 colorSize = stopColor - startColor;
int16 stop = ptr1[SCREEN_HEIGHT]; // stop
int16 start = ptr1[0]; // start
ptr1++;
uint8 *out2 = start + out;
int32 hsize = stop - start;
//varf2 = ptr2[SCREEN_HEIGHT];
//varf3 = ptr2[0];
ptr2++;
//varf4 = (float)((int32)varf2 - (int32)varf3);
if (hsize == 0) {
if (start >= 0 && start < SCREEN_WIDTH) {
*out2 = ((startColor + stopColor) / 2) >> 8; // moyenne des 2 couleurs
}
} else if (hsize > 0) {
if (hsize == 1) {
if (start >= -1 && start < SCREEN_WIDTH - 1) {
*(out2 + 1) = stopColor >> 8;
}
if (start >= 0 && start < SCREEN_WIDTH) {
*(out2) = startColor >> 8;
}
} else if (hsize == 2) {
if (start >= -2 && start < SCREEN_WIDTH - 2) {
*(out2 + 2) = stopColor >> 8;
}
if (start >= -1 && start < SCREEN_WIDTH - 1) {
*(out2 + 1) = ((startColor + stopColor) / 2) >> 8;
}
if (start >= 0 && start < SCREEN_WIDTH) {
*(out2) = startColor >> 8;
}
} else {
int32 currentXPos = start;
colorSize /= hsize;
hsize++;
if (hsize % 2) {
hsize /= 2;
if (currentXPos >= 0 && currentXPos < SCREEN_WIDTH) {
*(out2) = startColor >> 8;
}
out2++;
currentXPos++;
startColor += colorSize;
} else {
hsize /= 2;
}
do {
if (currentXPos >= 0 && currentXPos < SCREEN_WIDTH) {
*(out2) = startColor >> 8;
}
currentXPos++;
startColor += colorSize;
if (currentXPos >= 0 && currentXPos < SCREEN_WIDTH) {
*(out2 + 1) = startColor >> 8;
}
currentXPos++;
out2 += 2;
startColor += colorSize;
} while (--hsize);
}
}
}
out += SCREEN_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 < SCREEN_HEIGHT) {
int16 stop = ptr1[SCREEN_HEIGHT]; // stop
int16 start = ptr1[0]; // start
ptr1++;
int32 hsize = stop - start;
if (hsize >= 0) {
uint16 startColor = ptr2[0];
uint16 stopColor = ptr2[SCREEN_HEIGHT];
int32 currentXPos = start;
uint8 *out2 = start + out;
ptr2++;
if (hsize == 0) {
if (currentXPos >= 0 && currentXPos < SCREEN_WIDTH) {
*(out2) = (uint8)(((startColor + stopColor) / 2) >> 8);
}
} else {
int16 colorSize = stopColor - startColor;
if (hsize == 1) {
uint16 currentColor = startColor;
hsize++;
hsize /= 2;
currentColor &= 0xFF;
currentColor += startColor;
if (currentXPos >= 0 && currentXPos < SCREEN_WIDTH) {
*(out2) = currentColor >> 8;
}
currentColor &= 0xFF;
startColor += colorSize;
currentColor = ((currentColor & (0xFF00)) | ((((currentColor & 0xFF) << (hsize & 0xFF))) & 0xFF));
currentColor += startColor;
currentXPos++;
if (currentXPos >= 0 && currentXPos < SCREEN_WIDTH) {
*(out2 + 1) = currentColor >> 8;
}
} 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 < SCREEN_WIDTH) {
*(out2) = currentColor >> 8;
}
out2++;
currentXPos++;
startColor += colorSize;
currentColor &= 0xFF;
currentColor += startColor;
if (currentXPos >= 0 && currentXPos < SCREEN_WIDTH) {
*(out2) = currentColor >> 8;
}
currentColor &= 0xFF;
startColor += colorSize;
currentColor = ((currentColor & (0xFF00)) | ((((currentColor & 0xFF) << (hsize & 0xFF))) & 0xFF));
currentColor += startColor;
currentXPos++;
if (currentXPos >= 0 && currentXPos < SCREEN_WIDTH) {
*(out2 + 1) = currentColor >> 8;
}
} 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 < SCREEN_WIDTH) {
*(out2) = currentColor >> 8;
}
out2++;
currentXPos++;
} else {
hsize /= 2;
}
do {
currentColor &= 0xFF;
currentColor += startColor;
if (currentXPos >= 0 && currentXPos < SCREEN_WIDTH) {
*(out2) = currentColor >> 8;
}
currentXPos++;
currentColor &= 0xFF;
startColor += colorSize;
currentColor = ((currentColor & (0xFF00)) | ((((currentColor & 0xFF) << (hsize & 0xFF))) & 0xFF));
currentColor += startColor;
if (currentXPos >= 0 && currentXPos < SCREEN_WIDTH) {
*(out2 + 1) = currentColor >> 8;
}
currentXPos++;
out2 += 2;
startColor += colorSize;
} while (--hsize);
}
}
}
}
out += SCREEN_WIDTH;
currentLine++;
} while (--renderLoop);
}
void Renderer::renderPolygonsMarble(uint8 *out, int vtop, int32 vsize, int32 color) const {
}
void Renderer::renderPolygons(int32 renderType, int32 color, int vleft, int vright, int vtop, int vbottom) {
uint8 *out = (uint8*)_engine->frontVideoBuffer.getBasePtr(0, vtop);
const int32 vsize = vbottom - vtop + 1;
switch (renderType) {
case POLYGONTYPE_FLAT:
renderPolygonsFlat(out, vtop, vsize, color);
break;
case POLYGONTYPE_COPPER:
renderPolygonsCopper(out, vtop, vsize, color);
break;
case POLYGONTYPE_BOPPER:
renderPolygonsBopper(out, vtop, vsize, color);
break;
case POLYGONTYPE_TELE:
renderPolygonsTele(out, vtop, vsize, color);
break;
case POLYGONTYPE_TRAS:
renderPolygonsTras(out, vtop, vsize, color);
break;
case POLYGONTYPE_TRAME:
renderPolygonTrame(out, vtop, vsize, color);
break;
case POLYGONTYPE_GOURAUD:
renderPolygonsGouraud(out, vtop, vsize, color);
break;
case POLYGONTYPE_DITHER:
renderPolygonsDither(out, vtop, vsize, color);
break;
case POLYGONTYPE_MARBLE:
renderPolygonsMarble(out, vtop, vsize, color);
break;
default:
warning("RENDER WARNING: Unsuported render type %d", renderType);
break;
}
}
void Renderer::renderPolygons(int32 polyRenderType, int32 color) {
int vleft = 0;
int vright = 0;
int vtop = 0;
int vbottom = 0;
vertexData *vertices = (vertexData *)vertexCoordinates;
computePolygons(polyRenderType, vertices, numOfVertex, vleft, vright, vtop, vbottom);
renderPolygons(polyRenderType, color, vleft, vright, vtop, vbottom);
}
void Renderer::circleFill(int32 x, int32 y, int32 radius, int8 color) {
radius += 1;
for (int32 currentLine = -radius; currentLine <= radius; currentLine++) {
double width;
if (ABS(currentLine) != radius) {
width = sin(acos((float)currentLine / (float)radius));
} else {
width = 0;
}
width *= radius;
if (width < 0) {
width = -width;
}
_engine->_interface->drawLine((int32)(x - width), currentLine + y, (int32)(x + width), currentLine + y, color);
}
}
int32 Renderer::renderModelElements(int32 numOfPrimitives, uint8 *pointer, renderTabEntry **renderTabEntryPtr) {
int16 counter;
int16 type;
int32 bestDepth;
int32 currentDepth;
int32 bestPoly = 0;
// int32 ecx;
pointTab *currentVertex;
// prepare polygons
uint8 *edi = renderTab7; // renderTab7 coordinates buffer
int16 temp = *((const int16 *)pointer); // we read the number of polygons
pointer += 2;
uint8 *renderV19 = nullptr; // RECHECK THIS
if (temp) {
int16 primitiveCounter = temp; // the number of primitives = the number of polygons
do { // loop that load all the polygons
uint8 *render23 = edi;
const polyHeader *currentPolyHeader = (const polyHeader *)pointer;
//ecx = *((int32*) pointer);
pointer += 2;
int16 polyRenderType = currentPolyHeader->renderType;
// TODO: RECHECK coordinates axis
if (polyRenderType >= 9) {
polyHeader *destinationHeader = (polyHeader *)edi;
destinationHeader->renderType = currentPolyHeader->renderType - 2;
destinationHeader->numOfVertex = currentPolyHeader->numOfVertex;
destinationHeader->colorIndex = currentPolyHeader->colorIndex;
pointer += 2;
edi += 4;
counter = destinationHeader->numOfVertex;
bestDepth = -32000;
renderV19 = edi;
do {
const polyVertexHeader *currentPolyVertex = (const polyVertexHeader *)pointer;
int16 shadeValue = currentPolyHeader->colorIndex + shadeTable[currentPolyVertex->shadeEntry];
computedVertex *currentComputedVertex = (computedVertex *)edi;
currentComputedVertex->shadeValue = shadeValue;
currentVertex = &flattenPoints[currentPolyVertex->dataOffset / sizeof(pointTab)];
pointTab *destinationVertex = (pointTab *)(edi + 2);
destinationVertex->x = currentVertex->x;
destinationVertex->y = currentVertex->y;
edi += sizeof(pointTab);
pointer += 4;
currentDepth = currentVertex->z;
if (currentDepth > bestDepth) {
bestDepth = currentDepth;
}
} while (--counter);
} else if (polyRenderType >= POLYGONTYPE_GOURAUD) { // only 1 shade value is used
polyHeader *destinationHeader = (polyHeader *)edi;
destinationHeader->renderType = currentPolyHeader->renderType - 7;
destinationHeader->numOfVertex = currentPolyHeader->numOfVertex;
int16 color = currentPolyHeader->colorIndex;
int16 shadeEntry = *((const int16 *)(pointer + 2));
pointer += 4;
*((int16 *)(edi + 2)) = color + shadeTable[shadeEntry];
edi += 4;
renderV19 = edi;
bestDepth = -32000;
counter = destinationHeader->numOfVertex;
do {
int32 eax = *((const int16 *)pointer);
pointer += 2;
currentVertex = &flattenPoints[eax / sizeof(pointTab)];
pointTab *destinationVertex = (pointTab *)(edi + 2);
destinationVertex->x = currentVertex->x;
destinationVertex->y = currentVertex->y;
edi += sizeof(pointTab);
currentDepth = currentVertex->z;
if (currentDepth > bestDepth) {
bestDepth = currentDepth;
}
} while (--counter);
} else { // no shade is used
polyHeader *destinationHeader = (polyHeader *)edi;
destinationHeader->renderType = currentPolyHeader->renderType;
destinationHeader->numOfVertex = currentPolyHeader->numOfVertex;
destinationHeader->colorIndex = currentPolyHeader->colorIndex;
pointer += 2;
edi += 4;
bestDepth = -32000;
renderV19 = edi;
int32 eax = 0;
counter = currentPolyHeader->numOfVertex;
do {
eax = *((const int16 *)pointer);
pointer += 2;
currentVertex = &flattenPoints[eax / sizeof(pointTab)];
pointTab *destinationVertex = (pointTab *)(edi + 2);
destinationVertex->x = currentVertex->x;
destinationVertex->y = currentVertex->y;
edi += sizeof(pointTab);
currentDepth = currentVertex->z;
if (currentDepth > bestDepth) {
bestDepth = currentDepth;
}
} while (--(counter));
}
uint8 *render24 = edi;
edi = renderV19;
int32 render25 = bestDepth;
int16 ax = *((const int16 *)(edi + 4));
int16 bx = *((const int16 *)(edi + 8));
ax -= *((const int16 *)(edi + 16));
bx -= *((const int16 *)(edi + 2));
ax *= bx;
bestDepth = ax;
bx = currentDepth;
ax = *((const int16 *)(edi + 2));
int16 cx = *((const int16 *)(edi + 10));
ax -= *((const int16 *)(edi + 14));
cx -= *((const int16 *)(edi + 4));
ax *= cx;
ax -= bestDepth;
currentDepth -= (bx)-1; // peut-etre une erreur la
if (currentDepth < 0) {
edi = render23;
} else {
numOfPrimitives++;
(*renderTabEntryPtr)->depth = render25;
(*renderTabEntryPtr)->renderType = 1;
(*renderTabEntryPtr)->dataPtr = render23;
(*renderTabEntryPtr)++;
edi = render24;
}
} while (--primitiveCounter);
}
// prepare lines
temp = *((const int16 *)pointer);
pointer += 2;
if (temp) {
numOfPrimitives += temp;
do {
const lineData *lineDataPtr = (const lineData *)pointer;
lineCoordinates *lineCoordinatesPtr = (lineCoordinates *)edi;
if (*((const int16 *)&lineDataPtr->p1) % 6 != 0 || *((const int16 *)&lineDataPtr->p2) % 6 != 0) {
error("RENDER ERROR: lineDataPtr reference is malformed!");
}
const int32 point1 = *((const int16 *)&lineDataPtr->p1) / 6;
const int32 point2 = *((const int16 *)&lineDataPtr->p2) / 6;
const int32 param = *((const int32 *)&lineDataPtr->data);
*((int32 *)&lineCoordinatesPtr->data) = param;
*((int16 *)&lineCoordinatesPtr->x1) = flattenPoints[point1].x;
*((int16 *)&lineCoordinatesPtr->y1) = flattenPoints[point1].y;
*((int16 *)&lineCoordinatesPtr->x2) = flattenPoints[point2].x;
*((int16 *)&lineCoordinatesPtr->y2) = flattenPoints[point2].y;
bestDepth = flattenPoints[point1].z;
int32 depth = flattenPoints[point2].z;
if (depth >= bestDepth) {
bestDepth = depth;
}
(*renderTabEntryPtr)->depth = bestDepth;
(*renderTabEntryPtr)->renderType = 0;
(*renderTabEntryPtr)->dataPtr = edi;
(*renderTabEntryPtr)++;
pointer += 8;
edi += 12;
} while (--temp);
}
// prepare spheres
temp = *((const int16 *)pointer);
pointer += 2;
if (temp) {
numOfPrimitives += temp;
do {
uint8 color2 = *(pointer + 1);
int16 center = *((const uint16 *)(pointer + 6));
int16 size = *((const uint16 *)(pointer + 4));
*(uint8 *)edi = color2;
*((int16 *)(edi + 1)) = flattenPoints[center / sizeof(pointTab)].x;
*((int16 *)(edi + 3)) = flattenPoints[center / sizeof(pointTab)].y;
*((int16 *)(edi + 5)) = size;
(*renderTabEntryPtr)->depth = flattenPoints[center / sizeof(pointTab)].z;
(*renderTabEntryPtr)->renderType = 2;
(*renderTabEntryPtr)->dataPtr = edi;
(*renderTabEntryPtr)++;
pointer += 8;
edi += 7;
} while (--temp);
}
const renderTabEntry *renderTabEntryPtr2 = renderTab;
renderTabEntry *renderTabSortedPtr = renderTabSorted;
for (int32 i = 0; i < numOfPrimitives; i++) { // then we sort the polygones | WARNING: very slow | TODO: improve this
renderTabEntryPtr2 = renderTab;
int16 bestZ = -0x7FFF;
for (int32 j = 0; j < numOfPrimitives; j++) {
if (renderTabEntryPtr2->depth > bestZ) {
bestZ = renderTabEntryPtr2->depth;
bestPoly = j;
}
renderTabEntryPtr2++;
}
renderTabSortedPtr->depth = renderTab[bestPoly].depth;
renderTabSortedPtr->renderType = renderTab[bestPoly].renderType;
renderTabSortedPtr->dataPtr = renderTab[bestPoly].dataPtr;
renderTabSortedPtr++;
renderTab[bestPoly].depth = -0x7FFF;
}
renderTabEntryPtr2 = renderTabSorted;
// prepare to render elements
if (numOfPrimitives == 0) {
_engine->_redraw->renderRect.right = -1;
_engine->_redraw->renderRect.bottom = -1;
_engine->_redraw->renderRect.left = -1;
_engine->_redraw->renderRect.top = -1;
return -1;
}
int16 primitiveCounter = numOfPrimitives;
renderV19 = pointer;
do {
type = renderTabEntryPtr2->renderType;
pointer = renderTabEntryPtr2->dataPtr;
renderV19 += 8;
switch (type) {
case RENDERTYPE_DRAWLINE: { // draw a line
const lineCoordinates *lineCoordinatesPtr = (const lineCoordinates *)pointer;
int16 color = (*((const int32 *)&lineCoordinatesPtr->data) & 0xFF00) >> 8;
const int32 x1 = *((const int16 *)&lineCoordinatesPtr->x1);
const int32 y1 = *((const int16 *)&lineCoordinatesPtr->y1);
const int32 x2 = *((const int16 *)&lineCoordinatesPtr->x2);
const int32 y2 = *((const int16 *)&lineCoordinatesPtr->y2);
_engine->_interface->drawLine(x1, y1, x2, y2, color);
break;
}
case RENDERTYPE_DRAWPOLYGON: { // draw a polygon
int32 eax = *((const int *)pointer);
pointer += 4;
int16 polyRenderType = eax & 0xFF;
numOfVertex = (eax & 0xFF00) >> 8;
int16 color = (eax & 0xFF0000) >> 16;
uint8 *destPtr = (uint8 *)vertexCoordinates;
for (int32 i = 0; i < (numOfVertex * 3); i++) {
*((int16 *)destPtr) = *((const int16 *)pointer);
destPtr += 2;
pointer += 2;
}
renderPolygons(polyRenderType, color);
break;
}
case RENDERTYPE_DRAWSPHERE: { // draw a sphere
const int32 circleParam1 = *(const uint8 *)pointer;
const int32 circleParam4 = *((const int16 *)(pointer + 1));
const int32 circleParam5 = *((const int16 *)(pointer + 3));
int32 circleParam3 = *((const int16 *)(pointer + 5));
if (!isUsingOrhoProjection) {
circleParam3 = (circleParam3 * cameraPosY) / (cameraPosX + *(const int16 *)pointer);
} else {
circleParam3 = (circleParam3 * 34) >> 9;
}
circleParam3 += 3;
if (circleParam4 + circleParam3 > _engine->_redraw->renderRect.right) {
_engine->_redraw->renderRect.right = circleParam4 + circleParam3;
}
if (circleParam4 - circleParam3 < _engine->_redraw->renderRect.left) {
_engine->_redraw->renderRect.left = circleParam4 - circleParam3;
}
if (circleParam5 + circleParam3 > _engine->_redraw->renderRect.bottom) {
_engine->_redraw->renderRect.bottom = circleParam5 + circleParam3;
}
if (circleParam5 - circleParam3 < _engine->_redraw->renderRect.top) {
_engine->_redraw->renderRect.top = circleParam5 - circleParam3;
}
circleParam3 -= 3;
circleFill(circleParam4, circleParam5, circleParam3, circleParam1);
break;
}
default:
break;
}
pointer = renderV19;
renderTabEntryPtr2++;
} while (--primitiveCounter);
return 0;
}
int32 Renderer::renderAnimatedModel(uint8 *bodyPtr, renderTabEntry *renderTabEntryPtr) {
// int32 *tmpLightMatrix;
int32 numOfPoints = *((const uint16 *)bodyPtr);
bodyPtr += 2;
const uint8 *pointsPtr = bodyPtr;
bodyPtr += numOfPoints * sizeof(pointTab);
int32 numOfElements = *((const uint16 *)bodyPtr);
bodyPtr += 2;
uint8 *elementsPtr = bodyPtr;
const uint8 *elementsPtr2 = elementsPtr;
int32 *modelMatrix = matricesTable;
processRotatedElement(modelMatrix, pointsPtr, renderAngleX, renderAngleY, renderAngleZ, (const elementEntry *)elementsPtr);
elementsPtr += sizeof(elementEntry);
const elementEntry *elemEntryPtr = (const elementEntry *)elementsPtr;
int32 numOfPrimitives = 0;
if (numOfElements - 1 != 0) {
numOfPrimitives = numOfElements - 1;
modelMatrix = &matricesTable[9];
do {
int16 boneType = elemEntryPtr->flag;
if (boneType == 0) {
processRotatedElement(modelMatrix, pointsPtr, elemEntryPtr->rotateX, elemEntryPtr->rotateY, elemEntryPtr->rotateZ, elemEntryPtr); // rotation
} else if (boneType == 1) {
processTranslatedElement(modelMatrix, pointsPtr, elemEntryPtr->rotateX, elemEntryPtr->rotateY, elemEntryPtr->rotateZ, elemEntryPtr); // translation
}
modelMatrix += 9;
elementsPtr += sizeof(elementEntry);
elemEntryPtr = (elementEntry *)elementsPtr;
} while (--numOfPrimitives);
}
numOfPrimitives = numOfPoints;
const pointTab *pointPtr = (pointTab *)computedPoints;
pointTab *pointPtrDest = (pointTab *)flattenPoints;
if (isUsingOrhoProjection) { // 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 / 512 + orthoProjX;
pointPtrDest->y = (((coX - coZ) * 12) - coY * 30) / 512 + 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 += cameraPosX;
if (coZ <= 0) {
coZ = 0x7FFFFFFF;
}
// X projection
{
coX = orthoProjX + ((coX * cameraPosY) / 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 * cameraPosZ) / 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 *shadePtr = (int32 *)elementsPtr;
int32 numOfShades = *((const uint16 *)shadePtr);
shadePtr = (int32 *)(((uint8 *)shadePtr) + 2);
if (numOfShades) { // process normal data
uint8 *currentShadeDestination = (uint8 *)shadeTable;
int32 *lightMatrix = matricesTable;
const uint8 *pri2Ptr3;
numOfPrimitives = numOfElements;
const uint8 *tmpElemPtr = pri2Ptr3 = elementsPtr2 + 18;
do { // for each element
numOfShades = *((const uint16 *)tmpElemPtr);
if (numOfShades) {
int32 numShades = numOfShades;
shadeMatrix[0] = (*lightMatrix) * lightX;
shadeMatrix[1] = (*(lightMatrix + 1)) * lightX;
shadeMatrix[2] = (*(lightMatrix + 2)) * lightX;
shadeMatrix[3] = (*(lightMatrix + 3)) * lightY;
shadeMatrix[4] = (*(lightMatrix + 4)) * lightY;
shadeMatrix[5] = (*(lightMatrix + 5)) * lightY;
shadeMatrix[6] = (*(lightMatrix + 6)) * lightZ;
shadeMatrix[7] = (*(lightMatrix + 7)) * lightZ;
shadeMatrix[8] = (*(lightMatrix + 8)) * lightZ;
do { // for each normal
const int16 *colPtr = (const int16 *)shadePtr;
int16 col1 = *((const int16 *)colPtr++);
int16 col2 = *((const int16 *)colPtr++);
int16 col3 = *((const int16 *)colPtr++);
int32 color = shadeMatrix[0] * col1 + shadeMatrix[1] * col2 + shadeMatrix[2] * col3;
color += shadeMatrix[3] * col1 + shadeMatrix[4] * col2 + shadeMatrix[5] * col3;
color += shadeMatrix[6] * col1 + shadeMatrix[7] * col2 + shadeMatrix[8] * col3;
int32 shade = 0;
if (color > 0) {
color >>= 14;
const uint8 *tmpShadePtr = (const uint8 *)shadePtr;
color /= *((const uint16 *)(tmpShadePtr + 6));
shade = (uint16)color;
}
*((uint16 *)currentShadeDestination) = shade;
currentShadeDestination += 2;
shadePtr += 2;
} while (--numShades);
}
tmpElemPtr = pri2Ptr3 = pri2Ptr3 + sizeof(elementEntry); // next element
/*tmpLightMatrix =*/lightMatrix = lightMatrix + 9;
} while (--numOfPrimitives);
}
return renderModelElements(numOfPrimitives, (uint8 *)shadePtr, &renderTabEntryPtr);
}
void Renderer::prepareIsoModel(uint8 *bodyPtr) { // loadGfxSub
bodyHeaderStruct *bodyHeader;
int32 bp = 36;
int32 bx = sizeof(elementEntry);
bodyHeader = (bodyHeaderStruct *)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 & 0x80)) {
return;
}
bodyHeader->bodyFlag |= 0x80;
if (!(bodyHeader->bodyFlag & 2)) { // no animation applicable
return;
}
int16 offsetToData = bodyHeader->offsetToData;
uint8 *bodyDataPtr = bodyPtr + offsetToData + 16; // headersize
int16 numOfElement1 = *((const int16 *)bodyDataPtr);
uint8 *ptr2 = bodyDataPtr + 2 + numOfElement1 * sizeof(pointTab);
int16 numOfPoint = *((const int16 *)ptr2);
uint8 *ptrToKeyData = ptr2 + 2;
for (int32 i = 0; i < numOfPoint; i++) {
ptrToKeyData += sizeof(elementEntry);
*((int16 *)(ptrToKeyData + 6)) = (*((const int16 *)(ptrToKeyData + 6)) * bp) / bx;
}
}
int32 Renderer::renderIsoModel(int32 x, int32 y, int32 z, int32 angleX, int32 angleY, int32 angleZ, uint8 *bodyPtr) { // AffObjetIso
renderAngleX = angleX;
renderAngleY = angleY;
renderAngleZ = angleZ;
// model render size reset
_engine->_redraw->renderRect.left = 32767;
_engine->_redraw->renderRect.top = 32767;
_engine->_redraw->renderRect.right = -32767;
_engine->_redraw->renderRect.bottom = -32767;
if (isUsingOrhoProjection) {
renderX = x;
renderY = y;
renderZ = z;
} else {
getBaseRotationPosition(x, y, z);
renderX = destX - baseRotPosX;
renderY = destY - baseRotPosY; // RECHECK
renderZ = destZ - baseRotPosZ;
}
int16 bodyHeader = *((const uint16 *)bodyPtr);
// jump after the header
uint8 *ptr = bodyPtr + 16 + *((const uint16 *)(bodyPtr + 14));
if (bodyHeader & 2) { // if animated
// the mostly used renderer code
// restart at the beginning of the renderTable
return renderAnimatedModel(ptr, renderTab);
}
error("Unsupported unanimated model render!");
return 0;
}
void Renderer::copyActorInternAnim(const uint8 *bodyPtrSrc, uint8 *bodyPtrDest) {
// check if both characters allow animation
if (!(*((const int16 *)bodyPtrSrc) & 2)) {
return;
}
if (!(*((const int16 *)bodyPtrDest) & 2)) {
return;
}
// skip header
bodyPtrSrc += 16;
bodyPtrDest += 16;
*((uint32 *)bodyPtrDest) = *((const uint32 *)bodyPtrSrc);
*((uint32 *)(bodyPtrDest + 4)) = *((const uint32 *)(bodyPtrSrc + 4));
bodyPtrSrc = bodyPtrSrc + *((const int16 *)(bodyPtrSrc - 2));
const int32 srcNumPoints = *((const int16 *)bodyPtrSrc);
// skip vertices
bodyPtrSrc = bodyPtrSrc + srcNumPoints * sizeof(pointTab) + 2;
int16 cx = *((const int16 *)bodyPtrSrc);
bodyPtrDest = bodyPtrDest + *((const int16 *)(bodyPtrDest - 2));
const int32 destNumPoints = *((const int16 *)bodyPtrDest);
// skip vertices
bodyPtrDest = bodyPtrDest + destNumPoints * sizeof(pointTab) + 2;
int16 ax = *((const int16 *)bodyPtrDest);
if (cx > ax) {
cx = ax;
}
bodyPtrSrc += 10;
bodyPtrDest += 10;
for (int32 i = 0; i < cx; i++) {
*((uint32 *)bodyPtrDest) = *((const uint32 *)bodyPtrSrc);
*((uint32 *)(bodyPtrDest + 4)) = *((const uint32 *)(bodyPtrSrc + 4));
bodyPtrDest += 30;
bodyPtrSrc += 30;
}
}
void Renderer::renderBehaviourModel(const Common::Rect &rect, int32 y, int32 angle, uint8 *entityPtr) {
renderBehaviourModel(rect.left, rect.top, rect.right, rect.bottom, y, angle, entityPtr);
}
void Renderer::renderBehaviourModel(int32 boxLeft, int32 boxTop, int32 boxRight, int32 boxBottom, int32 y, int32 angle, uint8 *entityPtr) {
int32 tmpBoxRight = boxRight;
int32 ypos = boxBottom + boxTop;
ypos >>= 1;
int32 xpos = boxRight + boxLeft;
xpos >>= 1;
setOrthoProjection(xpos, ypos, 0);
_engine->_interface->setClip(Common::Rect(boxLeft, boxTop, tmpBoxRight, 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, 50, &move);
}
renderIsoModel(0, y, 0, 0, newAngle, 0, entityPtr);
} else {
renderIsoModel(0, y, 0, 0, angle, 0, entityPtr);
}
}
void Renderer::renderInventoryItem(int32 x, int32 y, uint8 *itemBodyPtr, int32 angle, int32 param) {
setCameraPosition(x, y, 128, 200, 200);
setCameraAngle(0, 0, 0, 60, 0, 0, param);
renderIsoModel(0, 0, 0, 0, angle, 0, itemBodyPtr);
}
} // namespace TwinE