// Support function for finding collided nodes using a subset of nodes in the scene // recursive method for going through all scene nodes void getNodeRayBB(ISceneNode* root, const core::line3df& ray, s32 bits, bool recurse, f32& outbestdistance, ISceneNode*& outbestnode) { core::vector3df edges[8]; const core::list<ISceneNode*>& children = root->getChildren(); core::list<ISceneNode*>::ConstIterator it = children.begin(); for (; it != children.end(); ++it) { ISceneNode* current = *it; if (current->isVisible() && // (bNoDebugObjects ? !current->isDebugObject() : true) && (bits==0 || (bits != 0 && (current->getID() & bits)))) { // get world to object space transform core::matrix4 mat; if (!current->getAbsoluteTransformation().getInverse(mat)) continue; // transform vector from world space to object space core::line3df line(ray); mat.transformVect(line.start); mat.transformVect(line.end); const core::aabbox3df& box = current->getBoundingBox(); // do intersection test in object space if (box.intersectsWithLine(line)) { box.getEdges(edges); f32 distance = 0.0f; for (s32 e=0; e<8; ++e) { f32 t = edges[e].getDistanceFromSQ(line.start); if (t > distance) distance = t; } if (distance < outbestdistance) { outbestnode = current; outbestdistance = distance; } } } if ( recurse ) getNodeRayBB(current, ray, bits, recurse, outbestdistance, outbestnode); } }
// Support function for finding collided nodes using a subset of nodes in the scene // recursive method for going through all scene nodes and testing them against a point bool getNodePointBB(ISceneNode* root, vector3df point, s32 bits, bool recurse, ISceneNode*& outbestnode) { core::vector3df edges[8]; const core::list<ISceneNode*>& children = root->getChildren(); core::list<ISceneNode*>::ConstIterator it = children.begin(); for (; it != children.end(); ++it) { ISceneNode* current = *it; if (current->isVisible() && // (bNoDebugObjects ? !current->isDebugObject() : true) && (bits==0 || (bits != 0 && (current->getID() & bits)))) { // get world to object space transform core::matrix4 mat; if (!current->getAbsoluteTransformation().getInverse(mat)) continue; // transform vector from world space to object space vector3df currentPoint( point ); mat.transformVect(currentPoint); const core::aabbox3df& box = current->getBoundingBox(); // do intersection test in object space if (box.isPointInside( currentPoint )) { outbestnode = current; return true; } } if ( recurse ) if ( getNodePointBB(current, point, bits, recurse, outbestnode)) return true; } return false; }
// ---------------------------------------------------------------------------- void Track::exportElements(std::ofstream& stream, bool obj) { ISceneManager* sm = Editor::getEditor()->getSceneManager(); ISceneNode* node; stringc name; int i = 1; while ((node = sm->getSceneNodeFromId(MAGIC_NUMBER + i))) { name = node->getName(); vector3df pos, rot, sca; if (node->isVisible() && name != "banana" && name != "item" && name != "small-nitro" && name != "big-nitro" && (name.equalsn("obj/", 4) == obj)) { pos = node->getPosition(); rot = node->getRotation(); sca = node->getScale(); if (name.equalsn("obj/", 4)) { stream << " <static-object model=\"" << Editor::toRelative(name).c_str(); copyObj(name); ITexture* tex; for (int j = 0; (tex = node->getMaterial(0).getTexture(j)); j++) copyObj(stringc("obj/") + Editor::toRelative(tex->getName())); } // export as static-object else { stream << " <library name=\"" << Editor::getLib(node->getName()).c_str(); } // export as library stream << "\" xyz=\""; stream << pos.X << " " << pos.Y << " " << pos.Z << "\" hpr=\""; stream << rot.X << " " << rot.Y << " " << rot.Z << "\" scale=\""; stream << sca.X << " " << sca.Y << " " << sca.Z << "\"/>\n"; } i++; } } // exportElements
// ---------------------------------------------------------------------------- void Track::build() { IrrlichtDevice* device = Editor::getEditor()->getDevice(); PHYSFS_setWriteDir(Editor::getEditor()->getTrackDir().c_str()); PHYSFS_mkdir(m_file_name.c_str()); path p = Editor::getEditor()->getTrackDir() + m_file_name; CMeshBuffer<S3DVertex2TCoords>* mb = m_terrain->build(p); SMesh smesh; smesh.addMeshBuffer(mb); for (u32 i = 1; i < m_roads.size(); i++) { IRoad* r = m_roads[i]; if (r->getSpline()->getPointNum()>1) smesh.addMeshBuffer(((Road*)r)->getMeshBuffer()); } B3DMeshWriter* writer = new B3DMeshWriter(device->getFileSystem()); IWriteFile *file; file = device->getFileSystem()->createAndWriteFile((p + "/track.b3d").c_str()); writer->writeMesh(file, &smesh); file->drop(); delete writer; m_driveline->build(p); std::ofstream mat; mat.open((p + "/materials.xml").c_str()); mat << "<materials>\n"; mat << " <material name=\"splatt.png\" graphical-effect=\"splatting\""; SMaterial m = m_terrain->getMaterial(0); for (int i = 1; i < 5; i++) { mat << " splatting-texture-" << i << "=\""; mat << Editor::toRelative(m.getTexture(i+1)->getName()).c_str(); mat << "\""; } mat << "/>\n"; if (m_gravity_road) { for (u32 i = 1; i < m_roads.size(); i++) { stringc tex = m_roads[i]->getTexName(); if (tex.size()>0) { mat << " <material name=\""; mat << tex.c_str(); mat << "\" has-gravity=\"yes\" />\n"; } } // roads } // gravity road mode mat <<"</materials>\n"; mat.close(); stringw track; track += "<track name = \""; track += m_track_name + L"\"\n"; track += " version = \"5\"\n"; track += " groups = \"made-by-STK-TE\"\n"; track += " designer = \""; track += m_designer + "\"\n"; track += " music = \""; track += m_music.c_str(); track += "\"\n"; track += " screenshot = \"screenshot.jpg\"\n"; track += " smooth-normals = \"true\"\n"; track += " reverse = \"Y\"\n>\n"; track += "</track>\n"; PHYSFS_uint64 len = 4 * track.size(); char* dst = new char[len]; #ifdef _WIN32 PHYSFS_utf8FromUcs2((PHYSFS_uint16*)track.c_str(),dst,len); #else PHYSFS_utf8FromUcs4((PHYSFS_uint32*)track.c_str(), dst, len); #endif FILE* f; f = fopen((p + "/track.xml").c_str(), "wb"); fwrite(dst, sizeof(char), strlen(dst), f); fclose(f); delete[] dst; std::ofstream scene; scene.open((p + "/scene.xml").c_str()); scene << "<scene>\n"; scene << " <track model=\"track.b3d\" x=\"0\" y=\"0\" z=\"0\">\n"; exportElements(scene, true); scene << " </track>\n"; exportElements(scene, false); ISceneManager* sm = Editor::getEditor()->getSceneManager(); ISceneNode* node; int i = 1; stringc name; while ((node = sm->getSceneNodeFromId(MAGIC_NUMBER + i))) { name = node->getName(); vector3df pos; if (node->isVisible() && (name == "banana" || name == "item" || name == "small-nitro" || name == "big-nitro")) { pos = node->getPosition(); scene << " <" << name.c_str() << " x=\"" << pos.X << "\" y=\"" << pos.Y << "\" z=\"" << pos.Z << "\" />\n"; } i++; } scene << Viewport::get()->getSky()->getXmlString().c_str(); Viewport::get()->printCheckLine(&scene); scene << " <default-start karts-per-row = \"3\"\n"; scene << " forwards-distance =\"1.50\"\n"; scene << " sidewards-distance=\"3.00\"\n"; scene << " upwards-distance =\"0.10\"/>\n"; scene << "</scene>\n"; scene.close(); MsgWndw::get()->showMsg(_("Track exported!")); } // build
// ---------------------------------------------------------------------------- void Track::save() { FILE* pFile = 0; path p = Editor::getEditor()->getMapsPath() + "/" + m_file_name.c_str(); pFile = fopen(p.c_str(), "wb"); if (!pFile) { MsgWndw::get()->showMsg(_("Save failed: file could not be created!\n")); return; } // SIGN u64 sign = TOP_SECRET_SIGNATURE_NUMBER; fwrite(&sign, sizeof(u64), 1, pFile); // TRACK NAME u8 size = m_track_name.size() + 1; fwrite(&size, sizeof(u8), 1, pFile); fwrite(m_track_name.c_str(), sizeof(wchar_t), size, pFile); // DESIGNER NAME size = m_designer.size() + 1; fwrite(&size, sizeof(u8), 1, pFile); fwrite(m_designer.c_str(), sizeof(wchar_t), size, pFile); // FILE NAME size = m_file_name.size() + 1; fwrite(&size, sizeof(u8), 1, pFile); fwrite(m_file_name.c_str(), sizeof(c8), size, pFile); // MUSIC size = m_music.size() + 1; fwrite(&size, sizeof(u8), 1, pFile); fwrite(m_music.c_str(), sizeof(c8), size, pFile); // TERRAIN m_terrain->save(pFile); // SKY Viewport::get()->getSky()->save(pFile); // GRAVITY ROAD FLAG fwrite(&m_gravity_road, sizeof(bool), 1, pFile); // ROADS size = m_roads.size(); fwrite(&size, sizeof(u8), 1, pFile); IRoad* r; for (u8 i = 0; i < size; i++) { r = m_roads[i]; r->save(pFile); } //CHECKLINES Viewport::get()->saveCheckLines(pFile); // OBJECTS ISceneManager* sm = Editor::getEditor()->getSceneManager(); ISceneNode* node; u32 num = Viewport::getLastEntityID() - MAGIC_NUMBER; u32 vnum = 0; for (u32 i = 0; i < num; i++) { node = sm->getSceneNodeFromId(MAGIC_NUMBER + i + 1); if (node && node->isVisible()) vnum++; } fwrite(&vnum, sizeof(u32), 1, pFile); for (u32 i = 0; i < num; i++) { node = sm->getSceneNodeFromId(MAGIC_NUMBER + i + 1); assert(node); if (node->isVisible()) { fwrite(&node->getPosition(), sizeof(vector3df), 1, pFile); fwrite(&node->getRotation(), sizeof(vector3df), 1, pFile); fwrite(&node->getScale(), sizeof(vector3df), 1, pFile); u8 size = strlen(node->getName()) + 1; fwrite(&size, sizeof(u8), 1, pFile); fwrite(node->getName(), sizeof(c8), size, pFile); } } fclose(pFile); Editor::getEditor()->addToRecentlyOpenedList(m_file_name); MsgWndw::get()->showMsg(_("Track saved!\n")); } // save
/****************************************************************************** * Apply explosion damage to all objects within the radius of the given * gameObject, scaled based on distance from the explosion, up to a maximum * damage as dictated by the given object's damage value. Explosions * affect vehicles, buildings, enemies, and Borbie. *****************************************************************************/ void GameInstance::applyExplosionDamage(GameObject *explodingObject) { // get the exploding object's variables GameObjectType explodingType = explodingObject->getObjectType(); ISceneNode *explodingNode = explodingObject->getNode(); float explosionRadius = explodingObject->getExplosionRadius(); float explosionDamage = explodingObject->getExplosionDamage(); vector3df explodePos = explodingNode->getPosition(); // calculate damage to enemies int numEnemies = enemies->objList.size(); for(int i=0; i<numEnemies; i++){ ISceneNode *curNode = enemies->objList[i]->getNode(); // if current node is the exploding node, ignore it if(curNode == explodingNode) continue; // if current node is NOT visible or if it already blew up, ignore it else if(!curNode->isVisible() || enemies->objList[i]->hasExploded()) continue; float distance = curNode->getPosition().getDistanceFrom(explodePos); if(distance <= explosionRadius){ int damage = explosionDamage; // max damage if(distance > 400){ // if more than 400 away, scale down damage float scale = (distance-400) / (explosionRadius-400); damage = int(explosionDamage * scale); } enemies->objList[i]->applyDamage(damage); } } // calculate damage for buildings int numBuild = buildings->objList.size(); for(int i=0; i<numBuild; i++){ ISceneNode *curNode = buildings->objList[i]->getNode(); // if current node is the exploding node, ignore it if(curNode == explodingNode) continue; // if current node is NOT visible, ignore it else if(!curNode->isVisible() || buildings->objList[i]->hasExploded()) continue; // otherwise, check if the distance is close enough, and apply damage // based on the distance to the explosion center. Offset position by 200 // since the buildings are not positioned relative to their center. float distance = curNode->getPosition().getDistanceFrom(explodePos); if(distance <= explosionRadius){ int damage = explosionDamage; // max damage if(distance > 400){ // if more than 400 away, scale down damage float scale = (distance-400) / (explosionRadius-400); damage = int(explosionDamage * scale); } // if the exploding object is a building, reduce splash damage // to prevent instant-killing an entire city. That would suck. if(explodingType == TYPE_BUILDING) damage = damage / 20; buildings->objList[i]->applyDamage(damage); } } // calculate damage for vehicles int numVehicles = vehicles->objList.size(); for(int i = 0 ; i < numVehicles ; i++){ ISceneNode *curNode = vehicles->objList[i]->getNode(); // if current node is the exploding node, ignore it if(curNode == explodingNode) continue; // if current node is NOT visible, ignore it else if(!curNode->isVisible() || vehicles->objList[i]->hasExploded()) continue; // otherwise, check if the distance is close enough, and apply damage // based on the distance to the explosion center float distance = curNode->getPosition().getDistanceFrom(explodePos); if(distance <= explosionRadius){ int damage = explosionDamage; // max damage if(distance > 400){ // if more than 400 away, scale down damage float scale = (distance-400) / (explosionRadius-400); damage = int(explosionDamage * scale); } vehicles->objList[i]->applyDamage(damage); } } // if Borbie is already dead, don't kill her again :( if(player->hasExploded()) return; // apply damage to borbie herself (the player) float distance = player->getNode()->getPosition().getDistanceFrom(explodePos); // if a vehicle, lower the explosion radius that applies to borbie if(explodingType == TYPE_VEHICLE) explosionRadius = 600; if(distance <= explosionRadius){ int damage = explosionDamage; // max damage if(distance > 400){ // if more than 400 away, scale down damage float scale = (distance-400) / (explosionRadius-400); damage = int(explosionDamage * scale); } player->applyDamage(damage/2); // reduce damage by half } }
void CImpostorSceneNode::OnRegisterSceneNode() { if (!IsVisible) return; RenderCount++; // in here we: // decide which nodes need updates, add them to the queue // process the render queue // allocate textures and mesh buffers if required // perform clean up tasks: // garbage collection- free up spaces // reorganise // get collision manager ISceneCollisionManager* colmgr = SceneManager->getSceneCollisionManager(); // and the active camera ICameraSceneNode* cam = SceneManager->getActiveCamera(); core::aabbox3df camBB = cam->getViewFrustum()->getBoundingBox(); u32 now = Timer->getRealTime(); // loop through all impostor nodes u32 i; for (i=0; i < Impostors.size(); ++i) { SNodeLink &Imp = Impostors[i]; ISceneNode *n = Imp.Node; // skip invisible and culled nodes if (!n->isVisible() || !camBB.intersectsWithBox(n->getTransformedBoundingBox())) { //Culled++; Imp.IsActive = false; continue; } updatePosAndVector(Imp); // now we have the screen position... core::rect<s32> r = SceneManager->getVideoDriver()->getViewPort(); if (!Imp.NewPos.isValid() || !r.isRectCollided(Imp.NewPos) ) { // culled // Culled++; continue; } core::dimension2di newSize = Imp.NewPos.getSize(); // Change in rotation: a.length >= a.dotProduct(b) <= -a.length f32 diff = 0; // far plane = never update, near plane = always update f32 far = cam->getFarValue(); far *= far; f32 dist = Imp.Node->getAbsolutePosition().getDistanceFromSQ(cam->getAbsolutePosition()); dist = 1.0 - (far/dist); // value between 0 and 1 diff = dist; Imp.Score = dist; //s32 a = core::max_<s32>(Imp.NewPos.getWidth(), Imp.NewPos.getHeight()); //diff *= f32(a); bool reRender = diff > Threshold; //reRender =true; reRender = reRender || ( !Imp.IsQueued && Imp.BufferID != -1 && Buffers[Imp.BufferID].SlotSize < getTextureSizeFromSurfaceSize(core::max_<s32>(newSize.Width, newSize.Height))); // so small that we don't care about it if (newSize.Width < 4 || newSize.Height < 4) { Imp.IsActive = false; // object was culled //Culled++; continue; } // too large to fit in a texture slot if (newSize.Width > MaxSize || newSize.Height > MaxSize) { // get rid releaseSlot(Imp); Imp.IsActive = false; } else { Imp.IsActive = true; } if (Imp.IsActive && Imp.BufferID == -1 && !Imp.IsQueued ) reRender = true; if (Imp.IsActive) { // impostor replaces the node if (reRender && now > Imp.Time + MinTime) { if (!Imp.IsQueued) { CacheMisses++; Imp.IsQueued = true; SRenderQueueItem q; q.Node = &Imp; //q.Value = val; RenderQueue.push_back(q); } else { //QueueHits++; } } else { // don't re-render the impostor texture, only draw it CacheHits++; } } if (!Imp.IsActive) // || ( Imp.BufferID == -1 && Imp.IsQueued)) { // original node is visible n->OnRegisterSceneNode(); // cache miss CacheMisses++; } } SceneManager->registerNodeForRendering(this, ESNRP_TRANSPARENT); }