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/fog.h"
24 
25 #include "common/stream.h"
26 
27 namespace BladeRunner {
28 
Fog()29 Fog::Fog() {
30 	_frameCount         = 0;
31 	_animatedParameters = 0;
32 	_fogDensity         = 0.0f;
33 	_animationData      = nullptr;
34 	_m11ptr             = nullptr;
35 	_m12ptr             = nullptr;
36 	_m13ptr             = nullptr;
37 	_m14ptr             = nullptr;
38 	_m21ptr             = nullptr;
39 	_m22ptr             = nullptr;
40 	_m23ptr             = nullptr;
41 	_m24ptr             = nullptr;
42 	_m31ptr             = nullptr;
43 	_m32ptr             = nullptr;
44 	_m33ptr             = nullptr;
45 	_m34ptr             = nullptr;
46 	_next               = nullptr;
47 }
48 
~Fog()49 Fog::~Fog() {
50 	if (_animationData != nullptr) {
51 		delete[] _animationData;
52 	}
53 }
54 
readCommon(Common::ReadStream * stream)55 int Fog::readCommon(Common::ReadStream *stream) {
56 	int offset = stream->readUint32LE();
57 	char buf[20];
58 	stream->read(buf, sizeof(buf));
59 	_name = buf;
60 	_fogColor.r = stream->readFloatLE();
61 	_fogColor.g = stream->readFloatLE();
62 	_fogColor.b = stream->readFloatLE();
63 	_fogDensity = stream->readFloatLE();
64 	return offset;
65 }
66 
readAnimationData(Common::ReadStream * stream,int size)67 void Fog::readAnimationData(Common::ReadStream *stream, int size) {
68 	_animatedParameters = stream->readUint32LE();
69 
70 	if (_animationData != nullptr) {
71 		delete[] _animationData;
72 	}
73 
74 	int floatCount = size / 4;
75 	_animationData = new float[floatCount];
76 	for (int i = 0; i < floatCount; ++i) {
77 		_animationData[i] = stream->readFloatLE();
78 	}
79 
80 	_m11ptr = _animationData;
81 	_m12ptr = _m11ptr + ((_animatedParameters &   0x1) ? _frameCount : 1);
82 	_m13ptr = _m12ptr + ((_animatedParameters &   0x2) ? _frameCount : 1);
83 	_m14ptr = _m13ptr + ((_animatedParameters &   0x4) ? _frameCount : 1);
84 	_m21ptr = _m14ptr + ((_animatedParameters &   0x8) ? _frameCount : 1);
85 	_m22ptr = _m21ptr + ((_animatedParameters &  0x10) ? _frameCount : 1);
86 	_m23ptr = _m22ptr + ((_animatedParameters &  0x20) ? _frameCount : 1);
87 	_m24ptr = _m23ptr + ((_animatedParameters &  0x40) ? _frameCount : 1);
88 	_m31ptr = _m24ptr + ((_animatedParameters &  0x80) ? _frameCount : 1);
89 	_m32ptr = _m31ptr + ((_animatedParameters & 0x100) ? _frameCount : 1);
90 	_m33ptr = _m32ptr + ((_animatedParameters & 0x200) ? _frameCount : 1);
91 	_m34ptr = _m33ptr + ((_animatedParameters & 0x400) ? _frameCount : 1);
92 
93 	setupFrame(0);
94 }
95 
reset()96 void Fog::reset() {
97 }
98 
setupFrame(int frame)99 void Fog::setupFrame(int frame) {
100 	int offset = frame % _frameCount;
101 	_matrix._m[0][0] = ((_animatedParameters &   0x1) ? _m11ptr[offset] : *_m11ptr);
102 	_matrix._m[0][1] = ((_animatedParameters &   0x2) ? _m12ptr[offset] : *_m12ptr);
103 	_matrix._m[0][2] = ((_animatedParameters &   0x4) ? _m13ptr[offset] : *_m13ptr);
104 	_matrix._m[0][3] = ((_animatedParameters &   0x8) ? _m14ptr[offset] : *_m14ptr);
105 	_matrix._m[1][0] = ((_animatedParameters &  0x10) ? _m21ptr[offset] : *_m21ptr);
106 	_matrix._m[1][1] = ((_animatedParameters &  0x20) ? _m22ptr[offset] : *_m22ptr);
107 	_matrix._m[1][2] = ((_animatedParameters &  0x40) ? _m23ptr[offset] : *_m23ptr);
108 	_matrix._m[1][3] = ((_animatedParameters &  0x80) ? _m24ptr[offset] : *_m24ptr);
109 	_matrix._m[2][0] = ((_animatedParameters & 0x100) ? _m31ptr[offset] : *_m31ptr);
110 	_matrix._m[2][1] = ((_animatedParameters & 0x200) ? _m32ptr[offset] : *_m32ptr);
111 	_matrix._m[2][2] = ((_animatedParameters & 0x400) ? _m33ptr[offset] : *_m33ptr);
112 	_matrix._m[2][3] = ((_animatedParameters & 0x800) ? _m34ptr[offset] : *_m34ptr);
113 	_inverted = invertMatrix(_matrix);
114 }
115 
read(Common::ReadStream * stream,int frameCount)116 void FogSphere::read(Common::ReadStream *stream, int frameCount) {
117 	_frameCount = frameCount;
118 	int size = readCommon(stream);
119 	_radius = stream->readFloatLE();
120 	readAnimationData(stream, size - 52);
121 }
122 
calculateCoeficient(Vector3 position,Vector3 viewPosition,float * coeficient)123 void FogSphere::calculateCoeficient(Vector3 position, Vector3 viewPosition, float *coeficient) {
124 	*coeficient = 0.0f;
125 
126 	// Ray - sphere intersection, where sphere center is always at 0, 0, 0 as everything else tranformed by the fog matrix.
127 	// Quadratic formula can and was simplified becasue rayDirection is normalized and hence a = 1.
128 	// Explained on wikipedia https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection
129 
130 	// There is also alternative approach which will end-up with this formula where plane is created from ray origin, ray destination
131 	// and sphere center, then there is only need to solve two right triangles.
132 	// Explained in book Andrew S. Glassner (1995), Graphics Gems I (p. 388-389)
133 
134 	Vector3 rayOrigin = _matrix * position;
135 	Vector3 rayDestination = _matrix * viewPosition;
136 	Vector3 rayDirection = (rayDestination - rayOrigin).normalize();
137 
138 	float b = Vector3::dot(rayDirection, rayOrigin);
139 	float c = Vector3::dot(rayOrigin, rayOrigin) - (_radius * _radius);
140 	float d = b * b - c;
141 
142 	if (d >= 0.0f) { // there is an interstection between ray and the sphere
143 		Vector3 intersection1 = rayOrigin + (-b - sqrt(d)) * rayDirection;
144 		Vector3 intersection2 = rayOrigin + (-b + sqrt(d)) * rayDirection;
145 
146 		Vector3 intersection1World = _inverted * intersection1;
147 		Vector3 intersection2World = _inverted * intersection2;
148 
149 		float intersection1Distance = (intersection1World - position).length();
150 		float intersection2Distance = (intersection2World - position).length();
151 
152 		float distance = (viewPosition - position).length();
153 
154 		if (intersection1Distance < 0.0f) {
155 			intersection1Distance = 0.0f;
156 		}
157 		if (intersection2Distance > distance) {
158 			intersection2Distance = distance;
159 		}
160 		if (intersection2Distance >= intersection1Distance) {
161 			*coeficient = intersection2Distance - intersection1Distance;
162 		}
163 	}
164 }
165 
read(Common::ReadStream * stream,int frameCount)166 void FogCone::read(Common::ReadStream *stream, int frameCount) {
167 	_frameCount = frameCount;
168 	int size = readCommon(stream);
169 	_coneAngle = stream->readFloatLE();
170 	readAnimationData(stream, size - 52);
171 }
172 
calculateCoeficient(Vector3 position,Vector3 viewPosition,float * coeficient)173 void FogCone::calculateCoeficient(Vector3 position, Vector3 viewPosition, float *coeficient) {
174 	*coeficient = 0.0f;
175 
176 	// ray - cone intersection, cone vertex V lies at (0,0,0) and direction v = (0,0,-1)
177 	// The algorithm looks like from book Alan W. Paeth (1995), Graphics Gems V (p. 228-230)
178 
179 	Vector3 positionT = _matrix * position;
180 	Vector3 viewPositionT = _matrix * viewPosition;
181 
182 	Vector3 v(0.0f, 0.0f, -1.0f);
183 
184 	Vector3 planeNormal = Vector3::cross(positionT, viewPositionT).normalize();
185 
186 	if (planeNormal.x != 0.0f || planeNormal.y != 0.0f || planeNormal.z != 0.0f) {
187 
188 		if (planeNormal.z < 0.0f) {
189 			planeNormal = -1.0f * planeNormal;
190 		}
191 
192 		float cosTheta = sqrt(1.0f - Vector3::dot(planeNormal, v) * Vector3::dot(planeNormal, v));
193 
194 		if (cosTheta > cos(_coneAngle)) {
195 			Vector3 u = Vector3::cross(v, planeNormal).normalize();
196 			Vector3 w = Vector3::cross(u, v).normalize();
197 
198 			float tanTheta = sqrt(1.0f - cosTheta * cosTheta) / cosTheta;
199 
200 			Vector3 temp1 = tanTheta * w;
201 			Vector3 temp2 = sqrt(tan(_coneAngle) * tan(_coneAngle) - tanTheta * tanTheta) * u;
202 
203 			Vector3 delta1 = v + temp1 - temp2;
204 			Vector3 delta2 = v + temp1 + temp2;
205 
206 			Vector3 d = viewPositionT - positionT;
207 			Vector3 vecVD = -1.0f * positionT;
208 
209 			Vector3 crossddelta1 = Vector3::cross(d, delta1);
210 			Vector3 crossddelta2 = Vector3::cross(d, delta2);
211 
212 			float r1 = Vector3::dot(Vector3::cross(vecVD, delta1), crossddelta1) / Vector3::dot(crossddelta1, crossddelta1);
213 			float r2 = Vector3::dot(Vector3::cross(vecVD, delta2), crossddelta2) / Vector3::dot(crossddelta2, crossddelta2);
214 
215 			if (r2 < r1) {
216 				float temp = r1;
217 				r1 = r2;
218 				r2 = temp;
219 			}
220 
221 			if (r1 <= 1.0f && r2 >= 0.0f) {
222 				if (r1 < 0.0f) {
223 					r1 = 0.0;
224 				}
225 				if (r2 > 1.0f) {
226 					r2 = 1.0;
227 				}
228 
229 				Vector3 intersection1 = positionT + (r1 * d);
230 				Vector3 intersection1World = _inverted * intersection1;
231 
232 				Vector3 intersection2 = positionT + (r2 * d);
233 				Vector3 intersection2World = _inverted * intersection2;
234 
235 				*coeficient = (intersection2World - intersection1World).length();
236 			}
237 		}
238 	}
239 }
240 
read(Common::ReadStream * stream,int frameCount)241 void FogBox::read(Common::ReadStream *stream, int frameCount) {
242 	_frameCount = frameCount;
243 	int size = readCommon(stream);
244 	_size.x = stream->readFloatLE();
245 	_size.y = stream->readFloatLE();
246 	_size.z = stream->readFloatLE();
247 	readAnimationData(stream, size - 60);
248 }
249 
calculateCoeficient(Vector3 position,Vector3 viewPosition,float * coeficient)250 void FogBox::calculateCoeficient(Vector3 position, Vector3 viewPosition, float *coeficient) {
251 	*coeficient = 0.0f;
252 
253 	// line - box intersection, where everything is rotated to box orientation by the fog matrix
254 
255 	Vector3 point1 = _matrix * position;
256 	Vector3 point2 = _matrix * viewPosition;
257 
258 	Vector3 intersection1 = point1;
259 	Vector3 intersection2 = point2;
260 
261 	Vector3 direction = point2 - point1;
262 
263 	// clip X
264 	float minX = -(_size.x * 0.5f);
265 	if (point1.x < minX) {
266 		if (point2.x < minX) {
267 			return;
268 		}
269 		float scale = (minX - point1.x) / direction.x;
270 		intersection1 = point1 + scale * direction;
271 	} else if (point2.x < minX) {
272 		float scale = (minX - point2.x) / direction.x;
273 		intersection2 = point2 + scale * direction;
274 	}
275 
276 	float maxX = _size.x * 0.5f;
277 	if (intersection1.x > maxX ) {
278 		if (intersection2.x > maxX) {
279 			return;
280 		}
281 		float scale = (maxX - intersection1.x) / direction.x;
282 		intersection1 = intersection1 + scale * direction;
283 	} else if (intersection2.x > maxX) {
284 		float scale = (maxX - intersection2.x) / direction.x;
285 		intersection2 = intersection2 + scale * direction;
286 	}
287 
288 	// clip Y
289 	float minY = -(_size.y * 0.5f);
290 	if (intersection1.y < minY) {
291 		if (intersection2.y < minY) {
292 			return;
293 		}
294 		float scale = (minY - intersection1.y) / direction.y;
295 		intersection1 = intersection1 + scale * direction;
296 	} else if (intersection2.y < minY) {
297 		float scale = (minY - intersection2.y) / direction.y;
298 		intersection2 = intersection2 + scale * direction;
299 	}
300 
301 	float maxY = _size.y * 0.5f;
302 	if (intersection1.y > maxY) {
303 		if (intersection2.y > maxY) {
304 			return;
305 		}
306 		float scale = (maxY - intersection1.y) / direction.y;
307 		intersection1 = intersection1 + scale * direction;
308 	} else if (intersection2.y > maxY) {
309 		float scale = (maxY - intersection2.y) / direction.y;
310 		intersection2 = intersection2 + scale * direction;
311 	}
312 
313 	// clip Z
314 	if (intersection1.z < 0.0f) {
315 		if (intersection2.z < 0.0f) {
316 			return;
317 		}
318 		float scale = -intersection1.z / direction.z;
319 		intersection1 = intersection1 + scale * direction;
320 	} else if (intersection2.z < 0.0f) {
321 		float scale = -intersection2.z / direction.z;
322 		intersection2 = intersection2 + scale * direction;
323 	}
324 
325 	if (intersection1.z > _size.z) {
326 		if (intersection2.z > _size.z) {
327 			return;
328 		}
329 		float scale = (_size.z - intersection1.z) / direction.z;
330 		intersection1 = intersection1 + scale * direction;
331 	} else if (intersection2.z > _size.z) {
332 		float scale = (_size.z - intersection2.z) / direction.z;
333 		intersection2 = intersection2 + scale * direction;
334 	}
335 
336 	Vector3 intersection1World = _inverted * intersection1;
337 	Vector3 intersection2World = _inverted * intersection2;
338 
339 	*coeficient = (intersection2World - intersection1World).length();
340 }
341 } // End of namespace BladeRunner
342