From 4b181e4bed3af7b7e32a8d4c9209d81ece366554 Mon Sep 17 00:00:00 2001 From: Thomas Fach-Pedersen Date: Thu, 5 Jun 2014 23:49:57 +0200 Subject: BLADERUNNER: First pass at slice animation renderer Z-buffers are not being read from the VQA background yet, so z-buffer handling is faked for now. --- engines/bladerunner/slice_renderer.cpp | 412 +++++++++++++++++++++++++++++++++ 1 file changed, 412 insertions(+) create mode 100644 engines/bladerunner/slice_renderer.cpp (limited to 'engines/bladerunner/slice_renderer.cpp') diff --git a/engines/bladerunner/slice_renderer.cpp b/engines/bladerunner/slice_renderer.cpp new file mode 100644 index 0000000000..e8e3ecf894 --- /dev/null +++ b/engines/bladerunner/slice_renderer.cpp @@ -0,0 +1,412 @@ +/* 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/slice_animations.h" + +#include "common/debug.h" +#include "common/memstream.h" +#include "common/rect.h" +#include "common/util.h" + +namespace BladeRunner { + +#if 0 +void dump(const char *str, Vector3 v) { + debug("%s %g %g %g", str, v.x, v.y, v.z); +} + +void dump(const char *str, Matrix3x2 m) { + debug("%s", str); + debug("%11.6g %11.6g %11.6g", m(0, 0), m(0, 1), m(0, 2)); + debug("%11.6g %11.6g %11.6g", m(1, 0), m(1, 1), m(1, 2)); +} + +void dump(const char *str, Matrix4x3 m) { + debug("%s", str); + debug("%11.6g %11.6g %11.6g %11.6g", m(0, 0), m(0, 1), m(0, 2), m(0, 3)); + debug("%11.6g %11.6g %11.6g %11.6g", m(1, 0), m(1, 1), m(1, 2), m(1, 3)); + debug("%11.6g %11.6g %11.6g %11.6g", m(2, 0), m(2, 1), m(2, 2), m(2, 3)); +} +#endif + +SliceRenderer::~SliceRenderer() { +} + +void SliceRenderer::setView(const View &view) { + _view = view; +} + +void SliceRenderer::setupFrame(int animation, int frame, Vector3 position, float facing, float scale) { + _animation = animation; + _frame = frame; + _position = position; + _facing = facing; + _scale = scale; + + _sliceFramePtr = _vm->_sliceAnimations->getFramePtr(_animation, _frame); + + Common::MemoryReadStream stream((byte*)_sliceFramePtr, _vm->_sliceAnimations->_animations[_animation].frameSize); + + _frameFront.x = stream.readFloatLE(); + _frameFront.y = stream.readFloatLE(); + _frameSliceHeight = stream.readFloatLE(); + _framePos.x = stream.readFloatLE(); + _framePos.y = stream.readFloatLE(); + _frameBottomZ = stream.readFloatLE(); + _framePaletteIndex = stream.readUint32LE(); + _frameSliceCount = stream.readUint32LE(); + + calculateBoundingRect(); +} + +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); + + _minX = 0.0f; + _maxX = 0.0f; + _minY = 0.0f; + _maxY = 0.0f; + + 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 m4(_view._viewportDistance / bottom.z, 0.0f, 0.0f, + 0.0f, 25.5f, 0.0f); + + Matrix3x2 m2(_frameFront.x, 0.0f, _framePos.x, + 0.0f, _frameFront.y, _framePos.y); + + _field_109E = m4 * (facingRotation * m2); + + Vector4 B6(_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 C2(_view._viewportHalfWidth + bottom.x / bottom.z * _view._viewportDistance, + _view._viewportHalfHeight + bottom.y / bottom.z * _view._viewportDistance, + 1.0f / bottom.z, + 0.0f); + + _field_10B6.x = B6.x; + _field_10B6.y = B6.y; + _field_10B6.z = B6.z; + _field_10C2.x = C2.x; + _field_10C2.y = C2.y; + _field_10C2.z = C2.z; + _field_10CE = B6.w; + _field_10D2 = C2.w; + + Vector4 delta = C2 - B6; + + if (delta.y == 0.0f) + return; + + /* + * Calculate min and max Y + */ + + float screenMinY = 0.0f; + float screenMaxY = 479.0f; + + if (B6.y < screenMinY) { + if (C2.y < screenMinY) + return; + + float f = (screenMinY - B6.y) / delta.y; + B6 = B6 + f * delta; + } else if (B6.y > screenMaxY) { + if (C2.y >= screenMaxY) + return; + + float f = (screenMaxY - B6.y) / delta.y; + B6 = B6 + f * delta; + } + + if (C2.y < screenMinY) { + float f = (screenMinY - C2.y) / delta.y; + C2 = C2 + f * delta; + } else if (C2.y > screenMaxY) { + float f = (screenMaxY - C2.y) / delta.y; + C2 = C2 + f * delta; + } + + int bbox_min_y = (int)MIN(B6.y, C2.y); + int bbox_max_y = (int)MAX(B6.y, C2.y) + 1; + + /* + * Calculate min and max X + */ + + Matrix3x2 mB6 = _field_109E + Vector2(B6.x, 25.5f / B6.z); + Matrix3x2 mC2 = _field_109E + Vector2(C2.x, 25.5f / C2.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 = MIN(MAX((int)min_x, 0), 640); + int bbox_max_x = MIN(MAX((int)max_x + 1, 0), 640); + + _field_10B6.x = B6.x; + _field_10B6.y = B6.y; + _field_10B6.z = B6.z; + _field_10C2.x = C2.x; + _field_10C2.y = C2.y; + _field_10C2.z = C2.z; + _field_10CE = B6.w; + _field_10D2 = C2.w; + + _minX = bbox_min_x; + _minY = bbox_min_y; + _maxX = bbox_max_x; + _maxY = bbox_max_y; +} + +struct SliceLineIterator { + int _field_00[2][3]; + int _field_18; + int _field_1C; + + float _field_20; + float _field_24; + float _field_28; + float _field_2C; + float _field_30; + float _field_34; + int _field_38; + int _field_3C; + + void setup(float arg_1, float arg_2, float arg_3, + float arg_4, float arg_5, float arg_6, + float arg_7, float arg_8, + Matrix3x2 arg_9); + float line(); + void advance(); +}; + +void SliceLineIterator::setup( + float arg_1, float arg_2, float arg_3, + float arg_4, float arg_5, float arg_6, + float arg_7, float arg_8, + Matrix3x2 arg_9) +{ + _field_18 = (int)arg_5; + _field_1C = (int)arg_2; + + float var_3 = arg_2 - arg_5; + + if (var_3 <= 0.0f || arg_6 <= 0.0f) + _field_3C = _field_1C + 1; + + _field_20 = arg_6; + _field_24 = (arg_3 - arg_6) / var_3; + _field_2C = (arg_7 - arg_8) / var_3; + _field_28 = arg_8 - (arg_5 - floor(arg_5) - 1.0f) * _field_2C; + + _field_30 = arg_4; + _field_34 = (arg_1 - arg_4) / var_3; + _field_38 = (int)((25.5f / var_3) * (1.0f / arg_3 - 1.0f / arg_6) * 64.0); + _field_3C = _field_18; + + float var_54 = _field_30; + float var_55 = 25.5f / _field_20; + + Matrix3x2 translate_matrix = Matrix3x2(1.0f, 0.0f, var_54, + 0.0f, 1.0f, var_55); + + Matrix3x2 scale_matrix = Matrix3x2(65536.0f, 0.0f, 0.0f, + 0.0f, 64.0f, 0.0f); + + arg_9 = scale_matrix * (translate_matrix * arg_9); + + for (int r = 0; r != 2; ++r) + for (int c = 0; c != 3; ++c) + _field_00[r][c] = arg_9(r, c); +} + +float SliceLineIterator::line() { + float var_0 = 0.0f; + + if (_field_20 != 0.0f) + var_0 = _field_28 / _field_20; + + if (var_0 < 0.0) + var_0 = 0.0f; + + return var_0; +} + +void SliceLineIterator::advance() { + _field_20 += _field_24; + _field_28 += _field_2C; + _field_30 += _field_34; + _field_3C += 1; + _field_00[0][2] += (int)(65536.0f * _field_34); + _field_00[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::drawFrame(Graphics::Surface &surface) { + assert(_sliceFramePtr); + + SliceLineIterator sliceLineIterator; + sliceLineIterator.setup( + _field_10C2.x, _field_10C2.y, _field_10C2.z, + _field_10B6.x, _field_10B6.y, _field_10B6.z, + _field_10D2, _field_10CE, // 2 floats + _field_109E // 3x2 matrix + ); + + setupLookupTable(_t1, sliceLineIterator._field_00[0][0]); + setupLookupTable(_t2, sliceLineIterator._field_00[0][1]); + setupLookupTable(_t4, sliceLineIterator._field_00[1][0]); + setupLookupTable(_t5, sliceLineIterator._field_00[1][1]); + + int frameY = sliceLineIterator._field_18; + + uint16 *frameLinePtr = (uint16*)surface.getPixels() + 640 * frameY; + + while (sliceLineIterator._field_3C <= sliceLineIterator._field_1C) { + _c3 = sliceLineIterator._field_00[0][2]; + _c6 = sliceLineIterator._field_00[1][2]; + + float sliceLine = sliceLineIterator.line(); + + if (frameY >= 0 && frameY < 480) + drawSlice((int)sliceLine, frameLinePtr); + + sliceLineIterator.advance(); + frameY += 1; + frameLinePtr += 640; + } +} + +void SliceRenderer::drawSlice(int slice, uint16 *frameLinePtr) { + if (slice < 0 || 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; + + int zbufLinePtr[640]; + for (int i = 0; i != 640; ++i) + zbufLinePtr[i] = 65535; + + while (polyCount--) { + uint32 vertexCount = READ_LE_UINT32(p); + p += 4; + + if (vertexCount == 0) + continue; + + uint32 lastVertex = vertexCount - 1; + int lastVertexX = MAX((_t1[p[3*lastVertex]] + _t2[p[3*lastVertex+1]] + _c3) >> 16, 0); + + int previousVertexX = lastVertexX; + + while (vertexCount--) { + int vertexX = CLIP((_t1[p[0]] + _t2[p[1]] + _c3) >> 16, 0, 640); + + if (vertexX > previousVertexX) { + int vertexZ = (_t4[p[0]] + _t5[p[1]] + _c6) >> 6; + + if (vertexZ >= 0 && vertexZ < 65536) { + for (int x = previousVertexX; x != vertexX; ++x) { + if (vertexZ < zbufLinePtr[x]) { + frameLinePtr[x] = palette[p[2]]; + zbufLinePtr[x] = (int16)vertexZ; + } + } + } + } + p += 3; + previousVertexX = vertexX; + } + } +} + +} // End of namespace BladeRunner -- cgit v1.2.3