This machine mirrors various open-source projects.
20 Gbit/s uplink.
If there are any issues or you want another project mirrored, please contact
mirror-service -=AT=- netcologne DOT de !
00001 // $Id: world.cxx,v 1.31 2003/01/11 19:07:48 grumbel Exp $ 00002 // 00003 // Construo - A wire-frame construction game 00004 // Copyright (C) 2002 Ingo Ruhnke <grumbel@gmx.de> 00005 // 00006 // This program is free software; you can redistribute it and/or 00007 // modify it under the terms of the GNU General Public License 00008 // as published by the Free Software Foundation; either version 2 00009 // of the License, or (at your option) any later version. 00010 // 00011 // This program is distributed in the hope that it will be useful, 00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 // GNU General Public License for more details. 00015 // 00016 // You should have received a copy of the GNU General Public License 00017 // along with this program; if not, write to the Free Software 00018 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 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 // Try to read a file and store the content in root_obj 00057 if (StringUtils::has_suffix(filename, ".construo.gz")) 00058 { 00059 #ifdef HAVE_LIBZ 00060 lisp_stream_t stream; 00061 int chunk_size = 128 * 1024; // allocate 256kb, should be enough for most levels 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) // buffer got full, eof not yet there 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 // (ret < chunk_size) 00097 { 00098 // everything fine, encountered EOF 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 //std::cout << "particles: " << particle_mgr->size () << std::endl; 00151 //std::cout << "springs: " << springs.size () << std::endl; 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 // Copy Constructor 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 // FIXME: Could need optimizations 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 // FIXME: Use copy c'tor here maxstiffnes and Co. aren't copied correctly 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 //(*i)->draw (gc); 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 // Main Movement and Forces 00326 // FIXME: Hardcoded Force Emitters 00327 for (ParticleFactory::ParticleIter i = particle_mgr->begin (); i != particle_mgr->end (); ++i) 00328 { 00329 // Gravity 00330 (*i)->add_force (Vector2d (0.0, 15.0f) * (*i)->get_mass ()); 00331 00332 // Central Gravity force: 00333 /*Vector2d direction = ((*i)->pos - Vector2d (400, 300)); 00334 if (direction.norm () != 0.0f) 00335 (*i)->add_force (direction * (-100.0f/(direction.norm () * direction.norm ()))); 00336 */ 00337 00338 /* 00339 for (ParticleIter j = particles.begin (); j != particles.end (); ++j) 00340 { 00341 Vector2d diff = (*j)->pos - (*i)->pos; 00342 if (diff.norm () != 0.0f) 00343 (*i)->add_force (diff * ((10.0f - (*j)->mass)/(diff.norm () * diff.norm ()))); 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 //std::cout << "Colliders: " << colliders.size () << std::endl; 00353 for (Colliders::iterator i = colliders.begin (); i != colliders.end (); ++i) 00354 (*i)->bounce (); 00355 00356 // Spring splitting 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 // Calc midpoint 00365 Vector2d pos = ((*i)->particles.first->pos 00366 + (*i)->particles.second->pos) * 0.5f; 00367 00368 // FIXME: particle mass needs to be recalculated 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 // FIXME: Insert a more sofistikated string splitter here 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 // Remove any springs that are marked as destroyed 00381 // FIXME: Could be faster 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 // FIXME: optimize me 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))) // FIXME: threashold is dependend on view 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 // Remove everyting that references the particle 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 // FIXME: this is potentially slow, since we don't care 00498 // about order, we could speed this up 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 // FIXME: insert creation date here 00560 // FIXME: Filter '()"' here 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 { // Rewrite file compressed 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 // Write the buffer in compressed format 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 /* EOF */