1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "bladerunner/slice_renderer.h"
24 
25 #include "bladerunner/bladerunner.h"
26 #include "bladerunner/lights.h"
27 #include "bladerunner/screen_effects.h"
28 #include "bladerunner/set_effects.h"
29 #include "bladerunner/slice_animations.h"
30 
31 #include "common/memstream.h"
32 #include "common/rect.h"
33 #include "common/util.h"
34 
35 namespace BladeRunner {
36 
SliceRenderer(BladeRunnerEngine * vm)37 SliceRenderer::SliceRenderer(BladeRunnerEngine *vm) {
38 	_vm = vm;
39 	_pixelFormat = screenPixelFormat();
40 
41 	// original game is going just up to 942 and not 997
42 	for (int i = 0; i < ARRAYSIZE(_animationsShadowEnabled); ++i) {
43 		_animationsShadowEnabled[i] = true;
44 	}
45 	_animation = -1;
46 	_frame     = -1;
47 	_facing    = 0.0f;
48 	_scale     = 0.0f;
49 
50 	_screenEffects = nullptr;
51 	_view          = nullptr;
52 	_lights        = nullptr;
53 	_setEffects    = nullptr;
54 	_sliceFramePtr = nullptr;
55 
56 	_frameBottomZ      = 0.0f;
57 	_frameSliceHeight  = 0.0f;
58 	_framePaletteIndex = 0;
59 	_frameSliceCount   = 0;
60 	_startSlice        = 0.0f;
61 	_endSlice          = 0.0f;
62 	_m13               = 0;
63 	_m23               = 0;
64 
65 	_shadowPolygonDefault[ 0] = Vector3( 16.0f,  96.0f, 0.0f);
66 	_shadowPolygonDefault[ 1] = Vector3( 16.0f, 160.0f, 0.0f);
67 	_shadowPolygonDefault[ 2] = Vector3( 64.0f, 192.0f, 0.0f);
68 	_shadowPolygonDefault[ 3] = Vector3( 80.0f, 240.0f, 0.0f);
69 	_shadowPolygonDefault[ 4] = Vector3(160.0f, 240.0f, 0.0f);
70 	_shadowPolygonDefault[ 5] = Vector3(192.0f, 192.0f, 0.0f);
71 	_shadowPolygonDefault[ 6] = Vector3(240.0f, 160.0f, 0.0f);
72 	_shadowPolygonDefault[ 7] = Vector3(240.0f,  96.0f, 0.0f);
73 	_shadowPolygonDefault[ 8] = Vector3(192.0f,  64.0f, 0.0f);
74 	_shadowPolygonDefault[ 9] = Vector3(160.0f,  16.0f, 0.0f);
75 	_shadowPolygonDefault[10] = Vector3( 96.0f,  16.0f, 0.0f);
76 	_shadowPolygonDefault[11] = Vector3( 64.0f,  64.0f, 0.0f);
77 
78 	for (int i = 0; i < 12; ++i) {
79 		_shadowPolygonCurrent[i] = Vector3(0.0f, 0.0f, 0.0f);
80 	}
81 }
82 
~SliceRenderer()83 SliceRenderer::~SliceRenderer() {
84 }
85 
setScreenEffects(ScreenEffects * screenEffects)86 void SliceRenderer::setScreenEffects(ScreenEffects *screenEffects) {
87 	_screenEffects = screenEffects;
88 }
89 
setView(View * view)90 void SliceRenderer::setView(View *view) {
91 	_view = view;
92 }
93 
setLights(Lights * lights)94 void SliceRenderer::setLights(Lights *lights) {
95 	_lights = lights;
96 }
97 
setSetEffects(SetEffects * setEffects)98 void SliceRenderer::setSetEffects(SetEffects *setEffects) {
99 	_setEffects = setEffects;
100 }
101 
setupFrameInWorld(int animationId,int animationFrame,Vector3 position,float facing,float scale)102 void SliceRenderer::setupFrameInWorld(int animationId, int animationFrame, Vector3 position, float facing, float scale) {
103 	_position = position;
104 	_facing = facing;
105 	_scale = scale;
106 
107 	loadFrame(animationId, animationFrame);
108 
109 	calculateBoundingRect();
110 }
111 
getScreenRectangle(Common::Rect * screenRectangle,int animationId,int animationFrame,Vector3 position,float facing,float scale)112 void SliceRenderer::getScreenRectangle(Common::Rect *screenRectangle, int animationId, int animationFrame, Vector3 position, float facing, float scale) {
113 	assert(screenRectangle);
114 	setupFrameInWorld(animationId, animationFrame, position, facing, scale);
115 	*screenRectangle = _screenRectangle;
116 }
117 
calculateFacingRotationMatrix()118 Matrix3x2 SliceRenderer::calculateFacingRotationMatrix() {
119 	assert(_sliceFramePtr);
120 
121 	Vector3 viewPos = _view->_sliceViewMatrix * _position;
122 	float dir = atan2(viewPos.x, viewPos.z) + _facing;
123 	float s = sin(dir);
124 	float c = cos(dir);
125 
126 	Matrix3x2 mRotation(c, -s, 0.0f,
127 	                    s,  c, 0.0f);
128 
129 	Matrix3x2 mView(_view->_sliceViewMatrix(0,0), _view->_sliceViewMatrix(0,1), 0.0f,
130 	                _view->_sliceViewMatrix(2,0), _view->_sliceViewMatrix(2,1), 0.0f);
131 
132 	return mView * mRotation;
133 }
134 
calculateBoundingRect()135 void SliceRenderer::calculateBoundingRect() {
136 	assert(_sliceFramePtr);
137 
138 	_screenRectangle.left   = 0;
139 	_screenRectangle.right  = 0;
140 	_screenRectangle.top    = 0;
141 	_screenRectangle.bottom = 0;
142 
143 	Matrix4x3 viewMatrix = _view->_sliceViewMatrix;
144 
145 	Vector3 frameBottom = Vector3(0.0f, 0.0f, _frameBottomZ);
146 	Vector3 frameTop    = Vector3(0.0f, 0.0f, _frameBottomZ + _frameSliceCount * _frameSliceHeight);
147 
148 	Vector3 bottom = viewMatrix * (_position + frameBottom);
149 	Vector3 top    = viewMatrix * (_position + frameTop);
150 
151 	top = bottom + _scale * (top - bottom);
152 
153 	if (bottom.z <= 0.0f || top.z <= 0.0f) {
154 		return;
155 	}
156 
157 	Vector4 startScreenVector(
158 	           _view->_viewportPosition.x + (top.x / top.z) * _view->_viewportPosition.z,
159 	           _view->_viewportPosition.y + (top.y / top.z) * _view->_viewportPosition.z,
160 	           1.0f / top.z,
161 	           _frameSliceCount * (1.0f / top.z));
162 
163 	Vector4 endScreenVector(
164 	           _view->_viewportPosition.x + (bottom.x / bottom.z) * _view->_viewportPosition.z,
165 	           _view->_viewportPosition.y + (bottom.y / bottom.z) * _view->_viewportPosition.z,
166 	           1.0f / bottom.z,
167 	           0.0f);
168 
169 	Vector4 delta = endScreenVector - startScreenVector;
170 
171 	if (delta.y == 0.0f) {
172 		return;
173 	}
174 
175 	/*
176 	 * Calculate min and max Y
177 	 */
178 
179 	float screenTop    =   0.0f;
180 	float screenBottom = 479.0f;
181 
182 	if (startScreenVector.y < screenTop) {
183 		if (endScreenVector.y < screenTop) {
184 			return;
185 		}
186 		float f = (screenTop - startScreenVector.y) / delta.y;
187 		startScreenVector = startScreenVector + f * delta;
188 	} else if (startScreenVector.y > screenBottom) {
189 		if (endScreenVector.y >= screenBottom) {
190 			return;
191 		}
192 		float f = (screenBottom - startScreenVector.y) / delta.y;
193 		startScreenVector = startScreenVector + f * delta;
194 	}
195 
196 	if (endScreenVector.y < screenTop) {
197 		float f = (screenTop - endScreenVector.y) / delta.y;
198 		endScreenVector = endScreenVector + f * delta;
199 	} else if (endScreenVector.y > screenBottom) {
200 		float f = (screenBottom - endScreenVector.y) / delta.y;
201 		endScreenVector = endScreenVector + f * delta;
202 	}
203 
204 	_screenRectangle.top    = (int)MIN(startScreenVector.y, endScreenVector.y);
205 	_screenRectangle.bottom = (int)MAX(startScreenVector.y, endScreenVector.y) + 1;
206 
207 	/*
208 	 * Calculate min and max X
209 	 */
210 
211 	Matrix3x2 facingRotation = calculateFacingRotationMatrix();
212 
213 	Matrix3x2 mProjection(_view->_viewportPosition.z / bottom.z,  0.0f, 0.0f,
214 	                                                       0.0f, 25.5f, 0.0f);
215 
216 	Matrix3x2 mOffset(1.0f, 0.0f, _framePos.x,
217 	                  0.0f, 1.0f, _framePos.y);
218 
219 	Matrix3x2 mScale(_frameScale.x,          0.0f, 0.0f,
220 	                          0.0f, _frameScale.y, 0.0f);
221 
222 	_mvpMatrix = mProjection * (facingRotation * (mOffset * mScale));
223 
224 	Matrix3x2 mStart(
225 		1.0f, 0.0f, startScreenVector.x,
226 		0.0f, 1.0f, 25.5f / startScreenVector.z
227 	);
228 
229 	Matrix3x2 mEnd(
230 		1.0f, 0.0f, endScreenVector.x,
231 		0.0f, 1.0f, 25.5f / endScreenVector.z
232 	);
233 
234 	Matrix3x2 mStartMVP = mStart * _mvpMatrix;
235 	Matrix3x2 mEndMVP   = mEnd   * _mvpMatrix;
236 
237 	float minX =  640.0f;
238 	float maxX =    0.0f;
239 
240 	for (float i = 0.0f; i <= 255.0f; i += 255.0f) {
241 		for (float j = 0.0f; j <= 255.0f; j += 255.0f) {
242 			Vector2 v1 = mStartMVP * Vector2(i, j);
243 			minX = MIN(minX, v1.x);
244 			maxX = MAX(maxX, v1.x);
245 
246 			Vector2 v2 = mEndMVP * Vector2(i, j);
247 			minX = MIN(minX, v2.x);
248 			maxX = MAX(maxX, v2.x);
249 		}
250 	}
251 
252 	_screenRectangle.left  = CLIP((int)minX,     0, 640);
253 	_screenRectangle.right = CLIP((int)maxX + 1, 0, 640);
254 
255 	_startScreenVector.x = startScreenVector.x;
256 	_startScreenVector.y = startScreenVector.y;
257 	_startScreenVector.z = startScreenVector.z;
258 	_endScreenVector.x   = endScreenVector.x;
259 	_endScreenVector.y   = endScreenVector.y;
260 	_endScreenVector.z   = endScreenVector.z;
261 	_startSlice          = startScreenVector.w;
262 	_endSlice            = endScreenVector.w;
263 }
264 
loadFrame(int animation,int frame)265 void SliceRenderer::loadFrame(int animation, int frame) {
266 	_animation = animation;
267 	_frame = frame;
268 	_sliceFramePtr = _vm->_sliceAnimations->getFramePtr(_animation, _frame);
269 
270 	Common::MemoryReadStream stream((byte *)_sliceFramePtr, _vm->_sliceAnimations->_animations[_animation].frameSize);
271 
272 	_frameScale.x      = stream.readFloatLE();
273 	_frameScale.y      = stream.readFloatLE();
274 	_frameSliceHeight  = stream.readFloatLE();
275 	_framePos.x        = stream.readFloatLE();
276 	_framePos.y        = stream.readFloatLE();
277 	_frameBottomZ      = stream.readFloatLE();
278 	_framePaletteIndex = stream.readUint32LE();
279 	_frameSliceCount   = stream.readUint32LE();
280 }
281 
282 struct SliceLineIterator {
283 	Matrix3x2 _sliceMatrix;
284 	int _startY;
285 	int _endY;
286 	int _currentY;
287 
288 	float _currentZ;
289 	float _stepZ;
290 	float _currentSlice;
291 	float _stepSlice;
292 
293 	float _currentX;
294 	float _stepX;
295 
296 	float _field_38;
297 
298 	void setup(float endScreenX,   float endScreenY,   float endScreenZ,
299 	           float startScreenX, float startScreenY, float startScreenZ,
300 	           float endSlice,     float startSlice,
301 	           Matrix3x2 m);
302 	float line() const;
303 	void advance();
304 };
305 
setup(float endScreenX,float endScreenY,float endScreenZ,float startScreenX,float startScreenY,float startScreenZ,float endSlice,float startSlice,Matrix3x2 m)306 void SliceLineIterator::setup(
307 		float endScreenX,   float endScreenY,   float endScreenZ,
308 		float startScreenX, float startScreenY, float startScreenZ,
309 		float endSlice,     float startSlice,
310 		Matrix3x2 m) {
311 	_startY   = (int)startScreenY;
312 	_endY     = (int)endScreenY;
313 	_currentY = _startY;
314 
315 	float size = endScreenY - startScreenY;
316 
317 	if (size <= 0.0f || startScreenZ <= 0.0f) {
318 		_currentY = _endY + 1;
319 	}
320 
321 	_currentZ  = startScreenZ;
322 	_stepZ     = (endScreenZ - startScreenZ) / size;
323 
324 	_stepSlice     = (endSlice - startSlice) / size;
325 	_currentSlice  = startSlice - (startScreenY - floor(startScreenY) - 1.0f) * _stepSlice;
326 
327 	_currentX = startScreenX;
328 	_stepX    = (endScreenX - startScreenX) / size;
329 
330 	_field_38 = (25.5f / size) * (1.0f / endScreenZ - 1.0f / startScreenZ);
331 
332 	float offsetX =         _currentX;
333 	float offsetZ = 25.5f / _currentZ;
334 
335 	Matrix3x2 mTranslate = Matrix3x2(1.0f, 0.0f, offsetX,
336 	                                 0.0f, 1.0f, offsetZ);
337 
338 	Matrix3x2 mScale = Matrix3x2(65536.0f,  0.0f, 0.0f,  // x position is using fixed-point precisson with 16 bits
339 	                                 0.0f, 64.0f, 0.0f); // z position is using fixed-point precisson with 6 bits
340 
341 	_sliceMatrix = mScale * (mTranslate * m);
342 }
343 
line() const344 float SliceLineIterator::line() const {
345 	float var_0 = 0.0f;
346 
347 	if (_currentZ != 0.0f)
348 		var_0 = _currentSlice / _currentZ;
349 
350 	if (var_0 < 0.0)
351 		var_0 = 0.0f;
352 
353 	return var_0;
354 }
355 
advance()356 void SliceLineIterator::advance() {
357 	_currentZ     += _stepZ;
358 	_currentSlice += _stepSlice;
359 	_currentX     += _stepX;
360 	++_currentY;
361 
362 	_sliceMatrix._m[0][2] += _stepX * 65536.0f;
363 	_sliceMatrix._m[1][2] += _field_38 * 64.0f;
364 }
365 
setupLookupTable(int t[256],int inc)366 static void setupLookupTable(int t[256], int inc) {
367 	int v = 0;
368 	for (int i = 0; i != 256; ++i) {
369 		t[i] = v;
370 		v += inc;
371 	}
372 }
373 
drawInWorld(int animationId,int animationFrame,Vector3 position,float facing,float scale,Graphics::Surface & surface,uint16 * zbuffer)374 void SliceRenderer::drawInWorld(int animationId, int animationFrame, Vector3 position, float facing, float scale, Graphics::Surface &surface, uint16 *zbuffer) {
375 	assert(_lights);
376 	assert(_setEffects);
377 	//assert(_view);
378 
379 	setupFrameInWorld(animationId, animationFrame, position, facing, scale);
380 
381 	assert(_sliceFramePtr);
382 
383 	if (_screenRectangle.isEmpty()) {
384 		return;
385 	}
386 
387 	SliceLineIterator sliceLineIterator;
388 	sliceLineIterator.setup(
389 		_endScreenVector.x,   _endScreenVector.y,   _endScreenVector.z,
390 		_startScreenVector.x, _startScreenVector.y, _startScreenVector.z,
391 		_endSlice,            _startSlice,
392 		_mvpMatrix
393 	);
394 
395 	SliceRendererLights sliceRendererLights = SliceRendererLights(_lights);
396 
397 	_lights->setupFrame(_view->_frame);
398 	_setEffects->setupFrame(_view->_frame);
399 
400 	float sliceLine = sliceLineIterator.line();
401 
402 	sliceRendererLights.calculateColorBase(
403 		Vector3(_position.x, _position.y, _position.z + _frameBottomZ + sliceLine * _frameSliceHeight),
404 		Vector3(_position.x, _position.y, _position.z + _frameBottomZ),
405 		sliceLineIterator._endY - sliceLineIterator._startY);
406 
407 	float setEffectsColorCoeficient;
408 	Color setEffectColor;
409 	_setEffects->calculateColor(
410 		_view->_cameraPosition,
411 		Vector3(_position.x, _position.y, _position.z + _frameBottomZ + sliceLine * _frameSliceHeight),
412 		&setEffectsColorCoeficient,
413 		&setEffectColor);
414 
415 	_lightsColor.r = setEffectsColorCoeficient * sliceRendererLights._finalColor.r * 65536.0f;
416 	_lightsColor.g = setEffectsColorCoeficient * sliceRendererLights._finalColor.g * 65536.0f;
417 	_lightsColor.b = setEffectsColorCoeficient * sliceRendererLights._finalColor.b * 65536.0f;
418 
419 	_setEffectColor.r = setEffectColor.r * 31.0f * 65536.0f;
420 	_setEffectColor.g = setEffectColor.g * 31.0f * 65536.0f;
421 	_setEffectColor.b = setEffectColor.b * 31.0f * 65536.0f;
422 
423 	setupLookupTable(_m12lookup, sliceLineIterator._sliceMatrix(0, 1));
424 	setupLookupTable(_m11lookup, sliceLineIterator._sliceMatrix(0, 0));
425 	_m13 = sliceLineIterator._sliceMatrix(0, 2);
426 	setupLookupTable(_m21lookup, sliceLineIterator._sliceMatrix(1, 0));
427 	setupLookupTable(_m22lookup, sliceLineIterator._sliceMatrix(1, 1));
428 	_m23 = sliceLineIterator._sliceMatrix(1, 2);
429 
430 	if (_animationsShadowEnabled[_animation]) {
431 		float coeficientShadow;
432 		Color colorShadow;
433 		_setEffects->calculateColor(
434 				_view->_cameraPosition,
435 				_position,
436 				&coeficientShadow,
437 				&colorShadow);
438 
439 		int transparency = 32.0f * sqrt(setEffectColor.r * setEffectColor.r + setEffectColor.g * setEffectColor.g + setEffectColor.b * setEffectColor.b);
440 
441 		drawShadowInWorld(transparency, surface, zbuffer);
442 	}
443 
444 	int frameY = sliceLineIterator._startY;
445 
446 	uint16 *zBufferLinePtr = zbuffer + 640 * frameY;
447 
448 	while (sliceLineIterator._currentY <= sliceLineIterator._endY) {
449 		_m13 = sliceLineIterator._sliceMatrix(0, 2);
450 		_m23 = sliceLineIterator._sliceMatrix(1, 2);
451 		sliceLine = sliceLineIterator.line();
452 
453 		sliceRendererLights.calculateColorSlice(Vector3(_position.x, _position.y, _position.z + _frameBottomZ + sliceLine * _frameSliceHeight));
454 
455 		if (sliceLineIterator._currentY & 1) {
456 			_setEffects->calculateColor(
457 				_view->_cameraPosition,
458 				Vector3(_position.x, _position.y, _position.z + _frameBottomZ + sliceLine * _frameSliceHeight),
459 				&setEffectsColorCoeficient,
460 				&setEffectColor);
461 		}
462 
463 		_lightsColor.r = setEffectsColorCoeficient * sliceRendererLights._finalColor.r * 65536.0f;
464 		_lightsColor.g = setEffectsColorCoeficient * sliceRendererLights._finalColor.g * 65536.0f;
465 		_lightsColor.b = setEffectsColorCoeficient * sliceRendererLights._finalColor.b * 65536.0f;
466 
467 		_setEffectColor.r = setEffectColor.r * 31.0f * 65536.0f;
468 		_setEffectColor.g = setEffectColor.g * 31.0f * 65536.0f;
469 		_setEffectColor.b = setEffectColor.b * 31.0f * 65536.0f;
470 
471 		if (frameY >= 0 && frameY < surface.h) {
472 			drawSlice((int)sliceLine, true, frameY, surface, zBufferLinePtr);
473 		}
474 
475 		sliceLineIterator.advance();
476 		++frameY;
477 		zBufferLinePtr += 640;
478 	}
479 }
480 
drawOnScreen(int animationId,int animationFrame,int screenX,int screenY,float facing,float scale,Graphics::Surface & surface)481 void SliceRenderer::drawOnScreen(int animationId, int animationFrame, int screenX, int screenY, float facing, float scale, Graphics::Surface &surface) {
482 	if (scale == 0.0f) {
483 		return;
484 	}
485 	_position.x = 0;
486 	_position.y = 0;
487 	_position.z = 0;
488 	_facing = facing;
489 
490 	loadFrame(animationId, animationFrame);
491 
492 	float frameHeight = _frameSliceHeight * _frameSliceCount;
493 	float frameSize = sqrt(_frameScale.x * 255.0f * _frameScale.x * 255.0f + _frameScale.y * 255.0f * _frameScale.y * 255.0f);
494 	float size = scale / MAX(frameSize, frameHeight);
495 
496 	float s = sin(_facing);
497 	float c = cos(_facing);
498 
499 	Matrix3x2 mRotation(c, -s, 0.0f,
500 	                    s,  c, 0.0f);
501 
502 	Matrix3x2 mFrame(_frameScale.x,           0.0f, _framePos.x,
503 	                           0.0f, _frameScale.y, _framePos.y);
504 
505 	Matrix3x2 mScale(size,  0.0f, 0.0f,
506 	                 0.0f, 25.5f, 0.0f);
507 
508 	Matrix3x2 mTranslate(1.0f, 0.0f, screenX,
509 	                     0.0f, 1.0f, 32768.0f);
510 
511 	Matrix3x2 mScaleFixed(65536.0f,  0.0f, 0.0f,  // x position is using fixed-point precisson with 16 bits
512 	                          0.0f, 64.0f, 0.0f); // z position is using fixed-point precisson with 6 bits
513 
514 	Matrix3x2 m = mScaleFixed * (mTranslate * (mScale * (mRotation * mFrame)));
515 
516 	setupLookupTable(_m11lookup, m(0, 0));
517 	setupLookupTable(_m12lookup, m(0, 1));
518 	_m13 = m(0, 2);
519 	setupLookupTable(_m21lookup, m(1, 0));
520 	setupLookupTable(_m22lookup, m(1, 1));
521 	_m23 = m(1, 2);
522 
523 	int frameY = screenY + (size / 2.0f * frameHeight);
524 	int currentY = frameY;
525 
526 	float currentSlice = 0;
527 	float sliceStep = 1.0f / size / _frameSliceHeight;
528 
529 	uint16 lineZbuffer[640];
530 
531 	while (currentSlice < _frameSliceCount) {
532 		if (currentY >= 0 && currentY < surface.h) {
533 			memset(lineZbuffer, 0xFF, 640 * 2);
534 			drawSlice(currentSlice, false, currentY, surface, lineZbuffer);
535 			currentSlice += sliceStep;
536 			--currentY;
537 		}
538 	}
539 }
540 
drawSlice(int slice,bool advanced,int y,Graphics::Surface & surface,uint16 * zbufferLine)541 void SliceRenderer::drawSlice(int slice, bool advanced, int y, Graphics::Surface &surface, uint16 *zbufferLine) {
542 	if (slice < 0 || (uint32)slice >= _frameSliceCount) {
543 		return;
544 	}
545 
546 	SliceAnimations::Palette &palette = _vm->_sliceAnimations->getPalette(_framePaletteIndex);
547 
548 	byte *p = (byte *)_sliceFramePtr + 0x20 + 4 * slice;
549 
550 	uint32 polyOffset = READ_LE_UINT32(p);
551 
552 	p = (byte *)_sliceFramePtr + polyOffset;
553 
554 	uint32 polyCount = READ_LE_UINT32(p);
555 	p += 4;
556 
557 	while (polyCount--) {
558 		uint32 vertexCount = READ_LE_UINT32(p);
559 		p += 4;
560 
561 		if (vertexCount == 0)
562 			continue;
563 
564 		uint32 lastVertex = vertexCount - 1;
565 		int lastVertexX = MAX((_m11lookup[p[3 * lastVertex]] + _m12lookup[p[3 * lastVertex + 1]] + _m13) / 65536, 0);
566 
567 		int previousVertexX = lastVertexX;
568 
569 		while (vertexCount--) {
570 			int vertexX = CLIP((_m11lookup[p[0]] + _m12lookup[p[1]] + _m13) / 65536, 0, 640);
571 
572 			if (vertexX > previousVertexX) {
573 				int vertexZ = (_m21lookup[p[0]] + _m22lookup[p[1]] + _m23) / 64;
574 
575 				if (vertexZ >= 0 && vertexZ < 65536) {
576 					uint32 outColor = palette.value[p[2]];
577 					if (advanced) {
578 						Color256 aescColor = { 0, 0, 0 };
579 						_screenEffects->getColor(&aescColor, vertexX, y, vertexZ);
580 
581 						Color256 color = palette.color[p[2]];
582 						color.r = ((int)(_setEffectColor.r + _lightsColor.r * color.r) / 65536) + aescColor.r;
583 						color.g = ((int)(_setEffectColor.g + _lightsColor.g * color.g) / 65536) + aescColor.g;
584 						color.b = ((int)(_setEffectColor.b + _lightsColor.b * color.b) / 65536) + aescColor.b;
585 						// We need to convert from 5 bits per channel (r,g,b) to 8 bits
586 						outColor = _pixelFormat.RGBToColor(Color::get8BitColorFrom5Bit(color.r), Color::get8BitColorFrom5Bit(color.g), Color::get8BitColorFrom5Bit(color.b));
587 					}
588 
589 					for (int x = previousVertexX; x != vertexX; ++x) {
590 						if (vertexZ < zbufferLine[x]) {
591 							zbufferLine[x] = (uint16)vertexZ;
592 
593 							void *dstPtr = surface.getBasePtr(CLIP(x, 0, surface.w - 1), CLIP(y, 0, surface.h - 1));
594 							drawPixel(surface, dstPtr, outColor);
595 						}
596 					}
597 				}
598 			}
599 			p += 3;
600 			previousVertexX = vertexX;
601 		}
602 	}
603 }
604 
drawShadowInWorld(int transparency,Graphics::Surface & surface,uint16 * zbuffer)605 void SliceRenderer::drawShadowInWorld(int transparency, Graphics::Surface &surface, uint16 *zbuffer) {
606 	Matrix4x3 mOffset(
607 		1.0f, 0.0f, 0.0f, _framePos.x,
608 		0.0f, 1.0f, 0.0f, _framePos.y,
609 		0.0f, 0.0f, 1.0f, 0.0f);
610 
611 	Matrix4x3 mTransition(
612 		1.0f, 0.0f, 0.0f, _position.x,
613 		0.0f, 1.0f, 0.0f, _position.y,
614 		0.0f, 0.0f, 1.0f, _position.z);
615 
616 	Matrix4x3 mRotation(
617 		cos(_facing), -sin(_facing), 0.0f, 0.0f,
618 		sin(_facing),  cos(_facing), 0.0f, 0.0f,
619 		        0.0f,          0.0f, 1.0f, 0.0f);
620 
621 	Matrix4x3 mScale(
622 		_frameScale.x,          0.0f,              0.0f, 0.0f,
623 		         0.0f, _frameScale.y,              0.0f, 0.0f,
624 		         0.0f,          0.0f, _frameSliceHeight, 0.0f);
625 
626 	Matrix4x3 m = _view->_sliceViewMatrix * (mTransition * (mRotation * (mOffset * mScale)));
627 
628 	for (int i = 0; i < 12; ++i) {
629 		Vector3 t = m * _shadowPolygonDefault[i];
630 		if (t.z > 0.0f) {
631 			_shadowPolygonCurrent[i] = Vector3(
632 				_view->_viewportPosition.x + t.x / t.z * _view->_viewportPosition.z,
633 				_view->_viewportPosition.y + t.y / t.z * _view->_viewportPosition.z,
634 				t.z * 25.5f
635 			);
636 		} else {
637 			_shadowPolygonCurrent[i] = Vector3(0.0f, 0.0f, 0.0f);
638 		}
639 	}
640 
641 	drawShadowPolygon(transparency, surface, zbuffer);
642 }
643 
drawShadowPolygon(int transparency,Graphics::Surface & surface,uint16 * zbuffer)644 void SliceRenderer::drawShadowPolygon(int transparency, Graphics::Surface &surface, uint16 *zbuffer) {
645 	// this simplified polygon drawing algo is in the game
646 
647 	int yMax = 0;
648 	int yMin = 480;
649 	uint16 zMin = 65535;
650 
651 	int polygonLeft[480] = {};
652 	int polygonRight[480] = {};
653 
654 	int iNext = 11;
655 	for (int i = 0; i < 12; ++i) {
656 		int xCurrent = _shadowPolygonCurrent[i].x;
657 		int yCurrent = _shadowPolygonCurrent[i].y;
658 		int xNext = _shadowPolygonCurrent[iNext].x;
659 		int yNext = _shadowPolygonCurrent[iNext].y;
660 
661 		if (yCurrent < yMin) {
662 			yMin = yCurrent;
663 		}
664 		if (yCurrent > yMax) {
665 			yMax = yCurrent;
666 		}
667 		if (_shadowPolygonCurrent[i].z < zMin) {
668 			zMin = _shadowPolygonCurrent[i].z;
669 		}
670 
671 		int xDelta = abs(xNext - xCurrent);
672 		int yDelta = abs(yNext - yCurrent);
673 
674 		int xDirection = -1;
675 		if (xCurrent < xNext) {
676 			xDirection = 1;
677 		}
678 
679 		int xCounter = 0;
680 
681 		int x = xCurrent;
682 		int y = yCurrent;
683 
684 		if (yCurrent > yNext) {
685 			while (y >= yNext) {
686 				if (y >= 0 && y < 480) {
687 					polygonLeft[y] = x;
688 				}
689 				xCounter += xDelta;
690 				while (xCounter >= yDelta) {
691 					x += xDirection;
692 					xCounter -= yDelta;
693 				}
694 				--y;
695 			}
696 		} else if (yCurrent < yNext) {
697 			while (y <= yNext) {
698 				if (y >= 0 && y < 480) {
699 					polygonRight[y] = x;
700 				}
701 				xCounter += xDelta;
702 				while (xCounter >= yDelta) {
703 					x += xDirection;
704 					xCounter -= yDelta;
705 				}
706 				++y;
707 			}
708 		}
709 		iNext = (iNext + 1) % 12;
710 	}
711 
712 	yMax = CLIP(yMax, 0, 480);
713 	yMin = CLIP(yMin, 0, 480);
714 
715 	static const int ditheringFactor[] = {
716 		0,  8,  2, 10,
717 		12, 4, 14,  6,
718 		3, 11,  1,  9,
719 		15, 7, 13,  5
720 	};
721 
722 	for (int y = yMin; y < yMax; ++y) {
723 		int xMin = CLIP(polygonLeft[y], 0, 640);
724 		int xMax = CLIP(polygonRight[y], 0, 640);
725 
726 		for (int x = MIN(xMin, xMax); x < MAX(xMin, xMax); ++x) {
727 			uint16 z = zbuffer[x + y * 640];
728 			void *pixel = surface.getBasePtr(CLIP(x, 0, surface.w - 1), CLIP(y, 0, surface.h - 1));
729 
730 			if (z >= zMin) {
731 				int index = (x & 3) + ((y & 3) << 2);
732 				if (transparency - ditheringFactor[index] <= 0) {
733 					uint8 r, g, b;
734 					surface.format.colorToRGB(READ_UINT32(pixel), r, g, b);
735 					r *= 0.75f;
736 					g *= 0.75f;
737 					b *= 0.75f;
738 
739 					drawPixel(surface, pixel, surface.format.RGBToColor(r, g, b));
740 				}
741 			}
742 		}
743 	}
744 }
745 
preload(int animationId)746 void SliceRenderer::preload(int animationId) {
747 	int frameCount = _vm->_sliceAnimations->getFrameCount(animationId);
748 	for (int i = 0; i < frameCount; ++i) {
749 		_vm->_sliceAnimations->getFramePtr(animationId, i);
750 	}
751 }
752 
disableShadows(int animationsIdsList[],int listSize)753 void SliceRenderer::disableShadows(int animationsIdsList[], int listSize) {
754 	for (int i = 0; i < listSize; ++i) {
755 		_animationsShadowEnabled[animationsIdsList[i]] = false;
756 	}
757 }
758 
SliceRendererLights(Lights * lights)759 SliceRendererLights::SliceRendererLights(Lights *lights) {
760 	_finalColor.r = 0.0f;
761 	_finalColor.g = 0.0f;
762 	_finalColor.b = 0.0f;
763 
764 	_lights = lights;
765 
766 	for (int i = 0; i < 20; ++i) {
767 		_cacheColor[i].r = 0.0f;
768 		_cacheColor[i].g = 0.0f;
769 		_cacheColor[i].b = 0.0f;
770 	}
771 
772 	_cacheRecalculation = 0.0f;
773 }
774 
calculateColorBase(Vector3 position1,Vector3 position2,float height)775 void SliceRendererLights::calculateColorBase(Vector3 position1, Vector3 position2, float height) {
776 	_finalColor.r = 0.0f;
777 	_finalColor.g = 0.0f;
778 	_finalColor.b = 0.0f;
779 	_cacheRecalculation = 0;
780 	if (_lights) {
781 		for (uint i = 0; i < _lights->_lights.size(); ++i) {
782 			Light *light = _lights->_lights[i];
783 			if (i < 20) {
784 				float cacheCoeficient = light->calculate(position1, position2/*, height*/);
785 				_cacheStart[i] = cacheCoeficient;
786 				_cacheCounter[i] = cacheCoeficient;
787 
788 				Color color;
789 				light->calculateColor(&color, position1);
790 				_cacheColor[i] = color;
791 				_finalColor.r += color.r;
792 				_finalColor.g += color.g;
793 				_finalColor.b += color.b;
794 			} else {
795 				Color color;
796 				light->calculateColor(&color, position1);
797 				_finalColor.r += color.r;
798 				_finalColor.g += color.g;
799 				_finalColor.b += color.b;
800 			}
801 		}
802 
803 		_finalColor.r += _lights->_ambientLightColor.r;
804 		_finalColor.g += _lights->_ambientLightColor.g;
805 		_finalColor.b += _lights->_ambientLightColor.b;
806 	}
807 }
808 
calculateColorSlice(Vector3 position)809 void SliceRendererLights::calculateColorSlice(Vector3 position) {
810 	_finalColor.r = 0.0f;
811 	_finalColor.g = 0.0f;
812 	_finalColor.b = 0.0f;
813 
814 	if (_lights) {
815 		for (uint i = 0; i < _lights->_lights.size(); ++i) {
816 			Light *light = _lights->_lights[i];
817 			if (i < 20) {
818 				_cacheCounter[i] -= 1.0f;
819 				if (_cacheCounter[i] <= 0.0f) {
820 					do {
821 						_cacheCounter[i] = _cacheCounter[i] + _cacheStart[i];
822 					} while (_cacheCounter[i] <= 0.0f);
823 					light->calculateColor(&_cacheColor[i], position);
824 					++_cacheRecalculation;
825 				}
826 				_finalColor.r += _cacheColor[i].r;
827 				_finalColor.g += _cacheColor[i].g;
828 				_finalColor.b += _cacheColor[i].b;
829 			} else {
830 				Color color;
831 				light->calculateColor(&color, position);
832 				++_cacheRecalculation;
833 				_finalColor.r += color.r;
834 				_finalColor.g += color.g;
835 				_finalColor.b += color.b;
836 			}
837 		}
838 		_finalColor.r += _lights->_ambientLightColor.r;
839 		_finalColor.g += _lights->_ambientLightColor.g;
840 		_finalColor.b += _lights->_ambientLightColor.b;
841 	}
842 }
843 
844 } // End of namespace BladeRunner
845