1 // $Id: world.cxx,v 1.35 2003/07/28 20:57:41 grumbel Exp $
3 // Construo - A wire-frame construction game
4 // Copyright (C) 2002 Ingo Ruhnke <grumbel@gmx.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include "construo_error.hxx"
31 #include "particle_factory.hxx"
32 #include "system_context.hxx"
33 #include "controller.hxx"
35 #include "rect_collider.hxx"
36 #include "string_utils.hxx"
38 World
* World::current_world
= 0;
41 : particle_mgr (new ParticleFactory(this))
47 World::World (const std::string
& filename
)
50 std::cout
<< "World: Trying to load: " << filename
<< std::endl
;
54 lisp_object_t
* root_obj
= 0;
56 // Try to read a file and store the content in root_obj
57 if (StringUtils::has_suffix(filename
, ".construo.gz"))
61 int chunk_size
= 128 * 1024; // allocate 256kb, should be enough for most levels
67 buf
= static_cast<char*>(malloc(chunk_size
));
70 throw ConstruoError ("World: Out of memory while opening " + filename
);
73 gzFile in
= gzopen(system_context
->translate_filename(filename
).c_str (), "rb");
77 int ret
= gzread(in
, buf
+ buf_pos
, chunk_size
);
81 throw ConstruoError ("World: Out of memory while opening " + filename
);
83 else if (ret
== chunk_size
) // buffer got full, eof not yet there
85 std::cout
<< "World: Read buffer to small, allocating more space" << std::endl
;
87 buf_pos
= chunk_size
* try_number
;
89 buf
= static_cast<char*>(realloc(buf
, chunk_size
* try_number
));
93 throw ConstruoError ("World: Out of memory while opening " + filename
);
96 else // (ret < chunk_size)
98 // everything fine, encountered EOF
103 lisp_stream_init_string (&stream
, buf
);
104 root_obj
= lisp_read (&stream
);
109 throw ConstruoError ("World: Reading of compressed files not supported, recompile with zlib support or extract the levelfile manually, " + filename
);
114 lisp_stream_t stream
;
115 FILE* in
= system_context
->open_input_file(filename
);
118 throw ConstruoError ("World: Couldn't open " + filename
);
121 lisp_stream_init_file (&stream
, in
);
122 root_obj
= lisp_read (&stream
);
125 if (root_obj
->type
== LISP_TYPE_EOF
|| root_obj
->type
== LISP_TYPE_PARSE_ERROR
)
127 std::cout
<< "World: Parse Error in file " << filename
<< std::endl
;
130 lisp_object_t
* cur
= lisp_car(root_obj
);
132 if (!lisp_symbol_p (cur
))
134 throw ConstruoError ("World: Read error in " + filename
);
137 if (strcmp(lisp_symbol(cur
), "construo-scene") == 0)
139 parse_scene (lisp_cdr(root_obj
));
143 throw ConstruoError ("World: Read error in " + filename
+ ". Couldn't find 'construo-scene'");
146 lisp_free (root_obj
);
148 ConstruoAssert(particle_mgr
, "No Particles given in file, load failed");
150 //std::cout << "particles: " << particle_mgr->size () << std::endl;
151 //std::cout << "springs: " << springs.size () << std::endl;
155 World::parse_scene (lisp_object_t
* cursor
)
157 while(!lisp_nil_p(cursor
))
159 lisp_object_t
* cur
= lisp_car(cursor
);
161 if (!lisp_cons_p(cur
) || !lisp_symbol_p (lisp_car(cur
)))
163 throw ConstruoError ("World: Read error in parse_scene");
167 if (strcmp(lisp_symbol(lisp_car(cur
)), "particles") == 0)
169 parse_particles(lisp_cdr(cur
));
171 else if (strcmp(lisp_symbol(lisp_car(cur
)), "springs") == 0)
173 parse_springs(lisp_cdr(cur
));
175 else if (strcmp(lisp_symbol(lisp_car(cur
)), "colliders") == 0)
177 parse_colliders(lisp_cdr(cur
));
179 else if (strcmp(lisp_symbol(lisp_car(cur
)), "version") == 0)
181 file_version
= lisp_integer(lisp_car(lisp_cdr(cur
)));
185 std::cout
<< "World: Read error in parse_scene. Unhandled tag '"
186 << lisp_symbol(lisp_car(cur
)) << "' skipping and continuing" << std::endl
;
189 cursor
= lisp_cdr (cursor
);
194 World::parse_springs (lisp_object_t
* cursor
)
196 while(!lisp_nil_p(cursor
))
198 lisp_object_t
* cur
= lisp_car(cursor
);
199 springs
.push_back(new Spring (this, cur
));
200 cursor
= lisp_cdr (cursor
);
205 World::parse_colliders (lisp_object_t
* cursor
)
207 while(!lisp_nil_p(cursor
))
209 lisp_object_t
* cur
= lisp_car(cursor
);
210 if (strcmp(lisp_symbol(lisp_car(cur
)), "rect") == 0)
212 colliders
.push_back(new RectCollider(lisp_cdr(cur
)));
216 std::cout
<< "WARNING: Unknown collider type '" << lisp_symbol(lisp_car(cur
))
217 << "' skipping" << std::endl
;
219 cursor
= lisp_cdr (cursor
);
224 World::parse_particles (lisp_object_t
* cursor
)
226 particle_mgr
= new ParticleFactory(this, cursor
);
230 World::World (const World
& old_world
)
234 for (Colliders::const_iterator i
= old_world
.colliders
.begin();
235 i
!= old_world
.colliders
.end();
238 colliders
.push_back((*i
)->duplicate());
241 // FIXME: Could need optimizations
242 particle_mgr
= new ParticleFactory (this, *old_world
.particle_mgr
);
244 for (CSpringIter i
= old_world
.springs
.begin (); i
!= old_world
.springs
.end (); ++i
)
246 Particle
* first
= particle_mgr
->lookup_particle((*i
)->particles
.first
->get_id());
247 Particle
* second
= particle_mgr
->lookup_particle((*i
)->particles
.second
->get_id());
251 // FIXME: Use copy c'tor here maxstiffnes and Co. aren't copied correctly
252 springs
.push_back (new Spring (first
, second
, (*i
)->length
));
256 std::cout
<< "World: Error couldn't resolve particles" << std::endl
;
267 World::draw (ZoomGraphicContext
* gc
)
269 // FIXME: This is *not* used in the WorldViewComponent!
271 current_world
= this;
279 World::draw_springs(ZoomGraphicContext
* gc
)
281 #ifdef NEW_SPRING_CODE
282 std::vector
<GraphicContext::Line
> lines (springs
.size());
284 Vector2d dist
= springs
[0]->particles
.first
->pos
- springs
[0]->particles
.second
->pos
;
285 float stretch
= fabs(dist
.norm ()/springs
[0]->length
- 1.0f
) * 10.0f
;
286 float color
= fabs((stretch
/springs
[0]->max_stretch
));
288 for (unsigned int i
= 0; i
< springs
.size(); ++i
)
291 lines
[i
].x1
= springs
[i
]->particles
.first
->pos
.x
;
292 lines
[i
].y1
= springs
[i
]->particles
.first
->pos
.y
;
293 lines
[i
].x2
= springs
[i
]->particles
.second
->pos
.x
;
294 lines
[i
].y2
= springs
[i
]->particles
.second
->pos
.y
;
296 gc
->draw_lines (lines
, Color(color
, 1.0f
- color
, 0.0f
), 2);
298 for (SpringIter i
= springs
.begin(); i
!= springs
.end(); ++i
)
306 World::draw_particles(ZoomGraphicContext
* gc
)
308 particle_mgr
->draw(gc
);
312 World::draw_colliders(ZoomGraphicContext
* gc
)
314 for (Colliders::iterator i
= colliders
.begin (); i
!= colliders
.end (); ++i
)
321 World::update (float delta
)
323 current_world
= this;
327 // Main Movement and Forces
328 // FIXME: Hardcoded Force Emitters
329 for (ParticleFactory::ParticleIter i
= particle_mgr
->begin (); i
!= particle_mgr
->end (); ++i
)
332 (*i
)->add_force (Vector2d (0.0, 15.0f
) * (*i
)->get_mass ());
334 // Central Gravity force:
335 /*Vector2d direction = ((*i)->pos - Vector2d (400, 300));
336 if (direction.norm () != 0.0f)
337 (*i)->add_force (direction * (-100.0f/(direction.norm () * direction.norm ())));
341 for (ParticleIter j = particles.begin (); j != particles.end (); ++j)
343 Vector2d diff = (*j)->pos - (*i)->pos;
344 if (diff.norm () != 0.0f)
345 (*i)->add_force (diff * ((10.0f - (*j)->mass)/(diff.norm () * diff.norm ())));
349 for (SpringIter i
= springs
.begin (); i
!= springs
.end (); ++i
)
350 (*i
)->update (delta
);
352 particle_mgr
->update(delta
);
354 //std::cout << "Colliders: " << colliders.size () << std::endl;
355 for (Colliders::iterator i
= colliders
.begin (); i
!= colliders
.end (); ++i
)
359 std::vector
<Spring
*> new_springs
;
360 for (SpringIter i
= springs
.begin (); i
!= springs
.end (); ++i
)
364 if ((*i
)->length
> 20.0f
)
367 Vector2d pos
= ((*i
)->particles
.first
->pos
368 + (*i
)->particles
.second
->pos
) * 0.5f
;
370 // FIXME: particle mass needs to be recalculated
371 Particle
* p1
= particle_mgr
->add_particle (pos
, (*i
)->particles
.first
->velocity
* 0.5f
, .1f
);
372 Particle
* p2
= particle_mgr
->add_particle (pos
, (*i
)->particles
.second
->velocity
* 0.5f
, .1f
);
374 // FIXME: Insert a more sofistikated string splitter here
375 new_springs
.push_back (new Spring ((*i
)->particles
.first
, p1
, (*i
)->length
/2));
376 new_springs
.push_back (new Spring ((*i
)->particles
.second
, p2
, (*i
)->length
/2));
380 springs
.insert(springs
.end(), new_springs
.begin(), new_springs
.end ());
382 // Remove any springs that are marked as destroyed
383 // FIXME: Could be faster
384 SpringIter i
= springs
.begin ();
385 while ( i
!= springs
.end ())
390 i
= springs
.erase(i
);
400 World::get_spring (float x
, float y
)
403 float min_distance
= 0.0f
;
405 float capture_threshold
= 15;
407 for (SpringIter i
= springs
.begin (); i
!= springs
.end (); ++i
)
411 float& x1
= (*i
)->particles
.first
->pos
.x
;
412 float& y1
= (*i
)->particles
.first
->pos
.y
;
413 float& x2
= (*i
)->particles
.second
->pos
.x
;
414 float& y2
= (*i
)->particles
.second
->pos
.y
;
416 // FIXME: optimize me
417 float u
= (((x0
- x1
)*(x2
-x1
) + (y0
- y1
)*(y2
- y1
))
418 / ((x2
-x1
)*(x2
-x1
)+(y2
-y1
)*(y2
-y1
)));
420 float distance
= (fabs((x2
- x1
)*(y1
-y0
) - (x1
-x0
)*(y2
-y1
))
421 / sqrt((x2
-x1
)*(x2
-x1
) + (y2
-y1
)*(y2
-y1
)));
423 if (u
>= 0 && u
<= 1.0f
424 && ((spring
&& min_distance
> distance
)
425 || (!spring
&& distance
<= capture_threshold
))) // FIXME: threashold is dependend on view
428 min_distance
= distance
;
436 World::get_particle (float x
, float y
)
438 Particle
* particle
= 0;
440 Vector2d
mouse_pos (x
, y
);
442 for (ParticleFactory::ParticleIter i
= particle_mgr
->begin (); i
!= particle_mgr
->end (); ++i
)
444 Vector2d diff
= mouse_pos
- (*i
)->pos
;
445 if (diff
.norm () < min_dist
)
447 min_dist
= diff
.norm ();
455 std::vector
<Particle
*>
456 World::get_particles (float x1_
, float y1_
, float x2_
, float y2_
)
458 float x1
= Math::min(x1_
, x2_
);
459 float x2
= Math::max(x1_
, x2_
);
460 float y1
= Math::min(y1_
, y2_
);
461 float y2
= Math::max(y1_
, y2_
);
463 std::vector
<Particle
*> caputred_particles
;
464 for (ParticleFactory::ParticleIter i
= particle_mgr
->begin (); i
!= particle_mgr
->end (); ++i
)
466 if ((*i
)->pos
.x
>= x1
&& (*i
)->pos
.x
< x2
467 && (*i
)->pos
.y
>= y1
&& (*i
)->pos
.y
< y2
)
468 caputred_particles
.push_back(*i
);
470 return caputred_particles
;
474 World::zero_out_velocity ()
476 std::cout
<< "Setting velocity to zero" << std::endl
;
477 for (ParticleFactory::ParticleIter i
= get_particle_mgr()->begin();
478 i
!= get_particle_mgr()->end (); ++i
)
480 (*i
)->velocity
= Vector2d ();
485 World::add_spring (Particle
* last_particle
, Particle
* particle
)
487 assert (last_particle
&& particle
);
488 springs
.push_back (new Spring (last_particle
, particle
));
492 World::remove_particle (Particle
* p
)
494 // Remove everyting that references the particle
495 for (SpringIter i
= springs
.begin (); i
!= springs
.end ();)
497 if ((*i
)->particles
.first
== p
|| (*i
)->particles
.second
== p
)
500 // FIXME: this is potentially slow, since we don't care
501 // about order, we could speed this up
502 i
= springs
.erase(i
);
510 particle_mgr
->remove_particle(p
);
514 World::remove_spring (Spring
* s
)
516 //std::cout << "particles: " << particle_mgr->size () << std::endl;
517 //std::cout << "springs: " << springs.size () << std::endl;
520 springs
.erase(std::remove(springs
.begin (), springs
.end (), s
),
525 World::remove_collider (Collider
* c
)
528 colliders
.erase(std::remove(colliders
.begin (), colliders
.end (), c
),
535 particle_mgr
->clear();
537 for (SpringIter i
= springs
.begin (); i
!= springs
.end (); ++i
)
544 World::write_lisp (const std::string
& filename
)
548 out
= system_context
->open_output_file(filename
);
552 std::cout
<< "World: Couldn't open '" << filename
<< "' for writing" << std::endl
;
556 std::cout
<< "Writing to: " << filename
<< std::endl
;
558 fputs(";; Written by " PACKAGE_STRING
"\n", out
);
559 fputs("(construo-scene\n", out
);
560 fputs(" (version 3)\n", out
);
562 // FIXME: insert creation date here
563 // FIXME: Filter '()"' here
564 fprintf(out
, " (author \"%s\" \"%s\")\n",
565 system_context
->get_user_realname().c_str(),
566 system_context
->get_user_email().c_str());
568 particle_mgr
->write_lisp(out
);
571 fputs(" (springs\n", out
);
572 for (CSpringIter i
= springs
.begin (); i
!= springs
.end (); ++i
)
574 lisp_object_t
* obj
= (*i
)->serialize ();
576 lisp_dump (obj
, out
);
582 fputs (" (colliders\n", out
);
583 for (Colliders::iterator i
= colliders
.begin(); i
!= colliders
.end(); ++i
)
585 lisp_object_t
* obj
= (*i
)->serialize ();
587 lisp_dump (obj
, out
);
594 fputs(")\n\n;; EOF ;;\n", out
);
598 if (StringUtils::has_suffix(filename
, ".gz"))
599 { // Rewrite file compressed
600 std::cout
<< "World: Filename ends with .gz, rewriting " << filename
<< " compressed" << std::endl
;
605 buf
= static_cast<char*>(malloc(len
));
608 throw ConstruoError("Out of memory");
610 FILE* in
= system_context
->open_input_file(filename
);
611 read_len
= fread (buf
, sizeof (char), len
, in
);
614 throw ConstruoError("World: Internal error, read buffer to small");
618 // Write the buffer in compressed format
619 gzFile out
= gzopen(system_context
->translate_filename(filename
).c_str(), "wb");
620 gzwrite (out
, buf
, len
);
627 World::calc_bounding_box()
631 if (particle_mgr
->size() > 0)
633 bbox
.x1
= bbox
.x2
= (*particle_mgr
->begin ())->pos
.x
;
634 bbox
.y1
= bbox
.y2
= (*particle_mgr
->begin ())->pos
.y
;
645 for (ParticleFactory::ParticleIter i
= particle_mgr
->begin (); i
!= particle_mgr
->end (); ++i
)
647 bbox
.join((*i
)->pos
);
650 for (Colliders::iterator i
= colliders
.begin(); i
!= colliders
.end(); ++i
)
652 bbox
.join((*i
)->get_bounding_box());
659 World::get_num_particles()
661 return particle_mgr
->size ();
665 World::get_num_springs()
667 return springs
.size ();
671 World::add_rect_collider(const Vector2d
& pos1
, const Vector2d
& pos2
)
673 Rect
<float> rect (pos1
.x
, pos1
.y
, pos2
.x
, pos2
.y
);
675 colliders
.push_back(new RectCollider(rect
.x1
, rect
.y1
, rect
.x2
, rect
.y2
));