mirror of
https://github.com/libretro/scummvm.git
synced 2025-04-14 12:43:30 +00:00
1741 lines
53 KiB
C++
1741 lines
53 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 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#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/parser/body.h"
|
|
#include "twine/renderer/redraw.h"
|
|
#include "twine/renderer/shadeangletab.h"
|
|
#include "twine/resources/resources.h"
|
|
#include "twine/scene/actor.h"
|
|
#include "twine/scene/grid.h"
|
|
#include "twine/scene/movements.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) {
|
|
}
|
|
|
|
Renderer::~Renderer() {
|
|
free(_polyTab);
|
|
free(_colorProgressionBuffer);
|
|
}
|
|
|
|
void Renderer::init(int32 w, int32 h) {
|
|
_polyTabSize = _engine->height() * 6;
|
|
_polyTab = (int16 *)malloc(_polyTabSize * sizeof(int16));
|
|
_colorProgressionBuffer = (int16 *)malloc(_polyTabSize * sizeof(int16));
|
|
_holomap_polytab_1_1 = &_polyTab[_engine->height() * 0];
|
|
_holomap_polytab_2_1 = &_polyTab[_engine->height() * 1];
|
|
_holomap_polytab_1_2 = &_polyTab[_engine->height() * 2];
|
|
_holomap_polytab_2_2 = &_polyTab[_engine->height() * 3];
|
|
_holomap_polytab_1_3 = &_polyTab[_engine->height() * 4];
|
|
_holomap_polytab_2_3 = &_polyTab[_engine->height() * 5];
|
|
}
|
|
|
|
IVec3 &Renderer::projectPositionOnScreen(int32 cX, int32 cY, int32 cZ) {
|
|
if (_isUsingOrthoProjection) {
|
|
_projPos.x = ((cX - cZ) * 24) / ISO_SCALE + _orthoProjPos.x;
|
|
_projPos.y = (((cX + cZ) * 12) - cY * 30) / ISO_SCALE + _orthoProjPos.y;
|
|
_projPos.z = cZ - cY - cX;
|
|
return _projPos;
|
|
}
|
|
|
|
if (_baseRotPos.z - cZ < 0) {
|
|
_projPos.x = 0;
|
|
_projPos.y = 0;
|
|
_projPos.z = 0;
|
|
return _projPos;
|
|
}
|
|
|
|
cX -= _baseRotPos.x;
|
|
cY -= _baseRotPos.y;
|
|
cZ = _baseRotPos.z - cZ;
|
|
|
|
int32 posZ = cZ + _cameraDepthOffset;
|
|
if (posZ <= 0) {
|
|
posZ = 0x7FFF;
|
|
}
|
|
|
|
_projPos.x = (cX * _cameraScaleX) / posZ + _orthoProjPos.x;
|
|
_projPos.y = (-cY * _cameraScaleY) / posZ + _orthoProjPos.y;
|
|
_projPos.z = posZ;
|
|
return _projPos;
|
|
}
|
|
|
|
void Renderer::setCameraPosition(int32 x, int32 y, int32 depthOffset, int32 scaleX, int32 scaleY) {
|
|
_orthoProjPos.x = x;
|
|
_orthoProjPos.y = y;
|
|
|
|
_cameraDepthOffset = depthOffset;
|
|
_cameraScaleX = scaleX;
|
|
_cameraScaleY = scaleY;
|
|
|
|
_isUsingOrthoProjection = false;
|
|
}
|
|
|
|
void Renderer::setBaseTranslation(int32 x, int32 y, int32 z) {
|
|
_baseTransPos.x = x;
|
|
_baseTransPos.y = y;
|
|
_baseTransPos.z = z;
|
|
}
|
|
|
|
void Renderer::setOrthoProjection(int32 x, int32 y, int32 z) {
|
|
_orthoProjPos.x = x;
|
|
_orthoProjPos.y = y;
|
|
_orthoProjPos.z = z;
|
|
|
|
_isUsingOrthoProjection = true;
|
|
}
|
|
|
|
void Renderer::baseMatrixTranspose() {
|
|
SWAP(_baseMatrix.row1.y, _baseMatrix.row2.x);
|
|
SWAP(_baseMatrix.row1.z, _baseMatrix.row3.x);
|
|
SWAP(_baseMatrix.row2.z, _baseMatrix.row3.y);
|
|
}
|
|
|
|
IVec3 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.x = (int32)(sin(Zradians) * sin(Yradians) * SCENE_SIZE_HALFF);
|
|
_baseMatrix.row1.y = (int32)(-cos(Zradians) * SCENE_SIZE_HALFF);
|
|
_baseMatrix.row1.z = (int32)(sin(Zradians) * cos(Yradians) * SCENE_SIZE_HALFF);
|
|
_baseMatrix.row2.x = (int32)(cos(Zradians) * sin(Xradians) * SCENE_SIZE_HALFF);
|
|
_baseMatrix.row2.y = (int32)(sin(Zradians) * sin(Xradians) * SCENE_SIZE_HALFF);
|
|
_baseMatrix.row3.x = (int32)(cos(Zradians) * cos(Xradians) * SCENE_SIZE_HALFF);
|
|
_baseMatrix.row3.y = (int32)(sin(Zradians) * cos(Xradians) * SCENE_SIZE_HALFF);
|
|
|
|
int32 matrixElem = _baseMatrix.row2.x;
|
|
|
|
_baseMatrix.row2.x = (int32)(sin(Yradians) * matrixElem + SCENE_SIZE_HALFF * cos(Yradians) * cos(Xradians));
|
|
_baseMatrix.row2.z = (int32)(cos(Yradians) * matrixElem - SCENE_SIZE_HALFF * sin(Yradians) * cos(Xradians));
|
|
|
|
matrixElem = _baseMatrix.row3.x;
|
|
|
|
_baseMatrix.row3.x = (int32)(sin(Yradians) * matrixElem - SCENE_SIZE_HALFF * sin(Xradians) * cos(Yradians));
|
|
_baseMatrix.row3.z = (int32)(cos(Yradians) * matrixElem + SCENE_SIZE_HALFF * sin(Xradians) * sin(Yradians));
|
|
|
|
if (transpose) {
|
|
baseMatrixTranspose();
|
|
}
|
|
_baseRotPos = getBaseRotationPosition(_baseTransPos.x, _baseTransPos.y, _baseTransPos.z);
|
|
|
|
return _baseRotPos;
|
|
}
|
|
|
|
IVec3 Renderer::getBaseRotationPosition(int32 x, int32 y, int32 z) {
|
|
const int32 vx = (_baseMatrix.row1.x * x + _baseMatrix.row1.y * y + _baseMatrix.row1.z * z) / SCENE_SIZE_HALF;
|
|
const int32 vy = (_baseMatrix.row2.x * x + _baseMatrix.row2.y * y + _baseMatrix.row2.z * z) / SCENE_SIZE_HALF;
|
|
const int32 vz = (_baseMatrix.row3.x * x + _baseMatrix.row3.y * y + _baseMatrix.row3.z * z) / SCENE_SIZE_HALF;
|
|
return IVec3(vx, vy, vz);
|
|
}
|
|
|
|
IVec3 Renderer::getCameraAnglePositions(int32 x, int32 y, int32 z) {
|
|
const int32 vx = (_baseMatrix.row1.x * x + _baseMatrix.row2.x * y + _baseMatrix.row3.x * z) / SCENE_SIZE_HALF;
|
|
const int32 vy = (_baseMatrix.row1.y * x + _baseMatrix.row2.y * y + _baseMatrix.row3.y * z) / SCENE_SIZE_HALF;
|
|
const int32 vz = (_baseMatrix.row1.z * x + _baseMatrix.row2.z * y + _baseMatrix.row3.z * z) / SCENE_SIZE_HALF;
|
|
return IVec3(vx, vy, vz);
|
|
}
|
|
|
|
IVec3 Renderer::translateGroup(int32 x, int32 y, int32 z) {
|
|
const int32 vx = (_shadeMatrix.row1.x * x + _shadeMatrix.row1.y * y + _shadeMatrix.row1.z * z) / SCENE_SIZE_HALF;
|
|
const int32 vy = (_shadeMatrix.row2.x * x + _shadeMatrix.row2.y * y + _shadeMatrix.row2.z * z) / SCENE_SIZE_HALF;
|
|
const int32 vz = (_shadeMatrix.row3.x * x + _shadeMatrix.row3.y * y + _shadeMatrix.row3.z * z) / SCENE_SIZE_HALF;
|
|
return IVec3(vx, vy, vz);
|
|
}
|
|
|
|
void Renderer::setCameraAngle(int32 transPosX, int32 transPosY, int32 transPosZ, int32 rotPosX, int32 rotPosY, int32 rotPosZ, int32 param6) {
|
|
_baseTransPos.x = transPosX;
|
|
_baseTransPos.y = transPosY;
|
|
_baseTransPos.z = transPosZ;
|
|
|
|
setBaseRotation(rotPosX, rotPosY, rotPosZ);
|
|
|
|
_baseRotPos.z += param6;
|
|
|
|
_baseTransPos = updateCameraAnglePositions();
|
|
}
|
|
|
|
IVec3 Renderer::updateCameraAnglePositions(int zShift) {
|
|
return getCameraAnglePositions(_baseRotPos.x, _baseRotPos.y, _baseRotPos.z + zShift);
|
|
}
|
|
|
|
IVec3 Renderer::getHolomapRotation(const int32 angleX, const int32 angleY, const int32 angleZ) const {
|
|
int32 rotX = angleX * 2 + 1000;
|
|
|
|
int32 rotY;
|
|
if (angleY == ANGLE_0) {
|
|
rotY = ANGLE_0;
|
|
} else {
|
|
rotY = -shadeAngleTable[ClampAngle(angleY)] * rotX / SCENE_SIZE_HALF;
|
|
rotX = shadeAngleTable[ClampAngle(angleY + ANGLE_90)] * rotX / SCENE_SIZE_HALF;
|
|
}
|
|
|
|
int32 rotZ;
|
|
if (angleZ == ANGLE_0) {
|
|
rotZ = ANGLE_0;
|
|
} else {
|
|
rotZ = -shadeAngleTable[ClampAngle(angleZ)] * rotX / SCENE_SIZE_HALF;
|
|
rotX = shadeAngleTable[ClampAngle(angleZ + ANGLE_90)] * rotX / SCENE_SIZE_HALF;
|
|
}
|
|
|
|
const int32 row1X = _baseMatrix.row1.x * rotX;
|
|
const int32 row1Y = _baseMatrix.row1.y * rotY;
|
|
const int32 row1Z = _baseMatrix.row1.z * rotZ;
|
|
const int32 row2X = _baseMatrix.row2.x * rotX;
|
|
const int32 row2Y = _baseMatrix.row2.y * rotY;
|
|
const int32 row2Z = _baseMatrix.row2.z * rotZ;
|
|
const int32 row3X = _baseMatrix.row3.x * rotX;
|
|
const int32 row3Y = _baseMatrix.row3.y * rotY;
|
|
const int32 row3Z = _baseMatrix.row3.z * rotZ;
|
|
IVec3 vec;
|
|
vec.x = (row1X + row1Y + row1Z) / SCENE_SIZE_HALF;
|
|
vec.y = (row2X + row2Y + row2Z) / SCENE_SIZE_HALF;
|
|
vec.z = (row3X + row3Y + row3Z) / SCENE_SIZE_HALF;
|
|
return vec;
|
|
}
|
|
|
|
void Renderer::applyRotation(IMatrix3x3 *targetMatrix, const IMatrix3x3 *currentMatrix, const IVec3 &angleVec) {
|
|
IMatrix3x3 matrix1;
|
|
IMatrix3x3 matrix2;
|
|
|
|
if (angleVec.x) {
|
|
int32 angle = angleVec.x;
|
|
int32 angleVar2 = shadeAngleTable[ClampAngle(angle)];
|
|
angle += ANGLE_90;
|
|
int32 angleVar1 = shadeAngleTable[ClampAngle(angle)];
|
|
|
|
matrix1.row1.x = currentMatrix->row1.x;
|
|
matrix1.row2.x = currentMatrix->row2.x;
|
|
matrix1.row3.x = currentMatrix->row3.x;
|
|
|
|
matrix1.row1.y = (currentMatrix->row1.z * angleVar2 + currentMatrix->row1.y * angleVar1) / SCENE_SIZE_HALF;
|
|
matrix1.row1.z = (currentMatrix->row1.z * angleVar1 - currentMatrix->row1.y * angleVar2) / SCENE_SIZE_HALF;
|
|
matrix1.row2.y = (currentMatrix->row2.z * angleVar2 + currentMatrix->row2.y * angleVar1) / SCENE_SIZE_HALF;
|
|
matrix1.row2.z = (currentMatrix->row2.z * angleVar1 - currentMatrix->row2.y * angleVar2) / SCENE_SIZE_HALF;
|
|
matrix1.row3.y = (currentMatrix->row3.z * angleVar2 + currentMatrix->row3.y * angleVar1) / SCENE_SIZE_HALF;
|
|
matrix1.row3.z = (currentMatrix->row3.z * angleVar1 - currentMatrix->row3.y * angleVar2) / SCENE_SIZE_HALF;
|
|
} else {
|
|
matrix1 = *currentMatrix;
|
|
}
|
|
|
|
if (angleVec.z) {
|
|
int32 angle = angleVec.z;
|
|
int32 angleVar2 = shadeAngleTable[ClampAngle(angle)];
|
|
angle += ANGLE_90;
|
|
int32 angleVar1 = shadeAngleTable[ClampAngle(angle)];
|
|
|
|
matrix2.row1.z = matrix1.row1.z;
|
|
matrix2.row2.z = matrix1.row2.z;
|
|
matrix2.row3.z = matrix1.row3.z;
|
|
|
|
matrix2.row1.x = (matrix1.row1.y * angleVar2 + matrix1.row1.x * angleVar1) / SCENE_SIZE_HALF;
|
|
matrix2.row1.y = (matrix1.row1.y * angleVar1 - matrix1.row1.x * angleVar2) / SCENE_SIZE_HALF;
|
|
matrix2.row2.x = (matrix1.row2.y * angleVar2 + matrix1.row2.x * angleVar1) / SCENE_SIZE_HALF;
|
|
matrix2.row2.y = (matrix1.row2.y * angleVar1 - matrix1.row2.x * angleVar2) / SCENE_SIZE_HALF;
|
|
matrix2.row3.x = (matrix1.row3.y * angleVar2 + matrix1.row3.x * angleVar1) / SCENE_SIZE_HALF;
|
|
matrix2.row3.y = (matrix1.row3.y * angleVar1 - matrix1.row3.x * angleVar2) / SCENE_SIZE_HALF;
|
|
} else {
|
|
matrix2 = matrix1;
|
|
}
|
|
|
|
if (angleVec.y) {
|
|
int32 angle = angleVec.y;
|
|
int32 angleVar2 = shadeAngleTable[ClampAngle(angle)];
|
|
angle += ANGLE_90;
|
|
int32 angleVar1 = shadeAngleTable[ClampAngle(angle)];
|
|
|
|
targetMatrix->row1.y = matrix2.row1.y;
|
|
targetMatrix->row2.y = matrix2.row2.y;
|
|
targetMatrix->row3.y = matrix2.row3.y;
|
|
|
|
targetMatrix->row1.x = (matrix2.row1.x * angleVar1 - matrix2.row1.z * angleVar2) / SCENE_SIZE_HALF;
|
|
targetMatrix->row1.z = (matrix2.row1.x * angleVar2 + matrix2.row1.z * angleVar1) / SCENE_SIZE_HALF;
|
|
targetMatrix->row2.x = (matrix2.row2.x * angleVar1 - matrix2.row2.z * angleVar2) / SCENE_SIZE_HALF;
|
|
targetMatrix->row2.z = (matrix2.row2.x * angleVar2 + matrix2.row2.z * angleVar1) / SCENE_SIZE_HALF;
|
|
|
|
targetMatrix->row3.x = (matrix2.row3.x * angleVar1 - matrix2.row3.z * angleVar2) / SCENE_SIZE_HALF;
|
|
targetMatrix->row3.z = (matrix2.row3.x * angleVar2 + matrix2.row3.z * angleVar1) / SCENE_SIZE_HALF;
|
|
} else {
|
|
*targetMatrix = matrix2;
|
|
}
|
|
}
|
|
|
|
void Renderer::applyPointsRotation(const Common::Array<BodyVertex> &vertices, int32 firstPoint, int32 numPoints, I16Vec3 *destPoints, const IMatrix3x3 *rotationMatrix, const IVec3 &destPos) {
|
|
for (int32 i = 0; i < numPoints; ++i) {
|
|
const BodyVertex &vertex = vertices[i + firstPoint];
|
|
destPoints->x = ((rotationMatrix->row1.x * vertex.x + rotationMatrix->row1.y * vertex.y + rotationMatrix->row1.z * vertex.z) / SCENE_SIZE_HALF) + destPos.x;
|
|
destPoints->y = ((rotationMatrix->row2.x * vertex.x + rotationMatrix->row2.y * vertex.y + rotationMatrix->row2.z * vertex.z) / SCENE_SIZE_HALF) + destPos.y;
|
|
destPoints->z = ((rotationMatrix->row3.x * vertex.x + rotationMatrix->row3.y * vertex.y + rotationMatrix->row3.z * vertex.z) / SCENE_SIZE_HALF) + destPos.z;
|
|
|
|
destPoints++;
|
|
}
|
|
}
|
|
|
|
void Renderer::processRotatedElement(IMatrix3x3 *targetMatrix, const Common::Array<BodyVertex> &vertices, int32 rotX, int32 rotY, int32 rotZ, const BodyBone &bone, ModelData *modelData) {
|
|
const int32 firstPoint = bone.firstVertex;
|
|
const int32 numOfPoints = bone.numVertices;
|
|
const IVec3 renderAngle(rotX, rotY, rotZ);
|
|
|
|
const IMatrix3x3 *currentMatrix;
|
|
IVec3 destPos;
|
|
// if its the first point
|
|
if (bone.isRoot()) {
|
|
currentMatrix = &_baseMatrix;
|
|
} else {
|
|
const int32 pointIdx = bone.vertex;
|
|
const int32 matrixIndex = bone.parent;
|
|
assert(matrixIndex >= 0 && matrixIndex < ARRAYSIZE(_matricesTable));
|
|
currentMatrix = &_matricesTable[matrixIndex];
|
|
|
|
destPos = modelData->computedPoints[pointIdx];
|
|
}
|
|
|
|
applyRotation(targetMatrix, currentMatrix, renderAngle);
|
|
|
|
if (!numOfPoints) {
|
|
warning("RENDER WARNING: No points in this model!");
|
|
}
|
|
|
|
applyPointsRotation(vertices, firstPoint, numOfPoints, &modelData->computedPoints[firstPoint], targetMatrix, destPos);
|
|
}
|
|
|
|
void Renderer::applyPointsTranslation(const Common::Array<BodyVertex> &vertices, int32 firstPoint, int32 numPoints, I16Vec3 *destPoints, const IMatrix3x3 *translationMatrix, const IVec3 &angleVec, const IVec3 &destPos) {
|
|
for (int32 i = 0; i < numPoints; ++i) {
|
|
const BodyVertex &vertex = vertices[i + firstPoint];
|
|
const int32 tmpX = vertex.x + angleVec.x;
|
|
const int32 tmpY = vertex.y + angleVec.y;
|
|
const int32 tmpZ = vertex.z + angleVec.z;
|
|
|
|
destPoints->x = ((translationMatrix->row1.x * tmpX + translationMatrix->row1.y * tmpY + translationMatrix->row1.z * tmpZ) / SCENE_SIZE_HALF) + destPos.x;
|
|
destPoints->y = ((translationMatrix->row2.x * tmpX + translationMatrix->row2.y * tmpY + translationMatrix->row2.z * tmpZ) / SCENE_SIZE_HALF) + destPos.y;
|
|
destPoints->z = ((translationMatrix->row3.x * tmpX + translationMatrix->row3.y * tmpY + translationMatrix->row3.z * tmpZ) / SCENE_SIZE_HALF) + destPos.z;
|
|
|
|
destPoints++;
|
|
}
|
|
}
|
|
|
|
void Renderer::processTranslatedElement(IMatrix3x3 *targetMatrix, const Common::Array<BodyVertex> &vertices, int32 rotX, int32 rotY, int32 rotZ, const BodyBone &bone, ModelData *modelData) {
|
|
IVec3 renderAngle;
|
|
renderAngle.x = rotX;
|
|
renderAngle.y = rotY;
|
|
renderAngle.z = rotZ;
|
|
|
|
IVec3 destPos;
|
|
|
|
if (bone.isRoot()) { // base point
|
|
*targetMatrix = _baseMatrix;
|
|
} else { // dependent
|
|
const int32 pointsIdx = bone.vertex;
|
|
destPos = modelData->computedPoints[pointsIdx];
|
|
|
|
const int32 matrixIndex = bone.parent;
|
|
assert(matrixIndex >= 0 && matrixIndex < ARRAYSIZE(_matricesTable));
|
|
*targetMatrix = _matricesTable[matrixIndex];
|
|
}
|
|
|
|
applyPointsTranslation(vertices, bone.firstVertex, bone.numVertices, &modelData->computedPoints[bone.firstVertex], targetMatrix, renderAngle, destPos);
|
|
}
|
|
|
|
void Renderer::setLightVector(int32 angleX, int32 angleY, int32 angleZ) {
|
|
// TODO: RECHECK THIS
|
|
/*_cameraAngleX = angleX;
|
|
_cameraAngleY = angleY;
|
|
_cameraAngleZ = angleZ;*/
|
|
const int32 normalUnit = 64;
|
|
const IVec3 renderAngle(angleX, angleY, angleZ);
|
|
applyRotation(&_shadeMatrix, &_baseMatrix, renderAngle);
|
|
_lightNorm = translateGroup(0, 0, normalUnit - 5);
|
|
}
|
|
|
|
static FORCEINLINE int16 clamp(int16 x, int16 a, int16 b) {
|
|
return x < a ? a : (x > b ? b : x);
|
|
}
|
|
|
|
bool Renderer::computePolygons(int16 polyRenderType, const Vertex *vertices, int32 numVertices) {
|
|
uint8 vertexParam1 = vertices[numVertices - 1].colorIndex;
|
|
int16 currentVertexX = vertices[numVertices - 1].x;
|
|
int16 currentVertexY = vertices[numVertices - 1].y;
|
|
const int16 *polyTabBegin = _polyTab;
|
|
const int16 *polyTabEnd = &_polyTab[_polyTabSize - 1];
|
|
const int16 *colProgressBufStart = _colorProgressionBuffer;
|
|
const int16 *colProgressBufEnd = &_colorProgressionBuffer[_polyTabSize - 1];
|
|
const int screenHeight = _engine->height();
|
|
|
|
const Common::Rect &clip = _engine->_interface->_clip;
|
|
if (!clip.isEmpty()) {
|
|
int32 vleft;
|
|
int32 vright;
|
|
int32 vtop;
|
|
int32 vbottom;
|
|
|
|
vleft = vtop = SCENE_SIZE_MAX;
|
|
vright = vbottom = SCENE_SIZE_MIN;
|
|
|
|
for (int32 i = 0; i < numVertices; i++) {
|
|
if (vertices[i].x < vleft)
|
|
vleft = vertices[i].x;
|
|
if (vertices[i].x > vright)
|
|
vright = vertices[i].x;
|
|
if (vertices[i].y < vtop)
|
|
vtop = vertices[i].y;
|
|
if (vertices[i].y > vbottom)
|
|
vbottom = vertices[i].y;
|
|
}
|
|
// no vertices
|
|
if (vtop > vbottom) {
|
|
return false;
|
|
}
|
|
if (vright <= clip.left || vleft >= clip.right || vbottom <= clip.top || vtop >= clip.bottom) {
|
|
debug(10, "Clipped %i:%i:%i:%i, clip rect(%i:%i:%i:%i)", vleft, vtop, vright, vbottom, clip.left, clip.top, clip.right, clip.bottom);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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 ? screenHeight : 0);
|
|
int16 *outPtr = &_polyTab[polyTabIndex]; // outPtr is the output ptr in the renderTab
|
|
|
|
float slope = (float)hsize / (float)vsize;
|
|
slope = up ? -slope : slope;
|
|
|
|
for (int16 i = 0; i <= vsize; i++) {
|
|
if (outPtr >= polyTabBegin && outPtr <= polyTabEnd) {
|
|
*outPtr = xpos;
|
|
}
|
|
outPtr += direction;
|
|
xpos += slope;
|
|
}
|
|
|
|
if (polyRenderType >= POLYGONTYPE_GOURAUD) { // we must compute the color progression
|
|
int16 *outPtr2 = &_colorProgressionBuffer[polyTabIndex];
|
|
|
|
for (int16 i = 0; i <= vsize; i++) {
|
|
if (outPtr2 >= colProgressBufStart && outPtr2 <= colProgressBufEnd) {
|
|
*outPtr2 = cvalue;
|
|
}
|
|
outPtr2 += direction;
|
|
cvalue += cdelta;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Renderer::renderPolygonsCopper(int vtop, int32 vsize, uint16 color) const {
|
|
uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
|
|
const int16 *ptr1 = &_polyTab[vtop];
|
|
const int screenWidth = _engine->width();
|
|
const int screenHeight = _engine->height();
|
|
|
|
int32 renderLoop = vsize;
|
|
if (vtop < 0) {
|
|
out += screenWidth * ABS(vtop);
|
|
renderLoop -= ABS(vtop);
|
|
}
|
|
if (renderLoop > screenHeight) {
|
|
renderLoop = screenHeight;
|
|
}
|
|
int32 sens = 1;
|
|
|
|
for (int32 currentLine = 0; currentLine < renderLoop; ++currentLine) {
|
|
int16 xMin = ptr1[0];
|
|
int16 xMax = ptr1[screenHeight];
|
|
|
|
ptr1++;
|
|
uint8 *pDest = out + xMin;
|
|
|
|
for (; xMin <= xMax; xMin++) {
|
|
*pDest++ = (uint8)color;
|
|
}
|
|
|
|
color += sens;
|
|
if (!(color & 0xF)) {
|
|
sens = -sens;
|
|
if (sens < 0) {
|
|
color += sens;
|
|
}
|
|
}
|
|
out += screenWidth;
|
|
}
|
|
}
|
|
|
|
void Renderer::renderPolygonsBopper(int vtop, int32 vsize, uint16 color) const {
|
|
uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
|
|
const int16 *ptr1 = &_polyTab[vtop];
|
|
const int screenWidth = _engine->width();
|
|
const int screenHeight = _engine->height();
|
|
int32 renderLoop = vsize;
|
|
if (vtop < 0) {
|
|
out += screenWidth * ABS(vtop);
|
|
renderLoop -= ABS(vtop);
|
|
}
|
|
if (renderLoop > screenHeight) {
|
|
renderLoop = screenHeight;
|
|
}
|
|
int32 sens = 1;
|
|
int32 line = 2;
|
|
for (int32 currentLine = 0; currentLine < renderLoop; ++currentLine) {
|
|
int16 xMin = ptr1[0];
|
|
int16 xMax = ptr1[screenHeight];
|
|
ptr1++;
|
|
|
|
uint8 *pDest = out + xMin;
|
|
|
|
for (; xMin <= xMax; xMin++) {
|
|
*pDest++ = (uint8)color;
|
|
}
|
|
|
|
line--;
|
|
if (!line) {
|
|
line = 2;
|
|
color += sens;
|
|
if (!(color & 0xF)) {
|
|
sens = -sens;
|
|
if (sens < 0) {
|
|
color += sens;
|
|
}
|
|
}
|
|
}
|
|
out += screenWidth;
|
|
}
|
|
}
|
|
|
|
void Renderer::renderPolygonsFlat(int vtop, int32 vsize, uint16 color) const {
|
|
uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
|
|
const int16 *ptr1 = &_polyTab[vtop];
|
|
const int screenWidth = _engine->width();
|
|
const int screenHeight = _engine->height();
|
|
int32 renderLoop = vsize;
|
|
if (vtop < 0) {
|
|
out += screenWidth * ABS(vtop);
|
|
renderLoop -= ABS(vtop);
|
|
}
|
|
if (renderLoop > screenHeight) {
|
|
renderLoop = screenHeight;
|
|
}
|
|
for (int32 currentLine = 0; currentLine < renderLoop; ++currentLine) {
|
|
const int16 start = ptr1[0];
|
|
const int16 stop = ptr1[screenHeight];
|
|
ptr1++;
|
|
const int32 hsize = stop - start;
|
|
|
|
for (int32 j = start; j <= hsize + start; j++) {
|
|
if (j >= 0 && j < screenWidth) {
|
|
out[j] = color;
|
|
}
|
|
}
|
|
out += screenWidth;
|
|
}
|
|
}
|
|
|
|
#define ROL16(x, b) (((x) << (b)) | ((x) >> (16 - (b))))
|
|
|
|
void Renderer::renderPolygonsTele(int vtop, int32 vsize, uint16 color) const {
|
|
uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
|
|
const int16 *ptr1 = &_polyTab[vtop];
|
|
const int screenWidth = _engine->width();
|
|
const int screenHeight = _engine->height();
|
|
|
|
int32 renderLoop = vsize;
|
|
if (vtop < 0) {
|
|
out += screenWidth * ABS(vtop);
|
|
renderLoop -= ABS(vtop);
|
|
}
|
|
if (renderLoop > screenHeight) {
|
|
renderLoop = screenHeight;
|
|
}
|
|
|
|
uint16 acc = 17371;
|
|
color &= 0xFF;
|
|
uint16 col;
|
|
for (int32 currentLine = 0; currentLine < renderLoop; ++currentLine) {
|
|
int16 xMin = ptr1[0];
|
|
int16 xMax = ptr1[screenHeight];
|
|
++ptr1;
|
|
uint8 *pDest = out + xMin;
|
|
col = xMin;
|
|
|
|
for (; xMin <= xMax; xMin++) {
|
|
col = ((col + acc) & 0xFF03) + (uint16)color;
|
|
acc = ROL16(acc, 2) + 1;
|
|
|
|
*pDest++ = (uint8)col;
|
|
}
|
|
out += screenWidth;
|
|
}
|
|
}
|
|
|
|
void Renderer::renderPolygonsTrans(int vtop, int32 vsize, uint16 color) const {
|
|
uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
|
|
const int16 *ptr1 = &_polyTab[vtop];
|
|
const int screenWidth = _engine->width();
|
|
const int screenHeight = _engine->height();
|
|
|
|
do {
|
|
int16 start = ptr1[0];
|
|
int16 stop = ptr1[screenHeight];
|
|
|
|
ptr1++;
|
|
int32 hsize = stop - start;
|
|
|
|
if (hsize >= 0) {
|
|
hsize++;
|
|
uint8 *out2 = start + out;
|
|
*out2 = (*(out2)&0x0F) | color;
|
|
out2++;
|
|
}
|
|
out += screenWidth;
|
|
} while (--vsize);
|
|
}
|
|
|
|
// Used e.g for the legs of the horse or the ears of most characters
|
|
void Renderer::renderPolygonsTrame(int vtop, int32 vsize, uint16 color) const {
|
|
uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
|
|
const int16 *ptr1 = &_polyTab[vtop];
|
|
const int screenWidth = _engine->width();
|
|
const int screenHeight = _engine->height();
|
|
|
|
int32 renderLoop = vsize;
|
|
if (vtop < 0) {
|
|
out += screenWidth * ABS(vtop);
|
|
renderLoop -= ABS(vtop);
|
|
}
|
|
if (renderLoop > screenHeight) {
|
|
renderLoop = screenHeight;
|
|
}
|
|
int32 pair = 0;
|
|
for (int32 currentLine = 0; currentLine < renderLoop; ++currentLine) {
|
|
int16 start = ptr1[0];
|
|
int16 stop = ptr1[screenHeight];
|
|
ptr1++;
|
|
uint8 *out2 = start + out;
|
|
stop = ((stop - start) + 1) / 2;
|
|
if (stop > 0) {
|
|
pair ^= 1; // paire/impair
|
|
if ((start & 1) ^ pair) {
|
|
out2++;
|
|
}
|
|
|
|
for (; stop > 0; stop--) {
|
|
*out2 = color;
|
|
out2 += 2;
|
|
}
|
|
}
|
|
|
|
out += screenWidth;
|
|
}
|
|
}
|
|
|
|
void Renderer::renderPolygonsGouraud(int vtop, int32 vsize) const {
|
|
uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
|
|
const int16 *ptr1 = &_polyTab[vtop];
|
|
const int16 *ptr2 = &_colorProgressionBuffer[vtop];
|
|
const int screenWidth = _engine->width();
|
|
const int screenHeight = _engine->height();
|
|
int32 renderLoop = vsize;
|
|
if (vtop < 0) {
|
|
out += screenWidth * ABS(vtop);
|
|
renderLoop -= ABS(vtop);
|
|
}
|
|
if (renderLoop > screenHeight) {
|
|
renderLoop = screenHeight;
|
|
}
|
|
for (int32 currentLine = 0; currentLine < renderLoop; ++currentLine) {
|
|
uint16 startColor = ptr2[0];
|
|
const uint16 stopColor = ptr2[screenHeight];
|
|
|
|
int16 colorDiff = stopColor - startColor;
|
|
|
|
const int16 stop = ptr1[screenHeight];
|
|
const int16 start = ptr1[0];
|
|
|
|
ptr1++;
|
|
uint8 *out2 = start + out;
|
|
int32 hsize = stop - start;
|
|
|
|
ptr2++;
|
|
|
|
if (hsize == 0) {
|
|
if (start >= 0 && start < screenWidth) {
|
|
*out2 = ((startColor + stopColor) / 2) / 256; // average of the 2 colors
|
|
}
|
|
} else if (hsize == 1) {
|
|
if (start >= 1 && start < screenWidth - 1) {
|
|
*(out2 + 1) = stopColor / 256;
|
|
}
|
|
|
|
if (start >= 0 && start < screenWidth) {
|
|
*out2 = startColor / 256;
|
|
}
|
|
} else if (hsize == 2) {
|
|
if (start >= 2 && start < screenWidth - 2) {
|
|
*(out2 + 2) = stopColor / 256;
|
|
}
|
|
|
|
if (start >= 1 && start < screenWidth - 1) {
|
|
*(out2 + 1) = ((startColor + stopColor) / 2) / 256; // average of the 2 colors
|
|
}
|
|
|
|
if (start >= 0 && start < screenWidth) {
|
|
*out2 = startColor / 256;
|
|
}
|
|
} else if (hsize > 0) {
|
|
int32 currentXPos = start;
|
|
colorDiff /= hsize;
|
|
hsize++;
|
|
|
|
if (hsize % 2) {
|
|
if (currentXPos >= 0 && currentXPos < screenWidth) {
|
|
*out2 = startColor / 256;
|
|
}
|
|
++out2;
|
|
++currentXPos;
|
|
startColor += colorDiff;
|
|
}
|
|
hsize /= 2;
|
|
|
|
do {
|
|
for (int i = 0; i < 2; ++i) {
|
|
if (currentXPos >= 0 && currentXPos < screenWidth) {
|
|
*out2 = startColor / 256;
|
|
}
|
|
++out2;
|
|
++currentXPos;
|
|
startColor += colorDiff;
|
|
}
|
|
} while (--hsize);
|
|
}
|
|
out += screenWidth;
|
|
}
|
|
}
|
|
|
|
// used for the most of the heads of the characters and the horse body
|
|
void Renderer::renderPolygonsDither(int vtop, int32 vsize) const {
|
|
uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
|
|
const int16 *ptr1 = &_polyTab[vtop];
|
|
const int16 *ptr2 = &_colorProgressionBuffer[vtop];
|
|
const int screenWidth = _engine->width();
|
|
const int screenHeight = _engine->height();
|
|
|
|
int32 renderLoop = vsize;
|
|
if (vtop < 0) {
|
|
out += screenWidth * ABS(vtop);
|
|
renderLoop -= ABS(vtop);
|
|
}
|
|
if (renderLoop > screenHeight) {
|
|
renderLoop = screenHeight;
|
|
}
|
|
for (int32 currentLine = 0; currentLine < renderLoop; ++currentLine) {
|
|
int16 stop = ptr1[screenHeight];
|
|
int16 start = ptr1[0];
|
|
ptr1++;
|
|
int32 hsize = stop - start;
|
|
if (hsize < 0) {
|
|
out += screenWidth;
|
|
continue;
|
|
}
|
|
uint16 startColor = ptr2[0];
|
|
uint16 stopColor = ptr2[screenHeight];
|
|
int32 currentXPos = start;
|
|
|
|
uint8 *out2 = start + out;
|
|
ptr2++;
|
|
|
|
if (hsize == 0) {
|
|
if (currentXPos >= 0 && currentXPos < screenWidth) {
|
|
*out2 = (uint8)(((startColor + stopColor) / 2) / 256); // average of the 2 colors
|
|
}
|
|
} else {
|
|
int16 colorSize = stopColor - startColor;
|
|
if (hsize == 1) {
|
|
uint16 currentColor = startColor;
|
|
hsize++;
|
|
hsize /= 2;
|
|
|
|
currentColor &= 0xFF;
|
|
currentColor += startColor;
|
|
if (currentXPos >= 0 && currentXPos < screenWidth) {
|
|
*out2 = currentColor / 256;
|
|
}
|
|
|
|
currentColor &= 0xFF;
|
|
startColor += colorSize;
|
|
currentColor = ((currentColor & (0xFF00)) | ((((currentColor & 0xFF) << (hsize & 0xFF))) & 0xFF));
|
|
currentColor += startColor;
|
|
|
|
currentXPos++;
|
|
if (currentXPos >= 0 && currentXPos < screenWidth) {
|
|
*(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 < screenWidth) {
|
|
*out2 = currentColor / 256;
|
|
}
|
|
|
|
out2++;
|
|
currentXPos++;
|
|
startColor += colorSize;
|
|
|
|
currentColor &= 0xFF;
|
|
currentColor += startColor;
|
|
|
|
if (currentXPos >= 0 && currentXPos < screenWidth) {
|
|
*out2 = currentColor / 256;
|
|
}
|
|
|
|
currentColor &= 0xFF;
|
|
startColor += colorSize;
|
|
currentColor = ((currentColor & (0xFF00)) | ((((currentColor & 0xFF) << (hsize & 0xFF))) & 0xFF));
|
|
currentColor += startColor;
|
|
|
|
currentXPos++;
|
|
if (currentXPos >= 0 && currentXPos < screenWidth) {
|
|
*(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 < screenWidth) {
|
|
*out2 = currentColor / 256;
|
|
}
|
|
out2++;
|
|
currentXPos++;
|
|
} else {
|
|
hsize /= 2;
|
|
}
|
|
|
|
do {
|
|
currentColor &= 0xFF;
|
|
currentColor += startColor;
|
|
if (currentXPos >= 0 && currentXPos < screenWidth) {
|
|
*out2 = currentColor / 256;
|
|
}
|
|
currentXPos++;
|
|
currentColor &= 0xFF;
|
|
startColor += colorSize;
|
|
currentColor = ((currentColor & (0xFF00)) | ((((currentColor & 0xFF) << (hsize & 0xFF))) & 0xFF));
|
|
currentColor += startColor;
|
|
if (currentXPos >= 0 && currentXPos < screenWidth) {
|
|
*(out2 + 1) = currentColor / 256;
|
|
}
|
|
currentXPos++;
|
|
out2 += 2;
|
|
startColor += colorSize;
|
|
} while (--hsize);
|
|
}
|
|
}
|
|
out += screenWidth;
|
|
}
|
|
}
|
|
|
|
void Renderer::renderPolygonsMarble(int vtop, int32 vsize, uint16 color) const {
|
|
const int screenWidth = _engine->width();
|
|
const int screenHeight = _engine->height();
|
|
|
|
uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
|
|
int16 *ptr1 = &_polyTab[vtop];
|
|
|
|
int16 xMin, xMax;
|
|
int16 y = vtop;
|
|
uint8 *pDestLine = out;
|
|
uint8 *pDest;
|
|
int16 *pVerticG = ptr1;
|
|
int16 *pVerticD = &ptr1[screenHeight];
|
|
|
|
uint16 start = (color & 0xFF) << 8;
|
|
uint16 end = color & 0xFF00;
|
|
uint16 delta = end - start + 1; // delta intensity
|
|
int32 step, dc;
|
|
|
|
for (; y <= vsize; y++) {
|
|
xMin = *pVerticG++;
|
|
xMax = *pVerticD++;
|
|
pDest = pDestLine + xMin;
|
|
|
|
dc = xMax - xMin;
|
|
if (dc == 0) {
|
|
// just one
|
|
*pDest++ = (uint8)(end >> 8);
|
|
} else if (dc > 0) {
|
|
step = delta / (dc + 1);
|
|
color = start;
|
|
|
|
for (; xMin <= xMax; xMin++) {
|
|
*pDest++ = (uint8)(color >> 8);
|
|
color += step;
|
|
}
|
|
}
|
|
|
|
pDestLine += screenWidth;
|
|
}
|
|
}
|
|
|
|
void Renderer::renderPolygonsSimplified(int vtop, int32 vsize, uint16 color) const {
|
|
uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, vtop);
|
|
const int16 *ptr1 = &_polyTab[vtop];
|
|
const int16 *ptr2 = &_colorProgressionBuffer[vtop];
|
|
const int screenWidth = _engine->width();
|
|
const int screenHeight = _engine->height();
|
|
|
|
int32 renderLoop = vsize;
|
|
if (vtop < 0) {
|
|
out += screenWidth * ABS(vtop);
|
|
renderLoop -= ABS(vtop);
|
|
}
|
|
if (renderLoop > screenHeight) {
|
|
renderLoop = screenHeight;
|
|
}
|
|
for (int32 currentLine = 0; currentLine < renderLoop; ++currentLine) {
|
|
int16 xMin = MAX<int16>(0, ptr1[0]);
|
|
const int16 xMax = MIN<int16>((int16)(screenWidth - 1), ptr1[screenHeight]);
|
|
uint8 *pDest = out + xMin;
|
|
|
|
color = (*ptr2++) >> 8;
|
|
for (; xMin <= xMax; xMin++) {
|
|
*pDest++ = color;
|
|
}
|
|
++ptr1;
|
|
|
|
out += screenWidth;
|
|
}
|
|
}
|
|
|
|
void Renderer::renderPolygons(const CmdRenderPolygon &polygon, Vertex *vertices, int vtop, int vbottom) {
|
|
if (computePolygons(polygon.renderType, vertices, polygon.numVertices)) {
|
|
const int32 vsize = vbottom - vtop + 1;
|
|
fillVertices(vtop, vsize, polygon.renderType, polygon.colorIndex);
|
|
}
|
|
}
|
|
|
|
void Renderer::fillVertices(int vtop, int32 vsize, uint8 renderType, uint16 color) {
|
|
switch (renderType) {
|
|
case POLYGONTYPE_FLAT:
|
|
renderPolygonsFlat(vtop, vsize, color);
|
|
break;
|
|
case POLYGONTYPE_TELE:
|
|
if (_engine->_cfgfile.PolygonDetails == 0) {
|
|
renderPolygonsFlat(vtop, vsize, color);
|
|
} else {
|
|
renderPolygonsTele(vtop, vsize, color);
|
|
}
|
|
break;
|
|
case POLYGONTYPE_COPPER:
|
|
renderPolygonsCopper(vtop, vsize, color);
|
|
break;
|
|
case POLYGONTYPE_BOPPER:
|
|
renderPolygonsBopper(vtop, vsize, color);
|
|
break;
|
|
case POLYGONTYPE_TRANS:
|
|
renderPolygonsTrans(vtop, vsize, color);
|
|
break;
|
|
case POLYGONTYPE_TRAME: // raster
|
|
renderPolygonsTrame(vtop, vsize, color);
|
|
break;
|
|
case POLYGONTYPE_GOURAUD:
|
|
if (_engine->_cfgfile.PolygonDetails == 0) {
|
|
renderPolygonsSimplified(vtop, vsize, color);
|
|
} else {
|
|
renderPolygonsGouraud(vtop, vsize);
|
|
}
|
|
break;
|
|
case POLYGONTYPE_DITHER:
|
|
if (_engine->_cfgfile.PolygonDetails == 0) {
|
|
renderPolygonsSimplified(vtop, vsize, color);
|
|
} else if (_engine->_cfgfile.PolygonDetails == 1) {
|
|
renderPolygonsGouraud(vtop, vsize);
|
|
} else {
|
|
renderPolygonsDither(vtop, vsize);
|
|
}
|
|
break;
|
|
case POLYGONTYPE_MARBLE:
|
|
renderPolygonsMarble(vtop, vsize, color);
|
|
break;
|
|
default:
|
|
warning("RENDER WARNING: Unsupported render type %d", renderType);
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool Renderer::prepareCircle(int32 x, int32 y, int32 radius) {
|
|
if (radius <= 0) {
|
|
return false;
|
|
}
|
|
int16 left = (int16)(x - radius);
|
|
int16 right = (int16)(x + radius);
|
|
int16 bottom = (int16)(y + radius);
|
|
int16 top = (int16)(y - radius);
|
|
const Common::Rect &clip = _engine->_interface->_clip;
|
|
int16 cleft = clip.left;
|
|
int16 cright = clip.right;
|
|
int16 ctop = clip.top;
|
|
int16 cbottom = clip.bottom;
|
|
|
|
if (left <= cright && right >= cleft && bottom <= cbottom && top >= ctop) {
|
|
if (left < cleft) {
|
|
left = cleft;
|
|
}
|
|
if (bottom > cbottom) {
|
|
bottom = cbottom;
|
|
}
|
|
if (right > cright) {
|
|
right = cright;
|
|
}
|
|
if (top < ctop) {
|
|
top = ctop;
|
|
}
|
|
|
|
int32 r = 0;
|
|
int32 acc = -radius;
|
|
|
|
int16 *start = _polyTab;
|
|
int16 *end = &_polyTab[_engine->height()];
|
|
|
|
while (r <= radius) {
|
|
int32 x1 = x - radius;
|
|
if (x1 < cleft) {
|
|
x1 = cleft;
|
|
}
|
|
|
|
int32 x2 = x + radius;
|
|
if (x2 > cright) {
|
|
x2 = cright;
|
|
}
|
|
|
|
int32 ny = y - r;
|
|
if ((ny >= ctop) && (ny <= cbottom)) {
|
|
start[ny] = (int16)x1;
|
|
end[ny] = (int16)x2;
|
|
}
|
|
|
|
ny = y + r;
|
|
if ((ny >= ctop) && (ny <= cbottom)) {
|
|
start[ny] = (int16)x1;
|
|
end[ny] = (int16)x2;
|
|
}
|
|
|
|
if (acc < 0) {
|
|
acc += r;
|
|
if (acc >= 0) {
|
|
x1 = x - r;
|
|
if (x1 < cleft) {
|
|
x1 = cleft;
|
|
}
|
|
|
|
x2 = x + r;
|
|
if (x2 > cright) {
|
|
x2 = cright;
|
|
}
|
|
|
|
ny = y - radius;
|
|
if ((ny >= ctop) && (ny <= cbottom)) {
|
|
start[ny] = (int16)x1;
|
|
end[ny] = (int16)x2;
|
|
}
|
|
|
|
ny = y + radius;
|
|
if ((ny >= ctop) && (ny <= cbottom)) {
|
|
start[ny] = (int16)x1;
|
|
end[ny] = (int16)x2;
|
|
}
|
|
|
|
--radius;
|
|
acc -= radius;
|
|
}
|
|
}
|
|
|
|
++r;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
uint8 *Renderer::prepareSpheres(const Common::Array<BodySphere> &spheres, int32 &numOfPrimitives, RenderCommand **renderCmds, uint8 *renderBufferPtr, ModelData *modelData) {
|
|
for (const BodySphere &sphere : spheres) {
|
|
CmdRenderSphere *cmd = (CmdRenderSphere *)renderBufferPtr;
|
|
cmd->color = sphere.color;
|
|
cmd->polyRenderType = sphere.fillType;
|
|
cmd->radius = sphere.radius;
|
|
const int16 centerIndex = sphere.vertex;
|
|
cmd->x = modelData->flattenPoints[centerIndex].x;
|
|
cmd->y = modelData->flattenPoints[centerIndex].y;
|
|
cmd->z = modelData->flattenPoints[centerIndex].z;
|
|
|
|
(*renderCmds)->depth = modelData->flattenPoints[centerIndex].z;
|
|
(*renderCmds)->renderType = RENDERTYPE_DRAWSPHERE;
|
|
(*renderCmds)->dataPtr = renderBufferPtr;
|
|
(*renderCmds)++;
|
|
|
|
renderBufferPtr += sizeof(CmdRenderSphere);
|
|
}
|
|
numOfPrimitives += spheres.size();
|
|
return renderBufferPtr;
|
|
}
|
|
|
|
uint8 *Renderer::prepareLines(const Common::Array<BodyLine> &lines, int32 &numOfPrimitives, RenderCommand **renderCmds, uint8 *renderBufferPtr, ModelData *modelData) {
|
|
for (const BodyLine &line : lines) {
|
|
CmdRenderLine *cmd = (CmdRenderLine *)renderBufferPtr;
|
|
cmd->colorIndex = line.color;
|
|
const int32 point1Index = line.vertex1;
|
|
const int32 point2Index = line.vertex2;
|
|
cmd->x1 = modelData->flattenPoints[point1Index].x;
|
|
cmd->y1 = modelData->flattenPoints[point1Index].y;
|
|
cmd->x2 = modelData->flattenPoints[point2Index].x;
|
|
cmd->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);
|
|
}
|
|
numOfPrimitives += lines.size();
|
|
return renderBufferPtr;
|
|
}
|
|
|
|
uint8 *Renderer::preparePolygons(const Common::Array<BodyPolygon> &polygons, int32 &numOfPrimitives, RenderCommand **renderCmds, uint8 *renderBufferPtr, ModelData *modelData) {
|
|
const int16 maxHeight = _engine->height() - 1;
|
|
const int16 maxWidth = _engine->width() - 1;
|
|
|
|
for (const BodyPolygon &polygon : polygons) {
|
|
const uint8 materialType = polygon.materialType;
|
|
const uint8 numVertices = polygon.indices.size();
|
|
assert(numVertices <= 16);
|
|
const int16 colorIndex = polygon.color;
|
|
|
|
int16 bestDepth = -32000;
|
|
|
|
CmdRenderPolygon *destinationPolygon = (CmdRenderPolygon *)renderBufferPtr;
|
|
destinationPolygon->numVertices = numVertices;
|
|
destinationPolygon->top = SCENE_SIZE_MAX;
|
|
destinationPolygon->bottom = SCENE_SIZE_MIN;
|
|
|
|
renderBufferPtr += sizeof(CmdRenderPolygon);
|
|
|
|
Vertex *const vertices = (Vertex *)renderBufferPtr;
|
|
renderBufferPtr += destinationPolygon->numVertices * sizeof(Vertex);
|
|
|
|
Vertex *vertex = vertices;
|
|
|
|
if (materialType >= MAT_GOURAUD) {
|
|
destinationPolygon->renderType = polygon.materialType - (MAT_GOURAUD - POLYGONTYPE_GOURAUD);
|
|
destinationPolygon->colorIndex = polygon.color;
|
|
|
|
for (int16 idx = 0; idx < numVertices; ++idx) {
|
|
const int16 shadeEntry = polygon.intensities[idx];
|
|
const int16 shadeValue = colorIndex + modelData->shadeTable[shadeEntry];
|
|
const int16 vertexIndex = polygon.indices[idx];
|
|
const I16Vec3 *point = &modelData->flattenPoints[vertexIndex];
|
|
|
|
vertex->colorIndex = shadeValue;
|
|
vertex->x = clamp(point->x, 0, maxWidth);
|
|
vertex->y = clamp(point->y, 0, maxHeight);
|
|
destinationPolygon->top = MIN<int>(destinationPolygon->top, vertex->y);
|
|
destinationPolygon->bottom = MAX<int>(destinationPolygon->bottom, vertex->y);
|
|
bestDepth = MAX(bestDepth, point->z);
|
|
++vertex;
|
|
}
|
|
} else {
|
|
if (materialType >= MAT_FLAT) {
|
|
// only 1 shade value is used
|
|
destinationPolygon->renderType = materialType - MAT_FLAT;
|
|
const int16 shadeEntry = polygon.intensities[0];
|
|
const int16 shadeValue = colorIndex + modelData->shadeTable[shadeEntry];
|
|
destinationPolygon->colorIndex = shadeValue;
|
|
} else {
|
|
// no shade is used
|
|
destinationPolygon->renderType = materialType;
|
|
destinationPolygon->colorIndex = colorIndex;
|
|
}
|
|
|
|
for (int16 idx = 0; idx < numVertices; ++idx) {
|
|
const int16 vertexIndex = polygon.indices[idx];
|
|
const I16Vec3 *point = &modelData->flattenPoints[vertexIndex];
|
|
|
|
vertex->colorIndex = destinationPolygon->colorIndex;
|
|
vertex->x = clamp(point->x, 0, maxWidth);
|
|
vertex->y = clamp(point->y, 0, maxHeight);
|
|
destinationPolygon->top = MIN<int>(destinationPolygon->top, vertex->y);
|
|
destinationPolygon->bottom = MAX<int>(destinationPolygon->bottom, vertex->y);
|
|
bestDepth = MAX(bestDepth, point->z);
|
|
++vertex;
|
|
}
|
|
}
|
|
|
|
numOfPrimitives++;
|
|
|
|
(*renderCmds)->depth = bestDepth;
|
|
(*renderCmds)->renderType = RENDERTYPE_DRAWPOLYGON;
|
|
(*renderCmds)->dataPtr = (uint8 *)destinationPolygon;
|
|
(*renderCmds)++;
|
|
}
|
|
|
|
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 BodyData &bodyData, RenderCommand **renderCmds, ModelData *modelData, Common::Rect &modelRect) {
|
|
uint8 *renderBufferPtr = _renderCoordinatesBuffer;
|
|
renderBufferPtr = preparePolygons(bodyData.getPolygons(), numOfPrimitives, renderCmds, renderBufferPtr, modelData);
|
|
renderBufferPtr = prepareLines(bodyData.getLines(), numOfPrimitives, renderCmds, renderBufferPtr, modelData);
|
|
renderBufferPtr = prepareSpheres(bodyData.getSpheres(), numOfPrimitives, renderCmds, renderBufferPtr, modelData);
|
|
|
|
if (numOfPrimitives == 0) {
|
|
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, header->top, header->bottom);
|
|
break;
|
|
}
|
|
case RENDERTYPE_DRAWSPHERE: {
|
|
CmdRenderSphere *sphere = (CmdRenderSphere *)pointer;
|
|
int32 radius = sphere->radius;
|
|
|
|
if (_isUsingOrthoProjection) {
|
|
// * sqrt(sx+sy) / 512 (isometric scale)
|
|
radius = (radius * 34) / ISO_SCALE;
|
|
} else {
|
|
int32 delta = _cameraDepthOffset + sphere->z;
|
|
if (delta == 0) {
|
|
break;
|
|
}
|
|
radius = (sphere->radius * _cameraScaleX) / delta;
|
|
}
|
|
|
|
radius += 3;
|
|
|
|
if (sphere->x + radius > modelRect.right) {
|
|
modelRect.right = sphere->x + radius;
|
|
}
|
|
|
|
if (sphere->x - radius < modelRect.left) {
|
|
modelRect.left = sphere->x - radius;
|
|
}
|
|
|
|
if (sphere->y + radius > modelRect.bottom) {
|
|
modelRect.bottom = sphere->y + radius;
|
|
}
|
|
|
|
if (sphere->y - radius < modelRect.top) {
|
|
modelRect.top = sphere->y - radius;
|
|
}
|
|
|
|
radius -= 3;
|
|
|
|
if (prepareCircle(sphere->x, sphere->y, radius)) {
|
|
const int32 vsize = 2 * radius;
|
|
fillVertices(sphere->y - radius, vsize, sphere->polyRenderType, sphere->color);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
cmds++;
|
|
} while (--primitiveCounter);
|
|
return true;
|
|
}
|
|
|
|
bool Renderer::renderAnimatedModel(ModelData *modelData, const BodyData &bodyData, RenderCommand *renderCmds, const IVec3 &angleVec, const IVec3 &renderPos, Common::Rect &modelRect) {
|
|
const int32 numVertices = bodyData.getNumVertices();
|
|
const int32 numBones = bodyData.getNumBones();
|
|
|
|
const Common::Array<BodyVertex> &vertices = bodyData.getVertices();
|
|
|
|
IMatrix3x3 *modelMatrix = &_matricesTable[0];
|
|
|
|
const BodyBone &firstBone = bodyData.getBone(0);
|
|
processRotatedElement(modelMatrix, vertices, angleVec.x, angleVec.y, angleVec.z, firstBone, modelData);
|
|
|
|
int32 numOfPrimitives = 0;
|
|
|
|
if (numBones - 1 != 0) {
|
|
numOfPrimitives = numBones - 1;
|
|
int boneIdx = 1;
|
|
modelMatrix = &_matricesTable[boneIdx];
|
|
|
|
do {
|
|
const BodyBone &bone = bodyData.getBone(boneIdx);
|
|
const BoneFrame *boneData = bodyData.getBoneState(boneIdx);
|
|
|
|
if (boneData->type == 0) {
|
|
processRotatedElement(modelMatrix, vertices, boneData->x, boneData->y, boneData->z, bone, modelData);
|
|
} else if (boneData->type == 1) {
|
|
processTranslatedElement(modelMatrix, vertices, boneData->x, boneData->y, boneData->z, bone, modelData);
|
|
}
|
|
|
|
++modelMatrix;
|
|
++boneIdx;
|
|
} while (--numOfPrimitives);
|
|
}
|
|
|
|
numOfPrimitives = numVertices;
|
|
|
|
const I16Vec3 *pointPtr = &modelData->computedPoints[0];
|
|
I16Vec3 *pointPtrDest = &modelData->flattenPoints[0];
|
|
|
|
if (_isUsingOrthoProjection) { // use standard projection
|
|
do {
|
|
const int32 coX = pointPtr->x + renderPos.x;
|
|
const int32 coY = pointPtr->y + renderPos.y;
|
|
const int32 coZ = -(pointPtr->z + renderPos.z);
|
|
|
|
// TODO: use projectPositionOnScreen()
|
|
pointPtrDest->x = (coX + coZ) * 24 / ISO_SCALE + _orthoProjPos.x;
|
|
pointPtrDest->y = (((coX - coZ) * 12) - coY * 30) / ISO_SCALE + _orthoProjPos.y;
|
|
pointPtrDest->z = coZ - coX - coY;
|
|
|
|
if (pointPtrDest->x < modelRect.left) {
|
|
modelRect.left = pointPtrDest->x;
|
|
}
|
|
if (pointPtrDest->x > modelRect.right) {
|
|
modelRect.right = pointPtrDest->x;
|
|
}
|
|
|
|
if (pointPtrDest->y < modelRect.top) {
|
|
modelRect.top = pointPtrDest->y;
|
|
}
|
|
if (pointPtrDest->y > modelRect.bottom) {
|
|
modelRect.bottom = pointPtrDest->y;
|
|
}
|
|
|
|
pointPtr++;
|
|
pointPtrDest++;
|
|
} while (--numOfPrimitives);
|
|
} else {
|
|
do {
|
|
int32 coX = pointPtr->x + renderPos.x;
|
|
int32 coY = pointPtr->y + renderPos.y;
|
|
int32 coZ = -(pointPtr->z + renderPos.z);
|
|
|
|
coZ += _cameraDepthOffset;
|
|
|
|
if (coZ <= 0) {
|
|
coZ = 0x7FFFFFFF;
|
|
}
|
|
|
|
// X projection
|
|
{
|
|
coX = _orthoProjPos.x + ((coX * _cameraScaleX) / coZ);
|
|
|
|
if (coX > 0xFFFF) {
|
|
coX = 0x7FFF;
|
|
}
|
|
|
|
pointPtrDest->x = coX;
|
|
|
|
if (pointPtrDest->x < modelRect.left) {
|
|
modelRect.left = pointPtrDest->x;
|
|
}
|
|
|
|
if (pointPtrDest->x > modelRect.right) {
|
|
modelRect.right = pointPtrDest->x;
|
|
}
|
|
}
|
|
|
|
// Y projection
|
|
{
|
|
coY = _orthoProjPos.y + ((-coY * _cameraScaleY) / coZ);
|
|
|
|
if (coY > 0xFFFF) {
|
|
coY = 0x7FFF;
|
|
}
|
|
|
|
pointPtrDest->y = coY;
|
|
|
|
if (pointPtrDest->y < modelRect.top) {
|
|
modelRect.top = pointPtrDest->y;
|
|
}
|
|
if (pointPtrDest->y > modelRect.bottom) {
|
|
modelRect.bottom = pointPtrDest->y;
|
|
}
|
|
}
|
|
|
|
// Z projection
|
|
{
|
|
if (coZ > 0xFFFF) {
|
|
coZ = 0x7FFF;
|
|
}
|
|
|
|
pointPtrDest->z = coZ;
|
|
}
|
|
|
|
pointPtr++;
|
|
pointPtrDest++;
|
|
|
|
} while (--numOfPrimitives);
|
|
}
|
|
|
|
int32 numOfShades = bodyData.getShades().size();
|
|
|
|
if (numOfShades) { // process normal data
|
|
uint16 *currentShadeDestination = (uint16 *)modelData->shadeTable;
|
|
IMatrix3x3 *lightMatrix = &_matricesTable[0];
|
|
|
|
numOfPrimitives = numBones;
|
|
|
|
int shadeIndex = 0;
|
|
int boneIdx = 0;
|
|
do { // for each element
|
|
numOfShades = bodyData.getBone(boneIdx).numOfShades;
|
|
|
|
if (numOfShades) {
|
|
int32 numShades = numOfShades;
|
|
|
|
_shadeMatrix = *lightMatrix * _lightNorm;
|
|
|
|
do { // for each normal
|
|
const BodyShade &shadePtr = bodyData.getShade(shadeIndex);
|
|
|
|
const int32 col1 = (int32)shadePtr.col1;
|
|
const int32 col2 = (int32)shadePtr.col2;
|
|
const int32 col3 = (int32)shadePtr.col3;
|
|
|
|
int32 color = 0;
|
|
color += _shadeMatrix.row1.x * col1 + _shadeMatrix.row1.y * col2 + _shadeMatrix.row1.z * col3;
|
|
color += _shadeMatrix.row2.x * col1 + _shadeMatrix.row2.y * col2 + _shadeMatrix.row2.z * col3;
|
|
color += _shadeMatrix.row3.x * col1 + _shadeMatrix.row3.y * col2 + _shadeMatrix.row3.z * col3;
|
|
|
|
int32 shade = 0;
|
|
|
|
if (color > 0) {
|
|
color >>= 14;
|
|
color /= shadePtr.unk4;
|
|
shade = (uint16)color;
|
|
}
|
|
|
|
*currentShadeDestination = shade;
|
|
currentShadeDestination++;
|
|
++shadeIndex;
|
|
} while (--numShades);
|
|
}
|
|
|
|
++boneIdx;
|
|
++lightMatrix;
|
|
} while (--numOfPrimitives);
|
|
}
|
|
|
|
return renderModelElements(numOfPrimitives, bodyData, &renderCmds, modelData, modelRect);
|
|
}
|
|
|
|
bool Renderer::renderIsoModel(int32 x, int32 y, int32 z, int32 angleX, int32 angleY, int32 angleZ, const BodyData &bodyData, Common::Rect &modelRect) {
|
|
IVec3 renderAngle;
|
|
renderAngle.x = angleX;
|
|
renderAngle.y = angleY;
|
|
renderAngle.z = angleZ;
|
|
|
|
// model render size reset
|
|
modelRect.left = SCENE_SIZE_MAX;
|
|
modelRect.top = SCENE_SIZE_MAX;
|
|
modelRect.right = SCENE_SIZE_MIN;
|
|
modelRect.bottom = SCENE_SIZE_MIN;
|
|
|
|
IVec3 renderPos;
|
|
if (_isUsingOrthoProjection) {
|
|
renderPos.x = x;
|
|
renderPos.y = y;
|
|
renderPos.z = z;
|
|
} else {
|
|
renderPos = getBaseRotationPosition(x, y, z) - _baseRotPos;
|
|
}
|
|
|
|
if (!bodyData.isAnimated()) {
|
|
#if 0
|
|
// TODO: fill modeldata.flattenedpoints
|
|
int32 numOfPrimitives = 0;
|
|
RenderCommand* renderCmds = _renderCmds;
|
|
return renderModelElements(numOfPrimitives, bodyData, &renderCmds, &_modelData, modelRect);
|
|
#else
|
|
error("Unsupported unanimated model render!");
|
|
#endif
|
|
}
|
|
// restart at the beginning of the renderTable
|
|
if (!renderAnimatedModel(&_modelData, bodyData, _renderCmds, renderAngle, renderPos, modelRect)) {
|
|
modelRect.right = -1;
|
|
modelRect.bottom = -1;
|
|
modelRect.left = -1;
|
|
modelRect.top = -1;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Renderer::renderBehaviourModel(const Common::Rect &rect, int32 y, int32 angle, const BodyData &bodyData, ActorMoveStruct &move) {
|
|
int32 boxLeft = rect.left;
|
|
int32 boxTop = rect.top;
|
|
int32 boxRight = rect.right;
|
|
int32 boxBottom = rect.bottom;
|
|
const int32 ypos = (boxBottom + boxTop) / 2;
|
|
const int32 xpos = (boxRight + boxLeft) / 2;
|
|
|
|
setOrthoProjection(xpos, ypos, 0);
|
|
_engine->_interface->setClip(rect);
|
|
|
|
Common::Rect dummy;
|
|
if (angle == -1) {
|
|
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, bodyData, dummy);
|
|
} else {
|
|
renderIsoModel(0, y, 0, ANGLE_0, angle, ANGLE_0, bodyData, dummy);
|
|
}
|
|
_engine->_interface->resetClip();
|
|
}
|
|
|
|
void Renderer::renderInventoryItem(int32 x, int32 y, const BodyData &bodyData, int32 angle, int32 param) {
|
|
setCameraPosition(x, y, 128, 200, 200);
|
|
setCameraAngle(0, 0, 0, 60, 0, 0, param);
|
|
|
|
Common::Rect dummy;
|
|
renderIsoModel(0, 0, 0, ANGLE_0, angle, ANGLE_0, bodyData, dummy);
|
|
}
|
|
|
|
void Renderer::computeHolomapPolygon(int32 top, int32 x1, int32 bottom, int32 x2, int16 *polygonTabPtr) {
|
|
int32 minY = bottom;
|
|
int32 minX = x1;
|
|
if (top < bottom) {
|
|
minY = top;
|
|
top = bottom;
|
|
minX = x2;
|
|
x2 = x1;
|
|
}
|
|
const uint32 deltaY = top - minY;
|
|
int16 *currentPolygonTabEntry = &polygonTabPtr[minY];
|
|
if (minX < x2) {
|
|
const uint32 deltaX = (x2 - minX) * 0x10000;
|
|
const uint32 deltaRatio = deltaX / deltaY;
|
|
uint32 iVar01 = (deltaRatio % deltaY >> 1) + 0x7fffU;
|
|
for (uint32 y = 0; y <= deltaY; ++y) {
|
|
if (currentPolygonTabEntry < _polyTab || currentPolygonTabEntry >= _polyTab + _polyTabSize) {
|
|
currentPolygonTabEntry++;
|
|
continue;
|
|
}
|
|
*currentPolygonTabEntry++ = (int16)x2;
|
|
x2 -= (deltaRatio >> 0x10);
|
|
if ((iVar01 & 0xffff0000U) != 0) {
|
|
x2 += (iVar01 >> 0x10);
|
|
iVar01 = iVar01 & 0xffffU;
|
|
}
|
|
iVar01 -= (deltaRatio & 0xffffU);
|
|
}
|
|
} else {
|
|
const uint32 deltaX = (minX - x2) * 0x10000;
|
|
const uint32 deltaRatio = deltaX / deltaY;
|
|
uint32 iVar01 = (deltaX % deltaY >> 1) + 0x7fffU;
|
|
for (uint32 y = 0; y <= deltaY; ++y) {
|
|
if (currentPolygonTabEntry < _polyTab || currentPolygonTabEntry >= _polyTab + _polyTabSize) {
|
|
currentPolygonTabEntry++;
|
|
continue;
|
|
}
|
|
*currentPolygonTabEntry++ = (int16)x2;
|
|
x2 += (deltaRatio >> 0x10);
|
|
if ((iVar01 & 0xffff0000U) != 0) {
|
|
x2 += (iVar01 >> 0x10);
|
|
iVar01 = iVar01 & 0xffffU;
|
|
}
|
|
iVar01 += (deltaRatio & 0xffffU);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Renderer::fillHolomapPolygons(const Vertex &vertex1, const Vertex &vertex2, const Vertex &texCoord1, const Vertex &texCoord2, int32 &top, int32 &bottom) {
|
|
const int32 yBottom = vertex1.y;
|
|
const int32 yTop = vertex2.y;
|
|
if (yBottom == yTop) {
|
|
return;
|
|
}
|
|
|
|
int16 *polygonTabPtr;
|
|
if (yBottom < yTop) {
|
|
if (yBottom < top) {
|
|
top = yBottom;
|
|
}
|
|
if (bottom < yTop) {
|
|
bottom = yTop;
|
|
}
|
|
computeHolomapPolygon(yTop, vertex2.x, yBottom, vertex1.x, _holomap_polytab_1_1);
|
|
computeHolomapPolygon(yTop, (uint32)(uint16)texCoord2.x, yBottom, (uint32)(uint16)texCoord1.x, _holomap_polytab_1_2);
|
|
polygonTabPtr = _holomap_polytab_1_3;
|
|
} else {
|
|
if (bottom < yBottom) {
|
|
bottom = yBottom;
|
|
}
|
|
if (yTop < top) {
|
|
top = yTop;
|
|
}
|
|
computeHolomapPolygon(yTop, vertex2.x, yBottom, vertex1.x, _holomap_polytab_2_1);
|
|
computeHolomapPolygon(yTop, (uint32)(uint16)texCoord2.x, yBottom, (uint32)(uint16)texCoord1.x, _holomap_polytab_2_2);
|
|
polygonTabPtr = _holomap_polytab_2_3;
|
|
}
|
|
computeHolomapPolygon(yTop, (uint32)(uint16)texCoord2.y, yBottom, (uint32)(uint16)texCoord1.y, polygonTabPtr);
|
|
}
|
|
|
|
void Renderer::renderHolomapVertices(const Vertex vertexCoordinates[3], const Vertex textureCoordinates[3], uint8 *holomapImage, uint32 holomapImageSize) {
|
|
int32 top = SCENE_SIZE_MAX;
|
|
int32 bottom = SCENE_SIZE_MIN;
|
|
fillHolomapPolygons(vertexCoordinates[0], vertexCoordinates[1], textureCoordinates[0], textureCoordinates[1], top, bottom);
|
|
fillHolomapPolygons(vertexCoordinates[1], vertexCoordinates[2], textureCoordinates[1], textureCoordinates[2], top, bottom);
|
|
fillHolomapPolygons(vertexCoordinates[2], vertexCoordinates[0], textureCoordinates[2], textureCoordinates[0], top, bottom);
|
|
renderHolomapPolygons(top, bottom, holomapImage, holomapImageSize);
|
|
}
|
|
|
|
void Renderer::renderHolomapPolygons(int32 top, int32 bottom, uint8 *holomapImage, uint32 holomapImageSize) {
|
|
if (top < 0 || top >= _engine->_frontVideoBuffer.h) {
|
|
return;
|
|
}
|
|
uint8 *screenBufPtr = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(0, top);
|
|
|
|
const int16 *lholomap_polytab_1_1 = _holomap_polytab_1_1 + top;
|
|
const int16 *lholomap_polytab_2_1 = _holomap_polytab_2_1 + top;
|
|
const uint16 *lholomap_polytab_1_2 = (const uint16 *)(_holomap_polytab_1_2 + top);
|
|
const uint16 *lholomap_polytab_1_3 = (const uint16 *)(_holomap_polytab_1_3 + top);
|
|
const uint16 *lholomap_polytab_2_2 = (const uint16 *)(_holomap_polytab_2_2 + top);
|
|
const uint16 *lholomap_polytab_2_3 = (const uint16 *)(_holomap_polytab_2_3 + top);
|
|
|
|
int32 yHeight = bottom - top;
|
|
while (yHeight > -1) {
|
|
int32 u;
|
|
int32 v;
|
|
const int16 left = *lholomap_polytab_1_1++;
|
|
const int16 right = *lholomap_polytab_2_1++;
|
|
const uint32 u0 = u = *lholomap_polytab_1_2++;
|
|
const uint32 v0 = v = *lholomap_polytab_1_3++;
|
|
const uint32 u1 = *lholomap_polytab_2_2++;
|
|
const uint32 v1 = *lholomap_polytab_2_3++;
|
|
const int16 width = right - left;
|
|
if (width > 0) {
|
|
uint8 *pixelBufPtr = screenBufPtr + left;
|
|
|
|
int32 ustep = ((int32)u1 - (int32)u0 + 1) / width;
|
|
int32 vstep = ((int32)v1 - (int32)v0 + 1) / width;
|
|
|
|
for (int16 i = 0; i < width; ++i) {
|
|
// u0 & 0xFF00 is the x position on the image * 256
|
|
// v0 & 0xFF00 is the y position on the image * 256
|
|
const uint32 idx = ((u >> 8) & 0xff) | (v & 0xff00);
|
|
assert(idx < holomapImageSize);
|
|
*pixelBufPtr++ = holomapImage[idx];
|
|
u += ustep;
|
|
v += vstep;
|
|
}
|
|
}
|
|
screenBufPtr += _engine->_frontVideoBuffer.pitch;
|
|
--yHeight;
|
|
}
|
|
}
|
|
|
|
} // namespace TwinE
|