diff options
Diffstat (limited to 'engines/bladerunner/slice_renderer.cpp')
-rw-r--r-- | engines/bladerunner/slice_renderer.cpp | 649 |
1 files changed, 649 insertions, 0 deletions
diff --git a/engines/bladerunner/slice_renderer.cpp b/engines/bladerunner/slice_renderer.cpp new file mode 100644 index 0000000000..4f2d52c0b8 --- /dev/null +++ b/engines/bladerunner/slice_renderer.cpp @@ -0,0 +1,649 @@ +/* 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 "bladerunner/slice_renderer.h" + +#include "bladerunner/bladerunner.h" +#include "bladerunner/lights.h" +#include "bladerunner/set_effects.h" +#include "bladerunner/slice_animations.h" + +#include "common/memstream.h" +#include "common/rect.h" +#include "common/util.h" + +namespace BladeRunner { + +SliceRenderer::SliceRenderer(BladeRunnerEngine *vm) { + _vm = vm; + _pixelFormat = createRGB555(); + int i; + + for (i = 0; i < 942; i++) { // yes, its going just to 942 and not 997 + _animationsShadowEnabled[i] = true; + } +} + +SliceRenderer::~SliceRenderer() { +} + +void SliceRenderer::setView(const View &view) { + _view = view; +} + +void SliceRenderer::setLights(Lights *lights) { + _lights = lights; +} + +void SliceRenderer::setSetEffects(SetEffects *setEffects) { + _setEffects = setEffects; +} + +void SliceRenderer::setupFrameInWorld(int animationId, int animationFrame, Vector3 position, float facing, float scale) { + _position = position; + _facing = facing; + _scale = scale; + + loadFrame(animationId, animationFrame); + + calculateBoundingRect(); +} + +void SliceRenderer::getScreenRectangle(Common::Rect *screenRectangle, int animationId, int animationFrame, Vector3 position, float facing, float scale) { + assert(screenRectangle); + setupFrameInWorld(animationId, animationFrame, position, facing, scale); + *screenRectangle = _screenRectangle; +} + +Matrix3x2 SliceRenderer::calculateFacingRotationMatrix() { + assert(_sliceFramePtr); + + Matrix4x3 viewMatrix = _view._sliceViewMatrix; + Vector3 viewPos = viewMatrix * _position; + float dir = atan2f(viewPos.x, viewPos.z) + _facing; + float s = sinf(dir); + float c = cosf(dir); + + Matrix3x2 rotation( c, -s, 0.0f, + s, c, 0.0f); + + Matrix3x2 viewRotation(viewMatrix(0,0), viewMatrix(0,1), 0.0f, + viewMatrix(2,0), viewMatrix(2,1), 0.0f); + + return viewRotation * rotation; +} + +void SliceRenderer::calculateBoundingRect() { + assert(_sliceFramePtr); + + _screenRectangle.left = 0; + _screenRectangle.right = 0; + _screenRectangle.top = 0; + _screenRectangle.bottom = 0; + + Matrix4x3 viewMatrix = _view._sliceViewMatrix; + + Vector3 frameBottom = Vector3(0.0f, 0.0f, _frameBottomZ); + Vector3 frameTop = Vector3(0.0f, 0.0f, _frameBottomZ + _frameSliceCount * _frameSliceHeight); + + Vector3 bottom = viewMatrix * (_position + frameBottom); + Vector3 top = viewMatrix * (_position + frameTop); + + top = bottom + _scale * (top - bottom); + + if (bottom.z < 0.0f || top.z < 0.0f) + return; + + Matrix3x2 facingRotation = calculateFacingRotationMatrix(); + + Matrix3x2 m_projection(_view._viewportDistance / bottom.z, 0.0f, 0.0f, + 0.0f, 25.5f, 0.0f); + + Matrix3x2 m_frame(_frameScale.x, 0.0f, _framePos.x, + 0.0f, _frameScale.y, _framePos.y); + + _modelMatrix = m_projection * (facingRotation * m_frame); + + Vector4 startScreenVector( + _view._viewportHalfWidth + top.x / top.z * _view._viewportDistance, + _view._viewportHalfHeight + top.y / top.z * _view._viewportDistance, + 1.0f / top.z, + _frameSliceCount * (1.0f / top.z)); + + Vector4 endScreenVector( + _view._viewportHalfWidth + bottom.x / bottom.z * _view._viewportDistance, + _view._viewportHalfHeight + bottom.y / bottom.z * _view._viewportDistance, + 1.0f / bottom.z, + 0.0f); + + _startScreenVector.x = startScreenVector.x; + _startScreenVector.y = startScreenVector.y; + _startScreenVector.z = startScreenVector.z; + _endScreenVector.x = endScreenVector.x; + _endScreenVector.y = endScreenVector.y; + _endScreenVector.z = endScreenVector.z; + _startSlice = startScreenVector.w; + _endSlice = endScreenVector.w; + + Vector4 delta = endScreenVector - startScreenVector; + + if (delta.y == 0.0f) { + return; + } + + /* + * Calculate min and max Y + */ + + float screenMinY = 0.0f; + float screenMaxY = 479.0f; + + if (startScreenVector.y < screenMinY) { + if (endScreenVector.y < screenMinY) + return; + + float f = (screenMinY - startScreenVector.y) / delta.y; + startScreenVector = startScreenVector + f * delta; + } else if (startScreenVector.y > screenMaxY) { + if (endScreenVector.y >= screenMaxY) + return; + + float f = (screenMaxY - startScreenVector.y) / delta.y; + startScreenVector = startScreenVector + f * delta; + } + + if (endScreenVector.y < screenMinY) { + float f = (screenMinY - endScreenVector.y) / delta.y; + endScreenVector = endScreenVector + f * delta; + } else if (endScreenVector.y > screenMaxY) { + float f = (screenMaxY - endScreenVector.y) / delta.y; + endScreenVector = endScreenVector + f * delta; + } + + int bbox_min_y = (int)MIN(startScreenVector.y, endScreenVector.y); + int bbox_max_y = (int)MAX(startScreenVector.y, endScreenVector.y) + 1; + + /* + * Calculate min and max X + */ + + Matrix3x2 mB6 = _modelMatrix + Vector2(startScreenVector.x, 25.5f / startScreenVector.z); + Matrix3x2 mC2 = _modelMatrix + Vector2(endScreenVector.x, 25.5f / endScreenVector.z); + + float min_x = 640.0f; + float max_x = 0.0f; + + for (float i = 0.0f; i <= 256.0f; i += 255.0f) { + for (float j = 0.0f; j <= 256.0f; j += 255.0f) { + Vector2 v1 = mB6 * Vector2(i, j); + + min_x = MIN(min_x, v1.x); + max_x = MAX(max_x, v1.x); + + Vector2 v2 = mC2 * Vector2(i, j); + + min_x = MIN(min_x, v2.x); + max_x = MAX(max_x, v2.x); + } + } + + int bbox_min_x = CLIP((int)min_x, 0, 640); + int bbox_max_x = CLIP((int)max_x + 1, 0, 640); + + _startScreenVector.x = startScreenVector.x; + _startScreenVector.y = startScreenVector.y; + _startScreenVector.z = startScreenVector.z; + _endScreenVector.x = endScreenVector.x; + _endScreenVector.y = endScreenVector.y; + _endScreenVector.z = endScreenVector.z; + _startSlice = startScreenVector.w; + _endSlice = endScreenVector.w; + + _screenRectangle.left = bbox_min_x; + _screenRectangle.right = bbox_max_x; + _screenRectangle.top = bbox_min_y; + _screenRectangle.bottom = bbox_max_y; +} + +void SliceRenderer::loadFrame(int animation, int frame) { + _animation = animation; + _frame = frame; + _sliceFramePtr = _vm->_sliceAnimations->getFramePtr(_animation, _frame); + + Common::MemoryReadStream stream((byte*)_sliceFramePtr, _vm->_sliceAnimations->_animations[_animation].frameSize); + + _frameScale.x = stream.readFloatLE(); + _frameScale.y = stream.readFloatLE(); + _frameSliceHeight = stream.readFloatLE(); + _framePos.x = stream.readFloatLE(); + _framePos.y = stream.readFloatLE(); + _frameBottomZ = stream.readFloatLE(); + _framePaletteIndex = stream.readUint32LE(); + _frameSliceCount = stream.readUint32LE(); +} + +struct SliceLineIterator { + int _sliceMatrix[2][3]; + int _startY; + int _endY; + + float _currentZ; + float _stepZ; + float _currentSlice; + float _stepSlice; + float _currentX; + float _stepX; + int _field_38; + int _currentY; + + void setup(float endScreenX, float endScreenY, float endScreenZ, + float startScreenX, float startScreenY, float startScreenZ, + float endSlice, float startSlice, + Matrix3x2 m); + float line(); + void advance(); +}; + +void SliceLineIterator::setup( + float endScreenX, float endScreenY, float endScreenZ, + float startScreenX, float startScreenY, float startScreenZ, + float endSlice, float startSlice, + Matrix3x2 m) { + _startY = (int)startScreenY; + _endY = (int)endScreenY; + + float size = endScreenY - startScreenY; + + if (size <= 0.0f || startScreenZ <= 0.0f) + _currentY = _endY + 1; + + _currentZ = startScreenZ; + _stepZ = (endScreenZ - startScreenZ) / size; + + _stepSlice = (endSlice - startSlice) / size; + _currentSlice = startSlice - (startScreenY - floor(startScreenY) - 1.0f) * _stepSlice; + + _currentX = startScreenX; + _stepX = (endScreenX - startScreenX) / size; + + _field_38 = (int)((25.5f / size) * (1.0f / endScreenZ - 1.0f / startScreenZ) * 64.0); + _currentY = _startY; + + float offsetX = _currentX; + float offsetZ = 25.5f / _currentZ; + + Matrix3x2 translate_matrix = Matrix3x2(1.0f, 0.0f, offsetX, + 0.0f, 1.0f, offsetZ); + + Matrix3x2 scale_matrix = Matrix3x2(65536.0f, 0.0f, 0.0f, + 0.0f, 64.0f, 0.0f); + + m = scale_matrix * (translate_matrix * m); + + for (int r = 0; r != 2; ++r) + for (int c = 0; c != 3; ++c) + _sliceMatrix[r][c] = m(r, c); +} + +float SliceLineIterator::line() { + float var_0 = 0.0f; + + if (_currentZ != 0.0f) + var_0 = _currentSlice / _currentZ; + + if (var_0 < 0.0) + var_0 = 0.0f; + + return var_0; +} + +void SliceLineIterator::advance() { + _currentZ += _stepZ; + _currentSlice += _stepSlice; + _currentX += _stepX; + _currentY += 1; + _sliceMatrix[0][2] += (int)(65536.0f * _stepX); + _sliceMatrix[1][2] += _field_38; +} + +static void setupLookupTable(int t[256], int inc) { + int v = 0; + for (int i = 0; i != 256; ++i) { + t[i] = v; + v += inc; + } +} + +void SliceRenderer::drawInWorld(int animationId, int animationFrame, Vector3 position, float facing, float scale, Graphics::Surface &surface, uint16 *zbuffer) { + assert(_lights); + assert(_setEffects); + //assert(_view); + + _vm->_sliceRenderer->setupFrameInWorld(animationId, animationFrame, position, facing); + assert(_sliceFramePtr); + + SliceLineIterator sliceLineIterator; + sliceLineIterator.setup( + _endScreenVector.x, _endScreenVector.y, _endScreenVector.z, + _startScreenVector.x, _startScreenVector.y, _startScreenVector.z, + _endSlice, _startSlice, + _modelMatrix + ); + + SliceRendererLights sliceRendererLights = SliceRendererLights(_lights); + + _lights->setupFrame(_view._frame); + _setEffects->setupFrame(_view._frame); + + float sliceLine = sliceLineIterator.line(); + + sliceRendererLights.calculateColorBase( + Vector3(_position.x, _position.y, _position.z + _frameBottomZ + sliceLine * _frameSliceHeight), + Vector3(_position.x, _position.y, _position.z + _frameBottomZ), + sliceLineIterator._endY - sliceLineIterator._startY); + + float setEffectsColorCoeficient; + Color setEffectColor; + _setEffects->calculateColor( + _view._cameraPosition, + Vector3(_position.x, _position.y, _position.z + _frameBottomZ + sliceLine * _frameSliceHeight), + &setEffectsColorCoeficient, + &setEffectColor); + + _lightsColor.r = setEffectsColorCoeficient * sliceRendererLights._finalColor.r * 65536.0f; + _lightsColor.g = setEffectsColorCoeficient * sliceRendererLights._finalColor.g * 65536.0f; + _lightsColor.b = setEffectsColorCoeficient * sliceRendererLights._finalColor.b * 65536.0f; + + _setEffectColor.r = setEffectColor.r * 31.0f * 65536.0f; + _setEffectColor.g = setEffectColor.g * 31.0f * 65536.0f; + _setEffectColor.b = setEffectColor.b * 31.0f * 65536.0f; + + setupLookupTable(_m11lookup, sliceLineIterator._sliceMatrix[0][0]); + setupLookupTable(_m12lookup, sliceLineIterator._sliceMatrix[0][1]); + _m13 = sliceLineIterator._sliceMatrix[0][2]; + setupLookupTable(_m21lookup, sliceLineIterator._sliceMatrix[1][0]); + setupLookupTable(_m22lookup, sliceLineIterator._sliceMatrix[1][1]); + _m23 = sliceLineIterator._sliceMatrix[1][2]; + + + if(_animationsShadowEnabled[_animation]) { + //TODO: draw shadows + } + + int frameY = sliceLineIterator._startY; + + uint16 *frameLinePtr = (uint16*)surface.getPixels() + 640 * frameY; + uint16 *zBufferLinePtr = zbuffer + 640 * frameY; + + while (sliceLineIterator._currentY <= sliceLineIterator._endY) { + sliceLine = sliceLineIterator.line(); + + sliceRendererLights.calculateColorSlice(Vector3(_position.x, _position.y, _position.z + _frameBottomZ + sliceLine * _frameSliceHeight)); + + if (sliceLineIterator._currentY & 1) { + _setEffects->calculateColor( + _view._cameraPosition, + Vector3(_position.x, _position.y, _position.z + _frameBottomZ + sliceLine * _frameSliceHeight), + &setEffectsColorCoeficient, + &setEffectColor); + } + + _lightsColor.r = setEffectsColorCoeficient * sliceRendererLights._finalColor.r * 65536.0f; + _lightsColor.g = setEffectsColorCoeficient * sliceRendererLights._finalColor.g * 65536.0f; + _lightsColor.b = setEffectsColorCoeficient * sliceRendererLights._finalColor.b * 65536.0f; + + _setEffectColor.r = setEffectColor.r * 31.0f * 65536.0f; + _setEffectColor.g = setEffectColor.g * 31.0f * 65536.0f; + _setEffectColor.b = setEffectColor.b * 31.0f * 65536.0f; + + if (frameY >= 0 && frameY < 480) { + drawSlice((int)sliceLine, true, frameLinePtr, zBufferLinePtr); + } + + sliceLineIterator.advance(); + frameY += 1; + frameLinePtr += 640; + zBufferLinePtr += 640; + } +} + +void SliceRenderer::drawOnScreen(int animationId, int animationFrame, int screenX, int screenY, float facing, float scale, Graphics::Surface &surface, uint16 *zbuffer) { + if (scale == 0.0f) { + return; + } + _position.x = 0; + _position.y = 0; + _position.z = 0; + _facing = facing; + + loadFrame(animationId, animationFrame); + + float frameHeight = _frameSliceHeight * _frameSliceCount; + float frameSize = sqrtf(_frameScale.x * 255.0f * _frameScale.x * 255.0f + _frameScale.y * 255.0f * _frameScale.y * 255.0f); + float size = scale / MAX(frameSize, frameHeight); + + float s = sinf(_facing); + float c = cosf(_facing); + + Matrix3x2 m_rotation(c, -s, 0.0f, + s, c, 0.0f); + + Matrix3x2 m_frame(_frameScale.x, 0.0f, _framePos.x, + 0.0f, _frameScale.y, _framePos.y); + + Matrix3x2 m_scale_size_25_5(size, 0.0f, 0.0f, + 0.0f, 25.5f, 0.0f); + + Matrix3x2 m_translate_x_32k(1.0f, 0.0f, screenX, + 0.0f, 1.0f, 32768.0f); + + Matrix3x2 m_scale_64k_64(65536.0f, 0.0f, 0.0f, + 0.0f, 64.0f, 0.0f); + + Matrix3x2 m = m_scale_64k_64 * (m_translate_x_32k * (m_scale_size_25_5 * (m_rotation * m_frame))); + + setupLookupTable(_m11lookup, m(0, 0)); + setupLookupTable(_m12lookup, m(0, 1)); + _m13 = m(0, 2); + setupLookupTable(_m21lookup, m(1, 0)); + setupLookupTable(_m22lookup, m(1, 1)); + _m23 = m(1, 2); + + int frameY = screenY + (size / 2.0f * frameHeight); + int currentY = frameY; + + float currentSlice = 0; + float sliceStep = 1.0f / size / _frameSliceHeight; + + uint16 *frameLinePtr = (uint16*)surface.getPixels() + 640 * frameY; + uint16 lineZbuffer[640]; + + while (currentSlice < _frameSliceCount) { + if (currentY >= 0 && currentY < 480) { + memset(lineZbuffer, 0xFF, 640 * 2); + drawSlice(currentSlice, false, frameLinePtr, lineZbuffer); + currentSlice += sliceStep; + currentY--; + frameLinePtr -= 640; + } + } +} + +void SliceRenderer::drawSlice(int slice, bool advanced, uint16 *frameLinePtr, uint16 *zbufLinePtr) { + if (slice < 0 || (uint32)slice >= _frameSliceCount) + return; + + SlicePalette &palette = _vm->_sliceAnimations->getPalette(_framePaletteIndex); + + byte *p = (byte*)_sliceFramePtr + 0x20 + 4 * slice; + + uint32 polyOffset = READ_LE_UINT32(p); + + p = (byte*)_sliceFramePtr + polyOffset; + + uint32 polyCount = READ_LE_UINT32(p); + p += 4; + while (polyCount--) { + uint32 vertexCount = READ_LE_UINT32(p); + p += 4; + + if (vertexCount == 0) + continue; + + uint32 lastVertex = vertexCount - 1; + int lastVertexX = MAX((_m11lookup[p[3 * lastVertex]] + _m12lookup[p[3 * lastVertex + 1]] + _m13) >> 16, 0); + + int previousVertexX = lastVertexX; + + while (vertexCount--) { + int vertexX = CLIP((_m11lookup[p[0]] + _m12lookup[p[1]] + _m13) >> 16, 0, 640); + + if (vertexX > previousVertexX) { + int vertexZ = (_m21lookup[p[0]] + _m22lookup[p[1]] + _m23) >> 6; + + if (vertexZ >= 0 && vertexZ < 65536) { + int color555 = palette.color555[p[2]]; + if (advanced) { + Color256 color = palette.color[p[2]]; + + color.r = (int)(_setEffectColor.r + _lightsColor.r * color.r) >> 16; + color.g = (int)(_setEffectColor.g + _lightsColor.g * color.g) >> 16; + color.b = (int)(_setEffectColor.b + _lightsColor.b * color.b) >> 16; + + int bladeToScummVmConstant = 256 / 32; + + color555 = _pixelFormat.RGBToColor(CLIP(color.r * bladeToScummVmConstant, 0, 255), CLIP(color.g * bladeToScummVmConstant, 0, 255), CLIP(color.b * bladeToScummVmConstant, 0, 255)); + } + for (int x = previousVertexX; x != vertexX; ++x) { + if (vertexZ < zbufLinePtr[x]) { + frameLinePtr[x] = color555; + zbufLinePtr[x] = (uint16)vertexZ; + } + } + } + } + p += 3; + previousVertexX = vertexX; + } + } +} + +void SliceRenderer::preload(int animationId) { + int i; + int frameCount = _vm->_sliceAnimations->getFrameCount(animationId); + for (i = 0; i < frameCount; i++) + _vm->_sliceAnimations->getFramePtr(animationId, i); +} + +void SliceRenderer::disableShadows(int animationsIdsList[], int listSize) { + int i; + for (i = 0; i < listSize; i++) { + _animationsShadowEnabled[animationsIdsList[i]] = false; + } +} + +SliceRendererLights::SliceRendererLights(Lights *lights) { + _finalColor.r = 0.0f; + _finalColor.g = 0.0f; + _finalColor.b = 0.0f; + + _lights = lights; + + for (int i = 0; i < 20; i++) { + _colors[i].r = 0.0f; + _colors[i].g = 0.0f; + _colors[i].b = 0.0f; + } +} + +void SliceRendererLights::calculateColorBase(Vector3 position1, Vector3 position2, float height) { + _finalColor.r = 0.0f; + _finalColor.g = 0.0f; + _finalColor.b = 0.0f; + _hmm3 = 0; + if (_lights) { + for (uint i = 0; i < _lights->_lights.size(); i++) { + Light *light = _lights->_lights[i]; + if (i < 20) { + float v8 = light->calculate(position1, position2/*, height*/); + + this->_hmm2[i] = v8; + this->_hmm[i] = v8; + + Color v22; + light->calculateColor(&v22, position1); + _colors[i] = v22; + _finalColor.r += v22.r; + _finalColor.g += v22.g; + _finalColor.b += v22.b; + } else { + Color v23; + light->calculateColor(&v23, position1); + _finalColor.r += v23.r; + _finalColor.g += v23.g; + _finalColor.b += v23.b; + } + } + + _finalColor.r += _lights->_ambientLightColor.r; + _finalColor.g += _lights->_ambientLightColor.g; + _finalColor.b += _lights->_ambientLightColor.b; + } +} + +void SliceRendererLights::calculateColorSlice(Vector3 position) { + _finalColor.r = 0.0f; + _finalColor.g = 0.0f; + _finalColor.b = 0.0f; + + if (_lights) { + for (uint i = 0; i < _lights->_lights.size(); i++) { + Light *light = _lights->_lights[i]; + if (i < 20) { + _hmm[i] = _hmm[i] - 1.0f; + + if (_hmm[i] <= 0.0f) { + do { + _hmm[i] = _hmm[i] + _hmm2[i]; + } while (_hmm[i] <= 0.0f); + light->calculateColor(&_colors[i], position); + _hmm3++; + } + _finalColor.r += _colors[i].r; + _finalColor.g += _colors[i].g; + _finalColor.b += _colors[i].b; + } else { + Color color; + light->calculateColor(&color, position); + _hmm3++; + _finalColor.r += color.r; + _finalColor.g += color.g; + _finalColor.b += color.b; + } + } + _finalColor.r += _lights->_ambientLightColor.r; + _finalColor.g += _lights->_ambientLightColor.g; + _finalColor.b += _lights->_ambientLightColor.b; + } +} + +} // End of namespace BladeRunner |