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