00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <assert.h>
00021 #include <algorithm>
00022 #include "config.h"
00023
00024 #ifdef HAVE_LIBZ
00025 # include <zlib.h>
00026 #endif
00027
00028 #include "math.hxx"
00029 #include "construo_error.hxx"
00030 #include "world.hxx"
00031 #include "particle_factory.hxx"
00032 #include "system_context.hxx"
00033 #include "controller.hxx"
00034 #include "rect.hxx"
00035 #include "rect_collider.hxx"
00036 #include "string_utils.hxx"
00037
00038 World* World::current_world = 0;
00039
00040 World::World ()
00041 : particle_mgr (new ParticleFactory(this))
00042 {
00043 file_version = 0;
00044 has_been_run = false;
00045 }
00046
00047 World::World (const std::string& filename)
00048 : particle_mgr (0)
00049 {
00050 std::cout << "World: Trying to load: " << filename << std::endl;
00051 file_version = 0;
00052
00053 has_been_run = false;
00054 lisp_object_t* root_obj = 0;
00055
00056
00057 if (StringUtils::has_suffix(filename, ".construo.gz"))
00058 {
00059 #ifdef HAVE_LIBZ
00060 lisp_stream_t stream;
00061 int chunk_size = 128 * 1024;
00062 char* buf;
00063 int buf_pos = 0;
00064 int try_number = 1;
00065 bool done = false;
00066
00067 buf = static_cast<char*>(malloc(chunk_size));
00068 if (!buf)
00069 {
00070 throw ConstruoError ("World: Out of memory while opening " + filename);
00071 }
00072
00073 gzFile in = gzopen(system_context->translate_filename(filename).c_str (), "rb");
00074
00075 while (!done)
00076 {
00077 int ret = gzread(in, buf + buf_pos, chunk_size);
00078 if (ret == -1)
00079 {
00080 free (buf);
00081 throw ConstruoError ("World: Out of memory while opening " + filename);
00082 }
00083 else if (ret == chunk_size)
00084 {
00085 std::cout << "World: Read buffer to small, allocating more space" << std::endl;
00086
00087 buf_pos = chunk_size * try_number;
00088 try_number += 1;
00089 buf = static_cast<char*>(realloc(buf, chunk_size * try_number));
00090
00091 if (!buf)
00092 {
00093 throw ConstruoError ("World: Out of memory while opening " + filename);
00094 }
00095 }
00096 else
00097 {
00098
00099 done = true;
00100 }
00101 }
00102
00103 lisp_stream_init_string (&stream, buf);
00104 root_obj = lisp_read (&stream);
00105
00106 free(buf);
00107 gzclose(in);
00108 #else
00109 throw ConstruoError ("World: Reading of compressed files not supported, recompile with zlib support or extract the levelfile manually, " + filename);
00110 #endif
00111 }
00112 else
00113 {
00114 lisp_stream_t stream;
00115 FILE* in = system_context->open_input_file(filename);
00116 if (!in)
00117 {
00118 throw ConstruoError ("World: Couldn't open " + filename);
00119 return;
00120 }
00121 lisp_stream_init_file (&stream, in);
00122 root_obj = lisp_read (&stream);
00123 }
00124
00125 if (root_obj->type == LISP_TYPE_EOF || root_obj->type == LISP_TYPE_PARSE_ERROR)
00126 {
00127 std::cout << "World: Parse Error in file " << filename << std::endl;
00128 }
00129
00130 lisp_object_t* cur = lisp_car(root_obj);
00131
00132 if (!lisp_symbol_p (cur))
00133 {
00134 throw ConstruoError ("World: Read error in " + filename);
00135 }
00136
00137 if (strcmp(lisp_symbol(cur), "construo-scene") == 0)
00138 {
00139 parse_scene (lisp_cdr(root_obj));
00140 }
00141 else
00142 {
00143 throw ConstruoError ("World: Read error in " + filename + ". Couldn't find 'construo-scene'");
00144 }
00145
00146 lisp_free (root_obj);
00147
00148 ConstruoAssert(particle_mgr, "No Particles given in file, load failed");
00149
00150
00151
00152 }
00153
00154 void
00155 World::parse_scene (lisp_object_t* cursor)
00156 {
00157 while(!lisp_nil_p(cursor))
00158 {
00159 lisp_object_t* cur = lisp_car(cursor);
00160
00161 if (!lisp_cons_p(cur) || !lisp_symbol_p (lisp_car(cur)))
00162 {
00163 throw ConstruoError ("World: Read error in parse_scene");
00164 }
00165 else
00166 {
00167 if (strcmp(lisp_symbol(lisp_car(cur)), "particles") == 0)
00168 {
00169 parse_particles(lisp_cdr(cur));
00170 }
00171 else if (strcmp(lisp_symbol(lisp_car(cur)), "springs") == 0)
00172 {
00173 parse_springs(lisp_cdr(cur));
00174 }
00175 else if (strcmp(lisp_symbol(lisp_car(cur)), "colliders") == 0)
00176 {
00177 parse_colliders(lisp_cdr(cur));
00178 }
00179 else if (strcmp(lisp_symbol(lisp_car(cur)), "version") == 0)
00180 {
00181 file_version = lisp_integer(lisp_car(lisp_cdr(cur)));
00182 }
00183 else
00184 {
00185 std::cout << "World: Read error in parse_scene. Unhandled tag '"
00186 << lisp_symbol(lisp_car(cur)) << "' skipping and continuing" << std::endl;
00187 }
00188 }
00189 cursor = lisp_cdr (cursor);
00190 }
00191 }
00192
00193 void
00194 World::parse_springs (lisp_object_t* cursor)
00195 {
00196 while(!lisp_nil_p(cursor))
00197 {
00198 lisp_object_t* cur = lisp_car(cursor);
00199 springs.push_back(new Spring (this, cur));
00200 cursor = lisp_cdr (cursor);
00201 }
00202 }
00203
00204 void
00205 World::parse_colliders (lisp_object_t* cursor)
00206 {
00207 while(!lisp_nil_p(cursor))
00208 {
00209 lisp_object_t* cur = lisp_car(cursor);
00210 if (strcmp(lisp_symbol(lisp_car(cur)), "rect") == 0)
00211 {
00212 colliders.push_back(new RectCollider(lisp_cdr(cur)));
00213 }
00214 else
00215 {
00216 std::cout << "WARNING: Unknown collider type '" << lisp_symbol(lisp_car(cur))
00217 << "' skipping" << std::endl;
00218 }
00219 cursor = lisp_cdr (cursor);
00220 }
00221 }
00222
00223 void
00224 World::parse_particles (lisp_object_t* cursor)
00225 {
00226 particle_mgr = new ParticleFactory(this, cursor);
00227 }
00228
00229
00230 World::World (const World& old_world)
00231 {
00232 file_version = 0;
00233
00234 for (Colliders::const_iterator i = old_world.colliders.begin();
00235 i != old_world.colliders.end();
00236 ++i)
00237 {
00238 colliders.push_back((*i)->duplicate());
00239 }
00240
00241
00242 particle_mgr = new ParticleFactory (this, *old_world.particle_mgr);
00243
00244 for (CSpringIter i = old_world.springs.begin (); i != old_world.springs.end (); ++i)
00245 {
00246 Particle* first = particle_mgr->lookup_particle((*i)->particles.first->get_id());
00247 Particle* second = particle_mgr->lookup_particle((*i)->particles.second->get_id());
00248
00249 if (first && second)
00250 {
00251
00252 springs.push_back (new Spring (first, second, (*i)->length));
00253 }
00254 else
00255 {
00256 std::cout << "World: Error couldn't resolve particles" << std::endl;
00257 }
00258 }
00259 }
00260
00261 World::~World ()
00262 {
00263 clear ();
00264 }
00265
00266 void
00267 World::draw (ZoomGraphicContext* gc)
00268 {
00269 current_world = this;
00270
00271 draw_colliders(gc);
00272 draw_springs(gc);
00273 draw_particles(gc);
00274 }
00275
00276 void
00277 World::draw_springs(ZoomGraphicContext* gc)
00278 {
00279 #ifdef NEW_SPRING_CODE
00280 std::vector<GraphicContext::Line> lines (springs.size());
00281
00282 Vector2d dist = springs[0]->particles.first->pos - springs[0]->particles.second->pos;
00283 float stretch = fabs(dist.norm ()/springs[0]->length - 1.0f) * 10.0f;
00284 float color = fabs((stretch/springs[0]->max_stretch));
00285
00286 for (unsigned int i = 0; i < springs.size(); ++i)
00287 {
00288
00289 lines[i].x1 = springs[i]->particles.first->pos.x;
00290 lines[i].y1 = springs[i]->particles.first->pos.y;
00291 lines[i].x2 = springs[i]->particles.second->pos.x;
00292 lines[i].y2 = springs[i]->particles.second->pos.y;
00293 }
00294 gc->draw_lines (lines, Color(color, 1.0f - color, 0.0f), 2);
00295 #else
00296 for (SpringIter i = springs.begin(); i != springs.end(); ++i)
00297 {
00298 (*i)->draw (gc);
00299 }
00300 #endif
00301 }
00302
00303 void
00304 World::draw_particles(ZoomGraphicContext* gc)
00305 {
00306 particle_mgr->draw(gc);
00307 }
00308
00309 void
00310 World::draw_colliders(ZoomGraphicContext* gc)
00311 {
00312 for (Colliders::iterator i = colliders.begin (); i != colliders.end (); ++i)
00313 {
00314 (*i)->draw(gc);
00315 }
00316 }
00317
00318 void
00319 World::update (float delta)
00320 {
00321 current_world = this;
00322
00323 has_been_run = true;
00324
00325
00326
00327 for (ParticleFactory::ParticleIter i = particle_mgr->begin (); i != particle_mgr->end (); ++i)
00328 {
00329
00330 (*i)->add_force (Vector2d (0.0, 15.0f) * (*i)->get_mass ());
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345 }
00346
00347 for (SpringIter i = springs.begin (); i != springs.end (); ++i)
00348 (*i)->update (delta);
00349
00350 particle_mgr->update(delta);
00351
00352
00353 for (Colliders::iterator i = colliders.begin (); i != colliders.end (); ++i)
00354 (*i)->bounce ();
00355
00356
00357 std::vector<Spring*> new_springs;
00358 for (SpringIter i = springs.begin (); i != springs.end (); ++i)
00359 {
00360 if ((*i)->destroyed)
00361 {
00362 if ((*i)->length > 20.0f)
00363 {
00364
00365 Vector2d pos = ((*i)->particles.first->pos
00366 + (*i)->particles.second->pos) * 0.5f;
00367
00368
00369 Particle* p1 = particle_mgr->add_particle (pos, (*i)->particles.first->velocity * 0.5f, .1f);
00370 Particle* p2 = particle_mgr->add_particle (pos, (*i)->particles.second->velocity * 0.5f, .1f);
00371
00372
00373 new_springs.push_back (new Spring ((*i)->particles.first, p1, (*i)->length/2));
00374 new_springs.push_back (new Spring ((*i)->particles.second, p2, (*i)->length/2));
00375 }
00376 }
00377 }
00378 springs.insert(springs.end(), new_springs.begin(), new_springs.end ());
00379
00380
00381
00382 for (SpringIter i = springs.begin (); i != springs.end ();)
00383 {
00384 if ((*i)->destroyed)
00385 {
00386 delete *i;
00387 i = springs.erase(i);
00388 }
00389 else
00390 {
00391 ++i;
00392 }
00393 }
00394 }
00395
00396 Spring*
00397 World::get_spring (float x, float y)
00398 {
00399 Spring* spring = 0;
00400 float min_distance = 0.0f;
00401
00402 float capture_threshold = 15;
00403
00404 for (SpringIter i = springs.begin (); i != springs.end (); ++i)
00405 {
00406 float x0 = x;
00407 float y0 = y;
00408 float& x1 = (*i)->particles.first->pos.x;
00409 float& y1 = (*i)->particles.first->pos.y;
00410 float& x2 = (*i)->particles.second->pos.x;
00411 float& y2 = (*i)->particles.second->pos.y;
00412
00413
00414 float u = (((x0 - x1)*(x2-x1) + (y0 - y1)*(y2 - y1))
00415 / ((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)));
00416
00417 float distance = (fabs((x2 - x1)*(y1-y0) - (x1-x0)*(y2-y1))
00418 / sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)));
00419
00420 if (u >= 0 && u <= 1.0f
00421 && ((spring && min_distance > distance)
00422 || (!spring && distance <= capture_threshold)))
00423 {
00424 spring = *i;
00425 min_distance = distance;
00426 }
00427 }
00428
00429 return spring;
00430 }
00431
00432 Particle*
00433 World::get_particle (float x, float y)
00434 {
00435 Particle* particle = 0;
00436 float min_dist = 15;
00437 Vector2d mouse_pos (x, y);
00438
00439 for (ParticleFactory::ParticleIter i = particle_mgr->begin (); i != particle_mgr->end (); ++i)
00440 {
00441 Vector2d diff = mouse_pos - (*i)->pos;
00442 if (diff.norm () < min_dist)
00443 {
00444 min_dist = diff.norm ();
00445 particle = *i;
00446 }
00447 }
00448
00449 return particle;
00450 }
00451
00452 std::vector<Particle*>
00453 World::get_particles (float x1_, float y1_, float x2_, float y2_)
00454 {
00455 float x1 = Math::min(x1_, x2_);
00456 float x2 = Math::max(x1_, x2_);
00457 float y1 = Math::min(y1_, y2_);
00458 float y2 = Math::max(y1_, y2_);
00459
00460 std::vector<Particle*> caputred_particles;
00461 for (ParticleFactory::ParticleIter i = particle_mgr->begin (); i != particle_mgr->end (); ++i)
00462 {
00463 if ((*i)->pos.x >= x1 && (*i)->pos.x < x2
00464 && (*i)->pos.y >= y1 && (*i)->pos.y < y2)
00465 caputred_particles.push_back(*i);
00466 }
00467 return caputred_particles;
00468 }
00469
00470 void
00471 World::zero_out_velocity ()
00472 {
00473 std::cout << "Setting velocity to zero" << std::endl;
00474 for (ParticleFactory::ParticleIter i = get_particle_mgr()->begin();
00475 i != get_particle_mgr()->end (); ++i)
00476 {
00477 (*i)->velocity = Vector2d ();
00478 }
00479 }
00480
00481 void
00482 World::add_spring (Particle* last_particle, Particle* particle)
00483 {
00484 assert (last_particle && particle);
00485 springs.push_back (new Spring (last_particle, particle));
00486 }
00487
00488 void
00489 World::remove_particle (Particle* p)
00490 {
00491
00492 for (SpringIter i = springs.begin (); i != springs.end ();)
00493 {
00494 if ((*i)->particles.first == p || (*i)->particles.second == p)
00495 {
00496 delete *i;
00497
00498
00499 i = springs.erase(i);
00500 }
00501 else
00502 {
00503 ++i;
00504 }
00505 }
00506
00507 particle_mgr->remove_particle(p);
00508 }
00509
00510 void
00511 World::remove_spring (Spring* s)
00512 {
00513 std::cout << "particles: " << particle_mgr->size () << std::endl;
00514 std::cout << "springs: " << springs.size () << std::endl;
00515
00516 delete s;
00517 springs.erase(std::remove(springs.begin (), springs.end (), s),
00518 springs.end ());
00519 }
00520
00521 void
00522 World::remove_collider (Collider* c)
00523 {
00524 delete c;
00525 colliders.erase(std::remove(colliders.begin (), colliders.end (), c),
00526 colliders.end ());
00527 }
00528
00529 void
00530 World::clear ()
00531 {
00532 particle_mgr->clear();
00533
00534 for (SpringIter i = springs.begin (); i != springs.end (); ++i)
00535 delete *i;
00536
00537 springs.clear ();
00538 }
00539
00540 void
00541 World::write_lisp (const std::string& filename)
00542 {
00543 FILE* out;
00544
00545 out = system_context->open_output_file(filename);
00546
00547 if (!out)
00548 {
00549 std::cout << "World: Couldn't open '" << filename << "' for writing" << std::endl;
00550 return;
00551 }
00552
00553 std::cout << "Writing to: " << filename << std::endl;
00554
00555 fputs(";; Written by " PACKAGE_STRING "\n", out);
00556 fputs("(construo-scene\n", out);
00557 fputs(" (version 3)\n", out);
00558
00559
00560
00561 fprintf(out, " (author \"%s\" \"%s\")\n",
00562 system_context->get_user_realname().c_str(),
00563 system_context->get_user_email().c_str());
00564
00565 particle_mgr->write_lisp(out);
00566
00567
00568 fputs(" (springs\n", out);
00569 for (CSpringIter i = springs.begin (); i != springs.end (); ++i)
00570 {
00571 lisp_object_t* obj = (*i)->serialize ();
00572 fputs(" ", out);
00573 lisp_dump (obj, out);
00574 fputc('\n', out);
00575 lisp_free(obj);
00576 }
00577 fputs(" )\n", out);
00578
00579 fputs (" (colliders\n", out);
00580 for (Colliders::iterator i = colliders.begin(); i != colliders.end(); ++i)
00581 {
00582 lisp_object_t* obj = (*i)->serialize ();
00583 fputs(" ", out);
00584 lisp_dump (obj, out);
00585 fputc('\n', out);
00586 lisp_free(obj);
00587 }
00588 fputs(" )", out);
00589
00590
00591 fputs(")\n\n;; EOF ;;\n", out);
00592
00593 fclose(out);
00594
00595 if (StringUtils::has_suffix(filename, ".gz"))
00596 {
00597 std::cout << "World: Filename ends with .gz, rewriting " << filename << " compressed" << std::endl;
00598
00599 int len = 512*1024;
00600 int read_len;
00601 char* buf;
00602 buf = static_cast<char*>(malloc(len));
00603 if (!buf)
00604 {
00605 throw ConstruoError("Out of memory");
00606 }
00607 FILE* in = system_context->open_input_file(filename);
00608 read_len = fread (buf, sizeof (char), len, in);
00609 if (len >= read_len)
00610 {
00611 throw ConstruoError("World: Internal error, read buffer to small");
00612 }
00613 fclose (in);
00614
00615
00616 gzFile out = gzopen(system_context->translate_filename(filename).c_str(), "wb");
00617 gzwrite (out, buf, len);
00618 gzclose (out);
00619 free (buf);
00620 }
00621 }
00622
00623 WorldBoundingBox
00624 World::calc_bounding_box()
00625 {
00626 WorldBoundingBox bbox;
00627
00628 if (particle_mgr->size() > 0)
00629 {
00630 bbox.x1 = bbox.x2 = (*particle_mgr->begin ())->pos.x;
00631 bbox.y1 = bbox.y2 = (*particle_mgr->begin ())->pos.y;
00632 }
00633 else
00634 {
00635 bbox.x1 = 0;
00636 bbox.y1 = 0;
00637
00638 bbox.x2 = 800;
00639 bbox.y2 = 600;
00640 }
00641
00642 for (ParticleFactory::ParticleIter i = particle_mgr->begin (); i != particle_mgr->end (); ++i)
00643 {
00644 bbox.x1 = Math::min(bbox.x1, (*i)->pos.x);
00645 bbox.y1 = Math::min(bbox.y1, (*i)->pos.y);
00646
00647 bbox.x2 = Math::max(bbox.x2, (*i)->pos.x);
00648 bbox.y2 = Math::max(bbox.y2, (*i)->pos.y);
00649 }
00650
00651 return bbox;
00652 }
00653
00654 int
00655 World::get_num_particles()
00656 {
00657 return particle_mgr->size ();
00658 }
00659
00660 int
00661 World::get_num_springs()
00662 {
00663 return springs.size ();
00664 }
00665
00666 void
00667 World::add_rect_collider(const Vector2d& pos1, const Vector2d& pos2)
00668 {
00669 Rect<float> rect (pos1.x, pos1.y, pos2.x, pos2.y);
00670
00671 colliders.push_back(new RectCollider(rect.x1, rect.y1, rect.x2, rect.y2));
00672 }
00673
00674