1 // I N C L U D E S ////////////////////////////////////////////////////////////
2 
3 #include "eye_candy.h"
4 #include "math_cache.h"
5 
6 #include "effect_cloud.h"
7 
8 namespace ec
9 {
10 
11 	// C L A S S   F U N C T I O N S //////////////////////////////////////////////
12 
CloudParticle(Effect * _effect,ParticleMover * _mover,const Vec3 _pos,const Vec3 _velocity,const color_t hue_adjust,const color_t saturation_adjust,const coord_t _min_height,const coord_t _max_height,const coord_t _size,const alpha_t _alpha)13 	CloudParticle::CloudParticle(Effect* _effect, ParticleMover* _mover,
14 		const Vec3 _pos, const Vec3 _velocity, const color_t hue_adjust,
15 		const color_t saturation_adjust, const coord_t _min_height,
16 		const coord_t _max_height, const coord_t _size, const alpha_t _alpha) :
17 		Particle(_effect, _mover, _pos, _velocity, _size)
18 	{
19 		color_t hue, saturation, value;
20 		hue = 0.67;
21 		saturation = 0.05;
22 		value = 1.0;
23 		hue += hue_adjust;
24 		if (hue > 1.0)
25 			hue -= 1.0;
26 		saturation = std::min(1.0f, saturation * saturation_adjust);
27 		hsv_to_rgb(hue, saturation, value, color[0], color[1], color[2]);
28 		alpha = _alpha;
29 		flare_max = 2.0;
30 		flare_exp = 0.7;
31 		flare_frequency = 30.0;
32 		min_height = _min_height;
33 		max_height = _max_height;
34 		normal = Vec3(0.0, 1.0, 0.0);
35 	}
36 
idle(const Uint64 delta_t)37 	bool CloudParticle::idle(const Uint64 delta_t)
38 	{
39 		if (effect->recall)
40 			return false;
41 
42 		if (base->particles.size() > 1)
43 		{
44 			if ((pos - base->center).magnitude_squared()
45 				> MAX_DRAW_DISTANCE_SQUARED)
46 			{
47 				//Find everything that sees this as a neighbor and delete the references to this.
48 				for (auto incoming: incoming_neighbors)
49 					incoming->remove_neighbor(this);
50 				for (auto neighbor: neighbors)
51 					neighbor->remove_incoming_neighbor(this);
52 				return false;
53 			}
54 		}
55 
56 		Vec3 velocity_shift;
57 		velocity_shift.randomize();
58 		velocity_shift.y /= 3;
59 		velocity_shift.normalize(0.00002 * std::sqrt(delta_t));
60 		velocity += velocity_shift;
61 		const coord_t magnitude = velocity.magnitude();
62 		if (magnitude > 0.15)
63 			velocity /= (magnitude / 0.15);
64 
65 		if (fabs(velocity.y) > 0.1)
66 			velocity.y *= std::pow(0.5f, delta_t / 300000.0f);
67 
68 		if (pos.y - size / 40 < min_height)
69 			velocity.y += delta_t / 500000.0;
70 
71 		if (pos.y + size / 40 > max_height)
72 			velocity.y -= delta_t / 2500000.0;
73 
74 		if (effect->particles.size() <= 1)
75 			return true;
76 
77 		if (!neighbors.size())
78 		{
79 			std::map<Particle*, bool>::iterator next_iter;
80 			int offset;
81 			CloudParticle* next;
82 			while (true)
83 			{
84 				next_iter = effect->particles.begin();
85 				offset = randint((int)effect->particles.size());
86 				for (int j = 0; j < offset; j++)
87 					++next_iter;
88 				next = (CloudParticle*)next_iter->first;
89 				if (next != this)
90 					break;
91 			}
92 			neighbors.push_back(next);
93 			next->add_incoming_neighbor(this);
94 		}
95 
96 		// Adjust our neighbors -- try a few points to see if they're closer.
97 		// First, create the map
98 
99 		CloudEffect* eff = (CloudEffect*)effect;
100 		std::map<coord_t, CloudParticle*> neighbors_map;
101 		for (int i = 0; i < (int)neighbors.size(); i++)
102 		{
103 			const coord_t distsquared = (neighbors[i]->pos - pos).magnitude_squared();
104 			neighbors_map[distsquared] = neighbors[i];
105 		}
106 
107 		// Now, try to replace elements.
108 		coord_t maxdist = neighbors_map.rbegin()->first;
109 		for (int i = 0; (i < 1) || ((neighbors_map.size() < 20) && (i < 40)); i++)
110 		{
111 			std::map<Particle*, bool>::iterator iter;
112 			int offset;
113 			CloudParticle* neighbor;
114 			while (true)
115 			{
116 				iter = eff->particles.begin();
117 				offset = randint((int)eff->particles.size());
118 				for (int j = 0; j < offset; j++)
119 					++iter;
120 				neighbor = (CloudParticle*)iter->first;
121 				if (neighbor != this)
122 					break;
123 			}
124 			const coord_t distsquared = (neighbor->pos - pos).magnitude_squared();
125 			if (neighbors_map.size() >= 20)
126 			{
127 				if (distsquared > maxdist)
128 					continue;
129 				if (neighbors_map.count(distsquared))
130 					continue;
131 			}
132 			if (neighbors_map.size() >= 20)
133 			{
134 				std::map<coord_t, CloudParticle*>::iterator iter =
135 					neighbors_map.begin();
136 				for (int j = 0; j < (int)neighbors_map.size() - 1; j++)
137 					++iter;
138 				neighbors_map.erase(iter);
139 			}
140 			neighbors_map[distsquared] = neighbor;
141 			maxdist = neighbors_map.rbegin()->first;
142 		}
143 
144 		// Set our color based on how deep into the cloud we are, based on our neighbors.  Also rebuild the neighbors vector.
145 		coord_t distsquaredsum = 0;
146 		Vec3 centerpoint(0.0, 0.0, 0.0);
147 		for (auto neighbor: neighbors)
148 			neighbor->remove_incoming_neighbor(this);
149 		neighbors.clear();
150 
151 		for (const auto& iter: neighbors_map)
152 		{
153 			distsquaredsum += iter.first;
154 			centerpoint += iter.second->pos; //Should really be (pos - iter->second->pos); will correct this below for speed.
155 			neighbors.push_back(iter.second);
156 			iter.second->add_incoming_neighbor(this);
157 		}
158 		centerpoint = (pos * 20) - centerpoint;
159 		Vec3 new_normal = centerpoint;
160 		const coord_t magnitude_squared = centerpoint.magnitude_squared();
161 		const coord_t scale= std::sqrt(magnitude_squared);
162 		new_normal /= scale; // Normalize
163 		//  light_t new_brightness = 1.0 - (25.0 / (scale + 50.0));
164 		//  new_normal.x = (new_normal.x < 0 ? -1 : 1) * math_cache.powf_0_1_rough_close(fabs(new_normal.x), new_brightness * 2.0 - 1.0);
165 		//  new_normal.y = (new_normal.y < 0 ? -1 : 1) * math_cache.powf_0_1_rough_close(fabs(new_normal.y), new_brightness * 2.0 - 1.0);
166 		//  new_normal.z = (new_normal.z < 0 ? -1 : 1) * math_cache.powf_0_1_rough_close(fabs(new_normal.z), new_brightness * 2.0 - 1.0);
167 		const percent_t change_rate = std::pow(0.5f, delta_t
168 			/ 2000000.0f);
169 		normal = normal * change_rate + new_normal * (1.0 - change_rate);
170 		normal.normalize();
171 		//  color[0] = color[0] * change_rate + new_brightness * (1.0 - change_rate);
172 		//  color[1] = color[0];
173 		//  color[2] = color[0];
174 		//  std::cout << "  " << centerpoint << std::endl;
175 		//  std::cout << "  " << normal << std::endl;
176 		//  std::cout << "  " << brightness << std::endl;
177 
178 		return true;
179 	}
180 
get_texture()181 	Uint32 CloudParticle::get_texture()
182 	{
183 		return base->get_texture(EC_SIMPLE);
184 	}
185 
get_burn() const186 	float CloudParticle::get_burn() const
187 	{
188 		return 0.0f;
189 	}
190 
remove_neighbor(const CloudParticle * const p)191 	void CloudParticle::remove_neighbor(const CloudParticle*const p)
192 	{
193 		for (int i = 0; i < (int)neighbors.size();)
194 		{
195 			std::vector<CloudParticle*>::iterator iter = neighbors.begin() + i;
196 			if (*iter == p)
197 			{
198 				neighbors.erase(iter);
199 				continue;
200 			}
201 			i++;
202 		}
203 
204 		if (effect->particles.size() <= 2)
205 			return;
206 
207 		if (!neighbors.size())
208 		{
209 			std::map<Particle*, bool>::iterator next_iter;
210 			int offset;
211 			CloudParticle* next;
212 			while (true)
213 			{
214 				next_iter = effect->particles.begin();
215 				offset = randint((int)effect->particles.size());
216 				for (int j = 0; j < offset; j++)
217 					++next_iter;
218 				next = (CloudParticle*)next_iter->first;
219 				if ((next != this) && (next != p))
220 					break;
221 			}
222 			neighbors.push_back(next);
223 			next->add_incoming_neighbor(this);
224 		}
225 	}
226 
add_incoming_neighbor(CloudParticle * const p)227 	void CloudParticle::add_incoming_neighbor(CloudParticle*const p)
228 	{
229 		incoming_neighbors.push_back(p);
230 	}
231 
remove_incoming_neighbor(const CloudParticle * const p)232 	void CloudParticle::remove_incoming_neighbor(const CloudParticle*const p)
233 	{
234 		for (int i = 0; i < (int)incoming_neighbors.size();)
235 		{
236 			std::vector<CloudParticle*>::iterator iter =
237 				incoming_neighbors.begin() + i;
238 			if (*iter == p)
239 			{
240 				incoming_neighbors.erase(iter);
241 				continue;
242 			}
243 			i++;
244 		}
245 	}
246 
CloudEffect(EyeCandy * _base,bool * _dead,Vec3 * _pos,const color_t _hue_adjust,const color_t _saturation_adjust,const float _density,BoundingRange * bounding_range,const Uint16 _LOD)247 	CloudEffect::CloudEffect(EyeCandy* _base, bool* _dead, Vec3* _pos,
248 		const color_t _hue_adjust, const color_t _saturation_adjust,
249 		const float _density, BoundingRange* bounding_range, const Uint16 _LOD)
250 	{
251 		if (EC_DEBUG)
252 			std::cout << "CloudEffect (" << this << ") created (" << *_pos
253 				<< ", " << bounding_range->get_radius(0.0) << ")." << std::endl;
254 		base = _base;
255 		dead = _dead;
256 		pos = _pos;
257 		center = *pos;
258 		hue_adjust = _hue_adjust;
259 		saturation_adjust = _saturation_adjust;
260 		LOD = base->last_forced_LOD;
261 		desired_LOD = _LOD;
262 		bounds = bounding_range;
263 		mover = new BoundingMover(this, center, bounding_range, 1.0);
264 		spawner = new NoncheckingFilledBoundingSpawner(bounding_range);
265 		//  count = (int)(spawner->get_area() * 0.03 * (LOD  + 1));
266 		count = (int)(MAX_DRAW_DISTANCE_SQUARED * PI * 0.03 * (LOD + 1));
267 		if (count < 21)
268 			count = 21;
269 
270 		alpha = 0.1725 / (1.0 / _density + 0.15);
271 		size_scalar = 110.0 / std::sqrt(LOD + 1);
272 
273 		for (int i = 0; i < count; i++)
274 		{
275 			Vec3 coords = spawner->get_new_coords();
276 			if (coords.x == -32768.0)
277 				break;
278 			coords += center + Vec3(0.0, randcoord(5.0), 0.0);
279 			Vec3 velocity;
280 			velocity.randomize(0.15);
281 			velocity.y /= 3;
282 			const coord_t size = size_scalar + randcoord(size_scalar);
283 			Particle
284 				* p =
285 					new CloudParticle(this, mover, coords, velocity, hue_adjust, saturation_adjust, center.y, center.y + 20.0, size, alpha);
286 			if (!base->push_back_particle(p))
287 				break;
288 		}
289 
290 		// Load one neighbor for each one.  It'll get more on its own.
291 		for (std::map<Particle*, bool>::iterator iter = particles.begin(); iter != particles.end(); ++iter)
292 		{
293 			CloudParticle* p = (CloudParticle*)iter->first;
294 			CloudParticle* next;
295 			std::map<Particle*, bool>::iterator iter2 = iter;
296 			++iter2;
297 			if (iter2 != particles.end())
298 				next = (CloudParticle*)iter2->first;
299 			else
300 				next = (CloudParticle*)particles.begin()->first;
301 			p->neighbors.push_back(next);
302 			next->add_incoming_neighbor(p);
303 		}
304 	}
305 
~CloudEffect()306 	CloudEffect::~CloudEffect()
307 	{
308 		delete mover;
309 		delete spawner;
310 		if (EC_DEBUG)
311 			std::cout << "CloudEffect (" << this << ") destroyed." << std::endl;
312 	}
313 
idle(const Uint64 usec)314 	bool CloudEffect::idle(const Uint64 usec)
315 	{
316 		if (recall && (particles.size() == 0))
317 			return false;
318 
319 		if (recall)
320 			return true;
321 
322 		const int start_count = particles.size();
323 		if (start_count == count)
324 			return true;
325 
326 		if (particles.size())
327 		{
328 			CloudParticle* last = (CloudParticle*)(particles.rbegin()->first);
329 			for (int i = count - (int)particles.size(); i >= 0; i--)
330 			{
331 				Vec3 coords = spawner->get_new_coords();
332 				if (coords.x == -32768.0)
333 					continue;
334 				coords += center + Vec3(0.0, randcoord(5.0), 0.0);
335 				Vec3 velocity;
336 				velocity.randomize(0.15);
337 				velocity.y /= 3;
338 				const coord_t size = size_scalar + randcoord(size_scalar);
339 				CloudParticle
340 					* p =
341 						new CloudParticle(this, mover, coords, velocity, hue_adjust, saturation_adjust, center.y, center.y + 20.0, size, alpha);
342 				if (!base->push_back_particle(p))
343 					break;
344 				p->neighbors.push_back(last);
345 				last->add_incoming_neighbor(p);
346 			}
347 		}
348 		else
349 		{
350 			for (int i = count - (int)particles.size(); i >= 0; i--)
351 			{
352 				Vec3 coords = spawner->get_new_coords();
353 				if (coords.x == -32768.0)
354 					continue;
355 				coords += center + Vec3(0.0, randcoord(5.0), 0.0);
356 				Vec3 velocity;
357 				velocity.randomize(0.15);
358 				velocity.y /= 3;
359 				const coord_t size = size_scalar + randcoord(size_scalar);
360 				Particle
361 					* p =
362 						new CloudParticle(this, mover, coords, velocity, hue_adjust, saturation_adjust, center.y, center.y + 20.0, size, alpha);
363 				if (!base->push_back_particle(p))
364 					break;
365 			}
366 
367 			// Load one neighbor for each one.  It'll get more on its own.
368 			for (std::map<Particle*, bool>::iterator iter = particles.begin(); iter != particles.end(); ++iter)
369 			{
370 				CloudParticle* p = (CloudParticle*)iter->first;
371 				CloudParticle* next;
372 				std::map<Particle*, bool>::iterator iter2 = iter;
373 				++iter2;
374 				if (iter2 != particles.end())
375 					next = (CloudParticle*)iter2->first;
376 				else
377 					next = (CloudParticle*)particles.begin()->first;
378 				p->neighbors.push_back(next);
379 				next->add_incoming_neighbor(p);
380 			}
381 		}
382 
383 		return true;
384 	}
385 
386 ///////////////////////////////////////////////////////////////////////////////
387 
388 }
389 ;
390 
391