diff options
author | Perttu Ahola <celeron55@gmail.com> | 2010-11-27 01:02:21 +0200 |
---|---|---|
committer | Perttu Ahola <celeron55@gmail.com> | 2010-11-27 01:02:21 +0200 |
commit | 4e249fb3fbf75f0359758760d88e22aa5b14533c (patch) | |
tree | 323087d05efbd2ace27b316d4f017cf812a31992 /src/mapblockobject.cpp | |
download | minetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.tar.gz minetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.tar.bz2 minetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.zip |
Initial files
Diffstat (limited to 'src/mapblockobject.cpp')
-rw-r--r-- | src/mapblockobject.cpp | 641 |
1 files changed, 641 insertions, 0 deletions
diff --git a/src/mapblockobject.cpp b/src/mapblockobject.cpp new file mode 100644 index 000000000..985a01dc1 --- /dev/null +++ b/src/mapblockobject.cpp @@ -0,0 +1,641 @@ +/* +(c) 2010 Perttu Ahola <celeron55@gmail.com> +*/ + +#include "mapblockobject.h" +#include "mapblock.h" +// Only for ::getNodeBox, TODO: Get rid of this +#include "map.h" + +/* + MapBlockObject +*/ + +// This is here because it uses the MapBlock +v3f MapBlockObject::getAbsolutePos() +{ + if(m_block == NULL) + return m_pos; + + // getPosRelative gets nodepos relative to map origin + v3f blockpos = intToFloat(m_block->getPosRelative()); + return blockpos + m_pos; +} + +void MapBlockObject::setBlockChanged() +{ + if(m_block) + m_block->setChangedFlag(); +} + +/* + MovingObject +*/ +void MovingObject::move(float dtime, v3f acceleration) +{ + //m_pos += dtime * 3.0; + + v3s16 oldpos_i = floatToInt(m_pos); + + if(m_block->isValidPosition(oldpos_i) == false) + { + // Should have wrapped, cancelling further movement. + return; + } + + // No collisions if there is no collision box + if(m_collision_box == NULL) + { + m_speed += dtime * acceleration; + m_pos += m_speed * dtime; + return; + } + + v3f position = m_pos; + v3f oldpos = position; + + /*std::cout<<"oldpos_i=("<<oldpos_i.X<<","<<oldpos_i.Y<<"," + <<oldpos_i.Z<<")"<<std::endl;*/ + + // Maximum time increment (for collision detection etc) + // Allow 0.1 blocks per increment + // time = distance / speed + // NOTE: In the loop below collisions are detected at 0.15*BS radius + float speedlength = m_speed.getLength(); + f32 dtime_max_increment; + if(fabs(speedlength) > 0.001) + dtime_max_increment = 0.1*BS / speedlength; + else + dtime_max_increment = 0.5; + + m_touching_ground = false; + + u32 loopcount = 0; + do + { + loopcount++; + + f32 dtime_part; + if(dtime > dtime_max_increment) + dtime_part = dtime_max_increment; + else + dtime_part = dtime; + dtime -= dtime_part; + + // Begin of dtime limited code + + m_speed += acceleration * dtime_part; + position += m_speed * dtime_part; + + /* + Collision detection + */ + + v3s16 pos_i = floatToInt(position); + + // The loop length is limited to the object moving a distance + f32 d = (float)BS * 0.15; + + core::aabbox3d<f32> objectbox( + m_collision_box->MinEdge + position, + m_collision_box->MaxEdge + position + ); + + core::aabbox3d<f32> objectbox_old( + m_collision_box->MinEdge + oldpos, + m_collision_box->MaxEdge + oldpos + ); + + //TODO: Get these ranges from somewhere + for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++) + for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++) + for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++) + { + try{ + if(m_block->getNodeParent(v3s16(x,y,z)).d == MATERIAL_AIR){ + continue; + } + } + catch(InvalidPositionException &e) + { + // Doing nothing here will block the player from + // walking over map borders + } + + core::aabbox3d<f32> nodebox = Map::getNodeBox( + v3s16(x,y,z)); + + // See if the player is touching ground + if( + fabs(nodebox.MaxEdge.Y-objectbox.MinEdge.Y) < d + && nodebox.MaxEdge.X-d > objectbox.MinEdge.X + && nodebox.MinEdge.X+d < objectbox.MaxEdge.X + && nodebox.MaxEdge.Z-d > objectbox.MinEdge.Z + && nodebox.MinEdge.Z+d < objectbox.MaxEdge.Z + ){ + m_touching_ground = true; + } + + if(objectbox.intersectsWithBox(nodebox)) + { + + v3f dirs[3] = { + v3f(0,0,1), // back + v3f(0,1,0), // top + v3f(1,0,0), // right + }; + for(u16 i=0; i<3; i++) + { + f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]); + f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]); + f32 playermax = objectbox.MaxEdge.dotProduct(dirs[i]); + f32 playermin = objectbox.MinEdge.dotProduct(dirs[i]); + f32 playermax_old = objectbox_old.MaxEdge.dotProduct(dirs[i]); + f32 playermin_old = objectbox_old.MinEdge.dotProduct(dirs[i]); + + bool main_edge_collides = + ((nodemax > playermin && nodemax <= playermin_old + d + && m_speed.dotProduct(dirs[i]) < 0) + || + (nodemin < playermax && nodemin >= playermax_old - d + && m_speed.dotProduct(dirs[i]) > 0)); + + bool other_edges_collide = true; + for(u16 j=0; j<3; j++) + { + if(j == i) + continue; + f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]); + f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]); + f32 playermax = objectbox.MaxEdge.dotProduct(dirs[j]); + f32 playermin = objectbox.MinEdge.dotProduct(dirs[j]); + if(!(nodemax - d > playermin && nodemin + d < playermax)) + { + other_edges_collide = false; + break; + } + } + + if(main_edge_collides && other_edges_collide) + { + m_speed -= m_speed.dotProduct(dirs[i]) * dirs[i]; + position -= position.dotProduct(dirs[i]) * dirs[i]; + position += oldpos.dotProduct(dirs[i]) * dirs[i]; + } + + } + + } // if(objectbox.intersectsWithBox(nodebox)) + } // for y + + } // End of dtime limited loop + while(dtime > 0.001); + + m_pos = position; +} + +/* + MapBlockObjectList +*/ + +MapBlockObjectList::MapBlockObjectList(MapBlock *block): + m_block(block) +{ + m_mutex.Init(); +} + +MapBlockObjectList::~MapBlockObjectList() +{ + clear(); +} + +/* + The serialization format: + [0] u16 number of entries + [2] entries (id, typeId, parameters) +*/ + +void MapBlockObjectList::serialize(std::ostream &os, u8 version) +{ + JMutexAutoLock lock(m_mutex); + + u8 buf[2]; + writeU16(buf, m_objects.size()); + os.write((char*)buf, 2); + + for(core::map<s16, MapBlockObject*>::Iterator + i = m_objects.getIterator(); + i.atEnd() == false; i++) + { + i.getNode()->getValue()->serialize(os, version); + } +} + +void MapBlockObjectList::update(std::istream &is, u8 version, + scene::ISceneManager *smgr) +{ + JMutexAutoLock lock(m_mutex); + + /* + Collect all existing ids to a set. + + As things are updated, they are removed from this. + + All remaining ones are deleted. + */ + core::map<s16, bool> ids_to_delete; + for(core::map<s16, MapBlockObject*>::Iterator + i = m_objects.getIterator(); + i.atEnd() == false; i++) + { + ids_to_delete.insert(i.getNode()->getKey(), true); + } + + u8 buf[6]; + + is.read((char*)buf, 2); + u16 count = readU16(buf); + + for(u16 i=0; i<count; i++) + { + // Read id + is.read((char*)buf, 2); + s16 id = readS16(buf); + + // Read position + // stored as x1000/BS v3s16 + is.read((char*)buf, 6); + v3s16 pos_i = readV3S16(buf); + v3f pos((f32)pos_i.X/1000*BS, + (f32)pos_i.Y/1000*BS, + (f32)pos_i.Z/1000*BS); + + // Read typeId + is.read((char*)buf, 2); + u16 type_id = readU16(buf); + + bool create_new = false; + + // Find an object with the id + core::map<s16, MapBlockObject*>::Node *n; + n = m_objects.find(id); + // If no entry is found for id + if(n == NULL) + { + // Insert dummy pointer node + m_objects.insert(id, NULL); + // Get node + n = m_objects.find(id); + // A new object will be created at this node + create_new = true; + } + // If type_id differs + else if(n->getValue()->getTypeId() != type_id) + { + // Delete old object + delete n->getValue(); + // A new object will be created at this node + create_new = true; + } + + MapBlockObject *obj = NULL; + + if(create_new) + { + /*dstream<<"MapBlockObjectList adding new object" + " id="<<id + <<std::endl;*/ + + if(type_id == MAPBLOCKOBJECT_TYPE_TEST) + { + // The constructors of objects shouldn't need + // any more parameters than this. + obj = new TestObject(m_block, id, pos); + } + else if(type_id == MAPBLOCKOBJECT_TYPE_TEST2) + { + obj = new Test2Object(m_block, id, pos); + } + else if(type_id == MAPBLOCKOBJECT_TYPE_SIGN) + { + obj = new SignObject(m_block, id, pos); + } + else if(type_id == MAPBLOCKOBJECT_TYPE_RAT) + { + obj = new RatObject(m_block, id, pos); + } + else + { + throw SerializationError + ("MapBlockObjectList::update(): Unknown MapBlockObject type"); + } + + if(smgr != NULL) + obj->addToScene(smgr); + + n->setValue(obj); + } + else + { + obj = n->getValue(); + obj->updatePos(pos); + } + + // Now there is an object in obj. + // Update it. + + obj->update(is, version); + + // Remove from deletion list + if(ids_to_delete.find(id) != NULL) + ids_to_delete.remove(id); + } + + // Delete all objects whose ids_to_delete remain in ids_to_delete + for(core::map<s16, bool>::Iterator + i = ids_to_delete.getIterator(); + i.atEnd() == false; i++) + { + s16 id = i.getNode()->getKey(); + + /*dstream<<"MapBlockObjectList deleting object" + " id="<<id + <<std::endl;*/ + + MapBlockObject *obj = m_objects[id]; + obj->removeFromScene(); + delete obj; + m_objects.remove(id); + } +} + +s16 MapBlockObjectList::getFreeId() throw(ContainerFullException) +{ + s16 id = 0; + for(;;) + { + if(m_objects.find(id) == NULL) + return id; + if(id == 32767) + throw ContainerFullException + ("MapBlockObjectList doesn't fit more objects"); + id++; + } +} + +void MapBlockObjectList::add(MapBlockObject *object) + throw(ContainerFullException, AlreadyExistsException) +{ + if(object == NULL) + { + dstream<<"MapBlockObjectList::add(): NULL object"<<std::endl; + return; + } + + JMutexAutoLock lock(m_mutex); + + // Create unique id if id==-1 + if(object->m_id == -1) + { + object->m_id = getFreeId(); + } + + if(m_objects.find(object->m_id) != NULL) + { + dstream<<"MapBlockObjectList::add(): " + "object with same id already exists"<<std::endl; + throw AlreadyExistsException + ("MapBlockObjectList already has given id"); + } + + object->m_block = m_block; + + /*v3f p = object->m_pos; + dstream<<"MapBlockObjectList::add(): " + <<"m_block->getPos()=(" + <<m_block->getPos().X<<"," + <<m_block->getPos().Y<<"," + <<m_block->getPos().Z<<")" + <<" inserting object with id="<<object->m_id + <<" pos=" + <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")" + <<std::endl;*/ + + m_objects.insert(object->m_id, object); +} + +void MapBlockObjectList::clear() +{ + JMutexAutoLock lock(m_mutex); + + for(core::map<s16, MapBlockObject*>::Iterator + i = m_objects.getIterator(); + i.atEnd() == false; i++) + { + MapBlockObject *obj = i.getNode()->getValue(); + //FIXME: This really shouldn't be NULL at any time, + // but this condition was added because it was. + if(obj != NULL) + { + obj->removeFromScene(); + delete obj; + } + } + + m_objects.clear(); +} + +void MapBlockObjectList::remove(s16 id) +{ + JMutexAutoLock lock(m_mutex); + + core::map<s16, MapBlockObject*>::Node *n; + n = m_objects.find(id); + if(n == NULL) + return; + + n->getValue()->removeFromScene(); + delete n->getValue(); + m_objects.remove(id); +} + +MapBlockObject * MapBlockObjectList::get(s16 id) +{ + core::map<s16, MapBlockObject*>::Node *n; + n = m_objects.find(id); + if(n == NULL) + return NULL; + else + return n->getValue(); +} + +void MapBlockObjectList::step(float dtime, bool server) +{ + JMutexAutoLock lock(m_mutex); + + core::map<s16, bool> ids_to_delete; + + for(core::map<s16, MapBlockObject*>::Iterator + i = m_objects.getIterator(); + i.atEnd() == false; i++) + { + MapBlockObject *obj = i.getNode()->getValue(); + + if(server) + { + bool to_delete = obj->serverStep(dtime); + + if(to_delete) + ids_to_delete.insert(obj->m_id, true); + } + else + { + obj->clientStep(dtime); + } + } + + // Delete objects in delete queue + for(core::map<s16, bool>::Iterator + i = ids_to_delete.getIterator(); + i.atEnd() == false; i++) + { + s16 id = i.getNode()->getKey(); + + MapBlockObject *obj = m_objects[id]; + obj->removeFromScene(); + delete obj; + m_objects.remove(id); + } + + /* + Wrap objects on server + */ + + if(server == false) + return; + + for(core::map<s16, MapBlockObject*>::Iterator + i = m_objects.getIterator(); + i.atEnd() == false; i++) + { + MapBlockObject *obj = i.getNode()->getValue(); + + v3s16 pos_i = floatToInt(obj->m_pos); + + if(m_block->isValidPosition(pos_i)) + { + // No wrap + continue; + } + + bool impossible = wrapObject(obj); + + if(impossible) + { + // No wrap + continue; + } + + // Restart find + i = m_objects.getIterator(); + } +} + +bool MapBlockObjectList::wrapObject(MapBlockObject *object) +{ + // No lock here; this is called so that the lock is already locked. + //JMutexAutoLock lock(m_mutex); + + assert(object->m_block == m_block); + assert(m_objects.find(object->m_id) != NULL); + assert(m_objects[object->m_id] == object); + + NodeContainer *parentcontainer = m_block->getParent(); + // This will only work if the parent is the map + if(parentcontainer->nodeContainerId() != NODECONTAINER_ID_MAP) + { + dstream<<"WARNING: Wrapping object not possible: " + "MapBlock's parent is not map"<<std::endl; + return true; + } + // OK, we have the map! + Map *map = (Map*)parentcontainer; + + // Calculate blockpos on map + v3s16 oldblock_pos_i_on_map = m_block->getPosRelative(); + v3f pos_f_on_oldblock = object->m_pos; + v3s16 pos_i_on_oldblock = floatToInt(pos_f_on_oldblock); + v3s16 pos_i_on_map = pos_i_on_oldblock + oldblock_pos_i_on_map; + v3s16 pos_blocks_on_map = getNodeBlockPos(pos_i_on_map); + + // Get new block + MapBlock *newblock; + try{ + newblock = map->getBlockNoCreate(pos_blocks_on_map); + } + catch(InvalidPositionException &e) + { + // Couldn't find block -> not wrapping + /*dstream<<"WARNING: Wrapping object not possible: " + <<"could not find new block" + <<"("<<pos_blocks_on_map.X + <<","<<pos_blocks_on_map.Y + <<","<<pos_blocks_on_map.Z + <<")"<<std::endl;*/ + /*dstream<<"pos_f_on_oldblock=(" + <<pos_f_on_oldblock.X<<"," + <<pos_f_on_oldblock.Y<<"," + <<pos_f_on_oldblock.Z<<")" + <<std::endl;*/ + return true; + } + + if(newblock == m_block) + { + dstream<<"WARNING: Wrapping object not possible: " + "newblock == oldblock"<<std::endl; + return true; + } + + // Calculate position on new block + v3f oldblock_pos_f_on_map = intToFloat(oldblock_pos_i_on_map); + v3s16 newblock_pos_i_on_map = newblock->getPosRelative(); + v3f newblock_pos_f_on_map = intToFloat(newblock_pos_i_on_map); + v3f pos_f_on_newblock = pos_f_on_oldblock + - newblock_pos_f_on_map + oldblock_pos_f_on_map; + + // Remove object from this block + m_objects.remove(object->m_id); + + // Add object to new block + object->m_pos = pos_f_on_newblock; + object->m_id = -1; + object->m_block = NULL; + newblock->addObject(object); + + //dstream<<"NOTE: Wrapped object"<<std::endl; + + return false; +} + +void MapBlockObjectList::getObjects(v3f origin, f32 max_d, + core::array<DistanceSortedObject> &dest) +{ + for(core::map<s16, MapBlockObject*>::Iterator + i = m_objects.getIterator(); + i.atEnd() == false; i++) + { + MapBlockObject *obj = i.getNode()->getValue(); + + f32 d = (obj->m_pos - origin).getLength(); + + if(d > max_d) + continue; + + DistanceSortedObject dso(obj, d); + + dest.push_back(dso); + } +} + +//END |