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