scummvm/engines/cryomni3d/omni3d.cpp
2019-06-01 22:43:48 +02:00

276 lines
6.7 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 "cryomni3d/omni3d.h"
#include "common/rect.h"
namespace CryOmni3D {
void Omni3DManager::init(double hfov) {
_alpha = 0.;
_beta = 0.;
_xSpeed = 0.;
_ySpeed = 0.;
double oppositeSide = tan(hfov / 2.) / (4. / 3.);
double vf = atan2(oppositeSide, 1.);
_vfov = (M_PI_2 - vf - (13. / 180.*M_PI)) * 10. / 9.;
double warpVfov = 155. / 180. * M_PI;
double hypV = 768. / 2. / sin(warpVfov / 2.);
double oppHTot = tan(hfov / 2.) * 16. / 320.;
_helperValue = 2048 * 65536 / (2. * M_PI);
for (int i = 0; i < 31; i++) {
double oppH = (i - 15) * oppHTot;
double angle = atan2(oppH, 1.);
_anglesH[i] = angle;
_hypothenusesH[i] = sqrt(oppH * oppH + 1);
double oppVTot = hypV * _hypothenusesH[i];
for (int j = 0; j < 21; j++) {
double oppV = (j - 20) * oppHTot;
_oppositeV[j] = oppV;
double coord = sqrt(oppV * oppV + _hypothenusesH[i] * _hypothenusesH[i]);
coord = oppVTot / coord;
coord = coord * 65536;
_squaresCoords[i][j] = coord;
}
}
_surface.create(640, 480, Graphics::PixelFormat::createFormatCLUT8());
clearConstraints();
}
Omni3DManager::~Omni3DManager() {
_surface.free();
}
void Omni3DManager::updateCoords(int xDelta, int yDelta, bool useOldSpeed) {
double xDelta1 = xDelta * 0.00025;
double yDelta1 = yDelta * 0.0002;
if (useOldSpeed) {
_xSpeed += xDelta1;
_ySpeed += yDelta1;
} else {
_xSpeed = xDelta1;
_ySpeed = yDelta1;
}
_alpha += _xSpeed;
_beta += _ySpeed;
//debug("alpha = %lf beta = %lf xSpeed = %lf ySpeed = %lf", _alpha, _beta, _xSpeed, _ySpeed);
_xSpeed *= 0.4;
_ySpeed *= 0.6;
if (useOldSpeed) {
if (fabs(_xSpeed) < 0.001) {
_xSpeed = 0.;
}
if (fabs(_ySpeed) < 0.001) {
_ySpeed = 0.;
}
}
if (_alpha < _alphaMin) {
_alpha = _alphaMin;
_xSpeed = 0.;
} else if (_alpha > _alphaMax) {
_alpha = _alphaMax;
_xSpeed = 0.;
}
if (_beta < _betaMin) {
_beta = _betaMin;
_ySpeed = 0.;
} else if (_beta > _betaMax) {
_beta = _betaMax;
_ySpeed = 0.;
}
if (_alpha >= 2. * M_PI) {
_alpha -= 2. * M_PI;
} else if (_alpha < 0.) {
_alpha += 2. * M_PI;
}
_dirtyCoords = true;
updateImageCoords();
}
void Omni3DManager::updateImageCoords() {
if (!_dirtyCoords) {
return;
}
if (_alpha >= 2.*M_PI) {
_alpha -= 2.*M_PI;
} else if (_alpha < 0) {
_alpha += 2.*M_PI;
}
if (_beta > 0.9 * _vfov) {
_beta = 0.9 * _vfov;
} else if (_beta < -0.9 * _vfov) {
_beta = -0.9 * _vfov;
}
double tmp = (2048 * 65536) - 2048 * 65536 / (2. * M_PI) * _alpha;
uint k = 0;
for (uint i = 0; i < 31; i++) {
double v11 = _anglesH[i] + _beta;
double v26 = sin(v11);
double v25 = cos(v11) * _hypothenusesH[i];
uint offset = 80;
uint j;
for (j = 0; j < 20; j++) {
double v16 = atan2(_oppositeV[j], v25);
double v17 = v16 * _helperValue;
double v18 = (384 * 65536) - _squaresCoords[i][j] * v26;
k += 2;
_imageCoords[k + 0] = (int)(tmp + v17);
_imageCoords[k + offset + 0] = (int)(tmp - v17);
_imageCoords[k + 1] = (int) v18;
_imageCoords[k + offset + 1] = (int) v18;
offset -= 4;
}
double v19 = atan2(_oppositeV[j], v25);
k += 2;
_imageCoords[k + 0] = (int)((2048.*65536.) - (_alpha - v19) * _helperValue);
_imageCoords[k + 1] = (int)((384.*65536.) - _squaresCoords[i][j] * v26);
k += 40;
}
_dirtyCoords = false;
_dirty = true;
}
const Graphics::Surface *Omni3DManager::getSurface() {
if (!_sourceSurface) {
return nullptr;
}
if (_dirtyCoords) {
updateImageCoords();
}
if (_dirty) {
uint off = 2;
byte *dst = (byte *)_surface.getBasePtr(0, 0);
const byte *src = (const byte *)_sourceSurface->getBasePtr(0, 0);
for (uint i = 0; i < 30; i++) {
for (uint j = 0; j < 40; j++) {
int x1 = (_imageCoords[off + 2] - _imageCoords[off + 0]) >> 4;
int y1 = (_imageCoords[off + 3] - _imageCoords[off + 1]) >> 4;
int x1_ = (_imageCoords[off + 82 + 2] - _imageCoords[off + 82 + 0]) >> 4;
int y1_ = (_imageCoords[off + 82 + 3] - _imageCoords[off + 82 + 1]) >> 4;
int dx1 = (x1_ - x1) >> 10;
int dy1 = (y1_ - y1) >> 15;
y1 >>= 5;
int dx2 = (_imageCoords[off + 82 + 0] - _imageCoords[off + 0]) >> 4;
int dy2 = (_imageCoords[off + 82 + 1] - _imageCoords[off + 1]) >> 9;
int x2 = (((_imageCoords[off + 0] >> 0) * 2) + dx2) >> 1;
int y2 = (((_imageCoords[off + 1] >> 5) * 2) + dy2) >> 1;
for (uint y = 0; y < 16; y++) {
uint px = (x2 * 2 + x1) * 16;
uint py = (y2 * 2 + y1) / 2;
uint deltaX = x1 * 32;
uint deltaY = y1;
for (uint x = 0; x < 16; x++) {
uint srcOff = (py & 0x1ff800) | (px >> 21);
dst[x] = src[srcOff];
px += deltaX;
py += deltaY;
}
dst += 640;
x1 += dx1;
y1 += dy1;
x2 += dx2;
y2 += dy2;
}
dst -= 16 * 640 - 16;
off += 2;
}
dst += 15 * 640;
off += 2;
}
_dirty = false;
}
return &_surface;
}
void Omni3DManager::clearConstraints() {
_alphaMin = -HUGE_VAL;
_alphaMax = HUGE_VAL;
_betaMin = -HUGE_VAL;
_betaMax = HUGE_VAL;
}
Common::Point Omni3DManager::mapMouseCoords(const Common::Point &mouse) {
Common::Point pt;
if (_dirtyCoords) {
updateImageCoords();
}
int smallX = mouse.x & 0xf, squareX = mouse.x >> 4;
int smallY = mouse.y & 0xf, squareY = mouse.y >> 4;
uint off = 82 * squareY + 2 * squareX;
pt.x = ((_imageCoords[off + 2] +
smallY * ((_imageCoords[off + 84] - _imageCoords[off + 2]) >> 4) +
(smallX * smallY) * ((_imageCoords[off + 86] - _imageCoords[off + 84]) >> 8) +
(smallX * (16 - smallY)) * ((_imageCoords[off + 4] - _imageCoords[off + 2]) >> 8))
& 0x07ff0000) >> 16;
pt.y = (_imageCoords[off + 3] +
smallY * ((_imageCoords[off + 85] - _imageCoords[off + 3]) >> 4) +
(smallX * smallY) * ((_imageCoords[off + 87] - _imageCoords[off + 85]) >> 8) +
(smallX * (16 - smallY)) * ((_imageCoords[off + 5] - _imageCoords[off + 3]) >> 8)) >> 16;
return pt;
}
} // End of namespace CryOmni3D