scummvm/engines/titanic/star_control/base_stars.cpp
2018-05-24 15:30:55 +02:00

569 lines
16 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 "titanic/star_control/base_stars.h"
#include "titanic/star_control/star_camera.h"
#include "titanic/star_control/star_closeup.h"
#include "titanic/star_control/star_ref.h"
#include "titanic/support/files_manager.h"
#include "titanic/support/simple_file.h"
#include "titanic/titanic.h"
namespace Titanic {
CBaseStarEntry::CBaseStarEntry() : _red(0), _value(0.0) {
Common::fill(&_data[0], &_data[5], 0);
}
void CBaseStarEntry::load(Common::SeekableReadStream &s) {
_red = s.readByte();
_green = s.readByte();
_blue = s.readByte();
_thickness = s.readByte();
_value = s.readFloatLE();
_position._x = s.readFloatLE();
_position._y = s.readFloatLE();
_position._z = s.readFloatLE();
for (int idx = 0; idx < 5; ++idx)
_data[idx] = s.readUint32LE();
}
bool CBaseStarEntry::operator==(const CBaseStarEntry &s) const {
return _red == s._red && _green == s._green
&& _blue == s._blue && _thickness == s._thickness
&& _value == s._value && _position == s._position
&& _data[0] == s._data[0] && _data[1] == s._data[1]
&& _data[2] == s._data[2] && _data[3] == s._data[3]
&& _data[4] == s._data[4];
}
/*------------------------------------------------------------------------*/
CBaseStars::CBaseStars() : _minVal(0.0), _maxVal(1.0), _range(0.0),
_value1(0.0), _value2(0.0), _value3(0.0), _value4(0.0) {
}
void CBaseStars::clear() {
_data.clear();
}
void CBaseStars::initialize() {
_minVal = 9.9999998e10;
_maxVal = -9.9999998e10;
_minMax.reset();
for (uint idx = 0; idx < _data.size(); ++idx) {
const CBaseStarEntry *entry = getDataPtr(idx);
_minMax.expand(entry->_position);
if (entry->_value < _minVal)
_minVal = entry->_value;
if (entry->_value > _maxVal)
_maxVal = entry->_value;
}
_range = (_maxVal - _minVal) / 1.0;
}
const CBaseStarEntry *CBaseStars::getDataPtr(int index) const {
return (index >= 0 && index < (int)_data.size()) ? &_data[index] : nullptr;
}
void CBaseStars::loadData(Common::SeekableReadStream &s) {
uint headerId = s.readUint32LE();
uint count = s.readUint32LE();
if (headerId != 100 || count == 0)
error("Invalid star data");
// Initialize the data array
clear();
_data.resize(count);
// Iterate through reading the data for each entry
for (uint idx = 0; idx < count; ++idx)
_data[idx].load(s);
}
void CBaseStars::loadData(const CString &resName) {
// Get a stream to read the data from the DAT file
Common::SeekableReadStream *stream = g_vm->_filesManager->getResource(resName);
assert(stream);
// Load the stream
loadData(*stream);
delete stream;
}
void CBaseStars::resetEntry(CBaseStarEntry &entry) {
entry._red = 0xFF;
entry._green = 0xFF;
entry._blue = 0xFF;
entry._thickness = 0;
entry._position._x = 0;
entry._position._y = 0;
entry._position._z = 0;
for (int idx = 0; idx < 5; ++idx)
entry._data[idx] = 0;
}
void CBaseStars::draw(CSurfaceArea *surfaceArea, CStarCamera *camera, CStarCloseup *closeup) {
if (!_data.empty()) {
switch (camera->getStarColor()) {
case WHITE: // draw white, green, and red stars (mostly white)
switch (surfaceArea->_bpp) {
case 1:
draw1(surfaceArea, camera, closeup);
break;
case 2:
draw2(surfaceArea, camera, closeup);
break;
default:
break;
}
break;
case PINK: // draw pink stars
switch (surfaceArea->_bpp) {
case 1:
draw3(surfaceArea, camera, closeup);
break;
case 2:
draw4(surfaceArea, camera, closeup);
break;
default:
break;
}
break;
default:
break;
}
}
}
void CBaseStars::draw1(CSurfaceArea *surfaceArea, CStarCamera *camera, CStarCloseup *closeup) {
FPose pose = camera->getPose();
camera->getRelativeXCenterPixels(&_value1, &_value2, &_value3, &_value4);
const double MAX_VAL = 1.0e9 * 1.0e9;
FPoint centroid = surfaceArea->_centroid + FPoint(0.5, 0.5);
double threshold = camera->getThreshold();
double minVal = threshold - 9216.0;
int width1 = surfaceArea->_width - 1;
int height1 = surfaceArea->_height - 1;
double *v1Ptr = &_value1, *v2Ptr = &_value2;
double tempX, tempY, tempZ, total2;
for (uint idx = 0; idx < _data.size(); ++idx) {
CBaseStarEntry &entry = _data[idx];
const FVector &vector = entry._position;
tempZ = vector._x * pose._row1._z + vector._y * pose._row2._z
+ vector._z * pose._row3._z + pose._vector._z;
if (tempZ <= minVal)
continue;
tempY = vector._x * pose._row1._y + vector._y * pose._row2._y + vector._z * pose._row3._y + pose._vector._y;
tempX = vector._x * pose._row1._x + vector._y * pose._row2._x + vector._z * pose._row3._x + pose._vector._x;
total2 = tempY * tempY + tempX * tempX + tempZ * tempZ;
if (total2 < 1.0e12) {
closeup->draw(pose, vector, FVector(centroid._x, centroid._y, total2),
surfaceArea, camera);
continue;
}
if (tempZ <= threshold || total2 >= MAX_VAL)
continue;
int xStart = (int)(*v1Ptr * tempX / tempZ + centroid._x);
int yStart = (int)(*v2Ptr * tempY / tempZ + centroid._y);
if (xStart < 0 || xStart >= width1 || yStart < 0 || yStart >= height1)
continue;
double sVal = sqrt(total2);
sVal = (sVal < 100000.0) ? 1.0 : 1.0 - ((sVal - 100000.0) / 1.0e9);
double red = MIN((double)entry._red * sVal, (double)255.0);
double green = MIN((double)entry._green * sVal, (double)255.0);
double blue = MIN((double)entry._green * sVal, (double)255.0);
int skipCtr = 0;
if (red < 0.0) {
red = 0.0;
++skipCtr;
}
if (green < 0.0) {
green = 0.0;
++skipCtr;
}
if (blue < 0.0) {
blue = 0.0;
++skipCtr;
}
if (skipCtr == 3)
continue;
int r = (int)(red - 0.5) & 0xfff8;
int g = (int)(green - 0.5) & 0xfff8;
int b = (int)(blue - 0.5) & 0xfff8;
int rgb = ((g | (r << 5)) << 2) | ((b >> 3) & 0xfff8);
uint16 *pixelP = (uint16 *)(surfaceArea->_pixelsPtr + surfaceArea->_pitch * yStart + xStart * 2);
switch (entry._thickness) {
case 0:
*pixelP = rgb;
break;
case 1:
*pixelP = rgb;
*(pixelP + 1) = rgb;
*(pixelP + surfaceArea->_pitch / 2) = rgb;
*(pixelP + surfaceArea->_pitch / 2 + 1) = rgb;
break;
default:
break;
}
}
}
void CBaseStars::draw2(CSurfaceArea *surfaceArea, CStarCamera *camera, CStarCloseup *closeup) {
FPose pose = camera->getPose();
camera->getRelativeXCenterPixels(&_value1, &_value2, &_value3, &_value4);
const double MAX_VAL = 1.0e9 * 1.0e9;
FPoint centroid = surfaceArea->_centroid + FPoint(0.5, 0.5);
double threshold = camera->getThreshold();
double minVal = threshold - 9216.0;
int width1 = surfaceArea->_width - 1;
int height1 = surfaceArea->_height - 1;
double *v1Ptr = &_value1, *v2Ptr = &_value2;
double tempX, tempY, tempZ, total2;
for (uint idx = 0; idx < _data.size(); ++idx) {
CBaseStarEntry &entry = _data[idx];
const FVector &vector = entry._position;
tempZ = vector._x * pose._row1._z + vector._y * pose._row2._z
+ vector._z * pose._row3._z + pose._vector._z;
if (tempZ <= minVal)
continue;
tempY = vector._x * pose._row1._y + vector._y * pose._row2._y + vector._z * pose._row3._y + pose._vector._y;
tempX = vector._x * pose._row1._x + vector._y * pose._row2._x + vector._z * pose._row3._x + pose._vector._x;
total2 = tempY * tempY + tempX * tempX + tempZ * tempZ;
if (total2 < 1.0e12) {
closeup->draw(pose, vector, FVector(centroid._x, centroid._y, total2),
surfaceArea, camera);
continue;
}
if (tempZ <= threshold || total2 >= MAX_VAL)
continue;
int xStart = (int)(*v1Ptr * tempX / tempZ + centroid._x);
int yStart = (int)(*v2Ptr * tempY / tempZ + centroid._y);
if (xStart < 0 || xStart >= width1 || yStart < 0 || yStart >= height1)
continue;
double sVal = sqrt(total2);
sVal = (sVal < 100000.0) ? 1.0 : 1.0 - ((sVal - 100000.0) / 1.0e9);
double red = MIN((double)entry._red * sVal, (double)255.0);
double green = MIN((double)entry._green * sVal, (double)255.0);
double blue = MIN((double)entry._green * sVal, (double)255.0);
int skipCtr = 0;
if (red < 0.0) {
red = 0.0;
++skipCtr;
}
if (green < 0.0) {
green = 0.0;
++skipCtr;
}
if (blue < 0.0) {
blue = 0.0;
++skipCtr;
}
if (skipCtr == 3)
continue;
int r = (int)(red - 0.5) & 0xf8;
int g = (int)(green - 0.5) & 0xfc;
int b = (int)(blue - 0.5) & 0xfff8;
int rgb = ((g | (r << 5)) << 3) | (b >> 3);
uint16 *pixelP = (uint16 *)(surfaceArea->_pixelsPtr + surfaceArea->_pitch * yStart + xStart * 2);
switch (entry._thickness) {
case 0:
*pixelP = rgb;
break;
case 1:
*pixelP = rgb;
*(pixelP + 1) = rgb;
*(pixelP + surfaceArea->_pitch / 2) = rgb;
*(pixelP + surfaceArea->_pitch / 2 + 1) = rgb;
break;
default:
break;
}
}
}
void CBaseStars::draw3(CSurfaceArea *surfaceArea, CStarCamera *camera, CStarCloseup *closeup) {
FPose pose = camera->getPose();
camera->getRelativeXCenterPixels(&_value1, &_value2, &_value3, &_value4);
const double MAX_VAL = 1.0e9 * 1.0e9;
FPoint centroid = surfaceArea->_centroid + FPoint(0.5, 0.5);
double threshold = camera->getThreshold();
double minVal = threshold - 9216.0;
int width1 = surfaceArea->_width - 1;
int height1 = surfaceArea->_height - 1;
double *v1Ptr = &_value1, *v2Ptr = &_value2;
double *v3Ptr = &_value3, *v4Ptr = &_value4;
double tempX, tempY, tempZ, total2, sVal;
int xStart, yStart, rgb;
uint16 *pixelP;
for (uint idx = 0; idx < _data.size(); ++idx) {
CBaseStarEntry &entry = _data[idx];
const FVector &vector = entry._position;
tempZ = vector._x * pose._row1._z + vector._y * pose._row2._z
+ vector._z * pose._row3._z + pose._vector._z;
if (tempZ <= minVal)
continue;
tempY = vector._x * pose._row1._y + vector._y * pose._row2._y + vector._z * pose._row3._y + pose._vector._y;
tempX = vector._x * pose._row1._x + vector._y * pose._row2._x + vector._z * pose._row3._x + pose._vector._x;
total2 = tempY * tempY + tempX * tempX + tempZ * tempZ;
if (total2 < 1.0e12) {
closeup->draw(pose, vector, FVector(centroid._x, centroid._y, total2),
surfaceArea, camera);
continue;
}
if (tempZ <= threshold || total2 >= MAX_VAL)
continue;
// First pixel
xStart = (int)((tempX + *v3Ptr) * *v1Ptr / tempZ + centroid._x);
yStart = (int)(tempY * *v2Ptr / tempZ + centroid._y);
if (xStart < 0 || xStart >= width1 || yStart < 0 || yStart >= height1)
continue;
sVal = sqrt(total2);
sVal = (sVal < 100000.0) ? 1.0 : 1.0 - ((sVal - 100000.0) / 1.0e9);
sVal *= 255.0;
if (sVal > 255.0)
sVal = 255.0;
if (sVal > 2.0) {
pixelP = (uint16 *)(surfaceArea->_pixelsPtr + surfaceArea->_pitch * yStart + xStart * 2);
rgb = ((int)(sVal - 0.5) & 0xf8) << 7;
switch (entry._thickness) {
case 0:
*pixelP = rgb;
break;
case 1:
*pixelP = rgb;
*(pixelP + 1) = rgb;
*(pixelP + surfaceArea->_pitch / 2) = rgb;
*(pixelP + surfaceArea->_pitch / 2 + 1) = rgb;
break;
default:
break;
}
}
// Second pixel
xStart = (int)((tempX + *v4Ptr) * *v1Ptr / tempZ + centroid._x);
yStart = (int)(tempY * *v2Ptr / tempZ + centroid._y);
if (xStart < 0 || xStart >= width1 || yStart < 0 || yStart >= height1)
continue;
sVal = sqrt(total2);
sVal = (sVal < 100000.0) ? 1.0 : 1.0 - ((sVal - 100000.0) / 1.0e9);
sVal *= 255.0;
if (sVal > 255.0)
sVal = 255.0;
if (sVal > 2.0) {
pixelP = (uint16 *)(surfaceArea->_pixelsPtr + surfaceArea->_pitch * yStart + xStart * 2);
rgb = ((int)(sVal - 0.5) & 0xf8) << 7;
switch (entry._thickness) {
case 0:
*pixelP |= rgb;
break;
case 1:
*pixelP |= rgb;
*(pixelP + 1) |= rgb;
*(pixelP + surfaceArea->_pitch / 2) |= rgb;
*(pixelP + surfaceArea->_pitch / 2 + 1) |= rgb;
break;
default:
break;
}
}
}
}
void CBaseStars::draw4(CSurfaceArea *surfaceArea, CStarCamera *camera, CStarCloseup *closeup) {
FPose pose = camera->getPose();
camera->getRelativeXCenterPixels(&_value1, &_value2, &_value3, &_value4);
const double MAX_VAL = 1.0e9 * 1.0e9;
FPoint centroid = surfaceArea->_centroid + FPoint(0.5, 0.5);
double threshold = camera->getThreshold();
double minVal = threshold - 9216.0;
int width1 = surfaceArea->_width - 1;
int height1 = surfaceArea->_height - 1;
double *v1Ptr = &_value1, *v2Ptr = &_value2, *v3Ptr = &_value3, *v4Ptr = &_value4;
double tempX, tempY, tempZ, total2, sVal;
int xStart, yStart, rgb;
uint16 *pixelP;
for (uint idx = 0; idx < _data.size(); ++idx) {
const CBaseStarEntry &entry = _data[idx];
const FVector &vector = entry._position;
tempZ = vector._x * pose._row1._z + vector._y * pose._row2._z
+ vector._z * pose._row3._z + pose._vector._z;
if (tempZ <= minVal)
continue;
tempY = vector._x * pose._row1._y + vector._y * pose._row2._y + vector._z * pose._row3._y + pose._vector._y;
tempX = vector._x * pose._row1._x + vector._y * pose._row2._x + vector._z * pose._row3._x + pose._vector._x;
total2 = tempY * tempY + tempX * tempX + tempZ * tempZ;
if (total2 < 1.0e12) {
// We're in close proximity to the given star, so draw a closeup of it
closeup->draw(pose, vector, FVector(centroid._x, centroid._y, total2),
surfaceArea, camera);
continue;
}
if (tempZ <= threshold || total2 >= MAX_VAL)
continue;
// First pixel
xStart = (int)((tempX + *v3Ptr) * *v1Ptr / tempZ + centroid._x);
yStart = (int)(tempY * *v2Ptr / tempZ + centroid._y);
if (xStart < 0 || xStart >= width1 || yStart < 0 || yStart >= height1)
continue;
sVal = sqrt(total2);
sVal = (sVal < 100000.0) ? 1.0 : 1.0 - ((sVal - 100000.0) / 1.0e9);
sVal *= 255.0;
if (sVal > 255.0)
sVal = 255.0;
if (sVal > 2.0) {
pixelP = (uint16 *)(surfaceArea->_pixelsPtr + surfaceArea->_pitch * yStart + xStart * 2);
rgb = ((int)(sVal - 0.5) & 0xf8) << 8;
switch (entry._thickness) {
case 0:
*pixelP = rgb;
break;
case 1:
*pixelP = rgb;
*(pixelP + 1) = rgb;
*(pixelP + surfaceArea->_pitch / 2) = rgb;
*(pixelP + surfaceArea->_pitch / 2 + 1) = rgb;
break;
default:
break;
}
}
// Second pixel
xStart = (int)((tempX + *v4Ptr) * *v1Ptr / tempZ + centroid._x);
yStart = (int)((tempY * *v2Ptr) / tempZ + centroid._y);
if (xStart < 0 || xStart >= width1 || yStart < 0 || yStart >= height1)
continue;
sVal = sqrt(total2);
sVal = (sVal < 100000.0) ? 1.0 : 1.0 - ((sVal - 100000.0) / 1.0e9);
sVal *= 255.0;
if (sVal > 255.0)
sVal = 255.0;
if (sVal > 2.0) {
pixelP = (uint16 *)(surfaceArea->_pixelsPtr + surfaceArea->_pitch * yStart + xStart * 2);
rgb = ((int)(sVal - 0.5) >> 3) & 0xff;
switch (entry._thickness) {
case 0:
*pixelP |= rgb;
break;
case 1:
*pixelP |= rgb;
*(pixelP + 1) |= rgb;
*(pixelP + surfaceArea->_pitch / 2) |= rgb;
*(pixelP + surfaceArea->_pitch / 2 + 1) |= rgb;
break;
default:
break;
}
}
}
}
int CBaseStars::findStar(CSurfaceArea *surfaceArea, CStarCamera *camera,
const Common::Point &pt) {
CStarRef1 ref(this, pt);
ref.process(surfaceArea, camera);
return ref._index;
}
int CBaseStars::baseFn2(CSurfaceArea *surfaceArea, CStarCamera *camera) {
CStarRef3 ref(this);
ref.process(surfaceArea, camera);
return ref._index;
}
/*------------------------------------------------------------------------*/
void CStarVector::apply() {
_owner->addLockedStar(_vector);
}
} // End of namespace Titanic