void RenderSubmit(SceneCollector& collector) { // (This is only called if a > 0) collector.Submit(&m_Overlay); if (ICmpSelectable::ms_EnableDebugOverlays) { // allocate debug overlays on-demand if (!m_DebugBoundingBoxOverlay) m_DebugBoundingBoxOverlay = new SOverlayLine; if (!m_DebugSelectionBoxOverlay) m_DebugSelectionBoxOverlay = new SOverlayLine; CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), GetEntityId()); if (cmpVisual) { SimRender::ConstructBoxOutline(cmpVisual->GetBounds(), *m_DebugBoundingBoxOverlay); m_DebugBoundingBoxOverlay->m_Thickness = 2; m_DebugBoundingBoxOverlay->m_Color = CColor(1.f, 0.f, 0.f, 1.f); SimRender::ConstructBoxOutline(cmpVisual->GetSelectionBox(), *m_DebugSelectionBoxOverlay); m_DebugSelectionBoxOverlay->m_Thickness = 2; m_DebugSelectionBoxOverlay->m_Color = CColor(0.f, 1.f, 0.f, 1.f); collector.Submit(m_DebugBoundingBoxOverlay); collector.Submit(m_DebugSelectionBoxOverlay); } } else { // reclaim debug overlay line memory when no longer debugging (and make sure to set to zero after deletion) if (m_DebugBoundingBoxOverlay) SAFE_DELETE(m_DebugBoundingBoxOverlay); if (m_DebugSelectionBoxOverlay) SAFE_DELETE(m_DebugSelectionBoxOverlay); } }
float GetConstructionProgressOffset(const CVector3D& pos) { if (m_ConstructionProgress.IsZero()) return 0.0f; CmpPtr<ICmpVisual> cmpVisual(GetEntityHandle()); if (!cmpVisual) return 0.0f; // We use selection boxes to calculate the model size, since the model could be offset // TODO: this annoyingly shows decals, would be nice to hide them CBoundingBoxOriented bounds = cmpVisual->GetSelectionBox(); if (bounds.IsEmpty()) return 0.0f; float dy = 2.0f * bounds.m_HalfSizes.Y; // If this is a floating unit, we want it to start all the way under the terrain, // so find the difference between its current position and the terrain CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity()); if (cmpTerrain && (m_Floating || m_ActorFloating)) { float ground = cmpTerrain->GetExactGroundLevel(pos.X, pos.Z); dy += std::max(0.f, pos.Y - ground); } return (m_ConstructionProgress.ToFloat() - 1.0f) * dy; }
std::vector<entity_id_t> EntitySelection::PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, player_id_t owner, bool allowEditorSelectables) { PROFILE2("PickEntitiesInRect"); // Make sure sx0 <= sx1, and sy0 <= sy1 if (sx0 > sx1) std::swap(sx0, sx1); if (sy0 > sy1) std::swap(sy0, sy1); CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY); ENSURE(cmpRangeManager); std::vector<entity_id_t> hitEnts; const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable); for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it) { entity_id_t ent = it->first; CEntityHandle handle = it->second->GetEntityHandle(); // Check if this entity is only selectable in Atlas if (static_cast<ICmpSelectable*>(it->second)->IsEditorOnly() && !allowEditorSelectables) continue; // Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld) if (cmpRangeManager->GetLosVisibility(handle, owner) == ICmpRangeManager::VIS_HIDDEN) continue; // Ignore entities not owned by 'owner' CmpPtr<ICmpOwnership> cmpOwnership(handle); if (owner != INVALID_PLAYER && (!cmpOwnership || cmpOwnership->GetOwner() != owner)) continue; // Find the current interpolated model position. // (We just use the centre position and not the whole bounding box, because maybe // that's better for users trying to select objects in busy areas) CmpPtr<ICmpVisual> cmpVisual(handle); if (!cmpVisual) continue; CVector3D position = cmpVisual->GetPosition(); // Reject if it's not on-screen (e.g. it's behind the camera) if (!camera.GetFrustum().IsPointVisible(position)) continue; // Compare screen-space coordinates float x, y; camera.GetScreenCoordinates(position, x, y); int ix = (int)x; int iy = (int)y; if (sx0 <= ix && ix <= sx1 && sy0 <= iy && iy <= sy1) hitEnts.push_back(ent); } return hitEnts; }
std::vector<entity_id_t> EntitySelection::PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, int owner, bool includeOffScreen, bool matchRank) { CmpPtr<ICmpTemplateManager> cmpTemplateManager(simulation, SYSTEM_ENTITY); CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY); std::vector<entity_id_t> hitEnts; const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable); for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it) { entity_id_t ent = it->first; if (matchRank) { // Exact template name matching if (cmpTemplateManager->GetCurrentTemplateName(ent) != templateName) continue; } // Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld) // In this case, the checking is done to avoid selecting garrisoned units if (cmpRangeManager->GetLosVisibility(ent, owner) == ICmpRangeManager::VIS_HIDDEN) continue; // Ignore entities not owned by 'owner' CmpPtr<ICmpOwnership> cmpOwnership(simulation.GetSimContext(), ent); if (!cmpOwnership || cmpOwnership->GetOwner() != owner) continue; // Ignore off screen entities if (!includeOffScreen) { // Find the current interpolated model position. CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent); if (!cmpVisual) continue; CVector3D position = cmpVisual->GetPosition(); // Reject if it's not on-screen (e.g. it's behind the camera) if (!camera.GetFrustum().IsPointVisible(position)) continue; } if (!matchRank) { // Match by selection group name // (This is relatively expensive since it involves script calls, so do it after all other tests) CmpPtr<ICmpIdentity> cmpIdentity(simulation.GetSimContext(), ent); if (!cmpIdentity || cmpIdentity->GetSelectionGroupName() != templateName) continue; } hitEnts.push_back(ent); } return hitEnts; }
std::vector<entity_id_t> EntitySelection::PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, int owner) { // Make sure sx0 <= sx1, and sy0 <= sy1 if (sx0 > sx1) std::swap(sx0, sx1); if (sy0 > sy1) std::swap(sy0, sy1); CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY); ENSURE(!cmpRangeManager.null()); std::vector<entity_id_t> hitEnts; const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable); for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it) { entity_id_t ent = it->first; // Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld) if (cmpRangeManager->GetLosVisibility(ent, owner) == ICmpRangeManager::VIS_HIDDEN) continue; // Ignore entities not owned by 'owner' CmpPtr<ICmpOwnership> cmpOwnership(simulation.GetSimContext(), ent); if (cmpOwnership.null() || cmpOwnership->GetOwner() != owner) continue; // Find the current interpolated model position. // (We just use the centre position and not the whole bounding box, because maybe // that's better for users trying to select objects in busy areas) CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent); if (cmpVisual.null()) continue; CVector3D position = cmpVisual->GetPosition(); // Reject if it's not on-screen (e.g. it's behind the camera) if (!camera.GetFrustum().IsPointVisible(position)) continue; // Compare screen-space coordinates float x, y; camera.GetScreenCoordinates(position, x, y); int ix = (int)x; int iy = (int)y; if (sx0 <= ix && ix <= sx1 && sy0 <= iy && iy <= sy1) hitEnts.push_back(ent); } return hitEnts; }
void ActorViewer::Render() { m.Terrain.MakeDirty(RENDERDATA_UPDATE_COLOR); g_Renderer.SetClearColor(m.Background); // Set shadows, sky and water locally (avoid clobbering global state) bool oldShadows = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWS); g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, m.ShadowsEnabled); bool oldSky = g_Renderer.GetSkyManager()->m_RenderSky; g_Renderer.GetSkyManager()->m_RenderSky = false; bool oldWater = g_Renderer.GetWaterManager()->m_RenderWater; g_Renderer.GetWaterManager()->m_RenderWater = m.WaterEnabled; // Set simulation context for rendering purposes g_Renderer.SetSimulation(&m.Simulation2); g_Renderer.BeginFrame(); // Find the centre of the interesting region, in the middle of the patch // and half way up the model (assuming there is one) CVector3D centre; CmpPtr<ICmpVisual> cmpVisual(m.Simulation2, m.Entity); if (cmpVisual) cmpVisual->GetBounds().GetCentre(centre); else centre.Y = 0.f; centre.X = centre.Z = TERRAIN_TILE_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2; CCamera camera = AtlasView::GetView_Actor()->GetCamera(); camera.m_Orientation.Translate(centre.X, centre.Y, centre.Z); camera.UpdateFrustum(); g_Renderer.SetSceneCamera(camera, camera); g_Renderer.RenderScene(m); glDisable(GL_DEPTH_TEST); g_Logger->Render(); g_ProfileViewer.RenderProfile(); glEnable(GL_DEPTH_TEST); g_Renderer.EndFrame(); // Restore the old renderer state g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, oldShadows); g_Renderer.GetSkyManager()->m_RenderSky = oldSky; g_Renderer.GetWaterManager()->m_RenderWater = oldWater; ogl_WarnIfError(); }
std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, int player) { CVector3D origin, dir; camera.BuildCameraRay(screenX, screenY, origin, dir); CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY); ENSURE(!cmpRangeManager.null()); std::vector<std::pair<float, entity_id_t> > hits; // (dist^2, entity) pairs const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable); for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it) { entity_id_t ent = it->first; // Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld) if (cmpRangeManager->GetLosVisibility(ent, player) == ICmpRangeManager::VIS_HIDDEN) continue; CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent); if (cmpVisual.null()) continue; CBound bounds = cmpVisual->GetBounds(); float tmin, tmax; if (!bounds.RayIntersect(origin, dir, tmin, tmax)) continue; // Find the perpendicular distance from the object's centre to the picker ray CVector3D centre; bounds.GetCentre(centre); CVector3D closest = origin + dir * (centre - origin).Dot(dir); float dist2 = (closest - centre).LengthSquared(); hits.push_back(std::make_pair(dist2, ent)); } // Sort hits by distance std::sort(hits.begin(), hits.end()); // lexicographic comparison // Extract the entity IDs std::vector<entity_id_t> hitEnts; hitEnts.reserve(hits.size()); for (size_t i = 0; i < hits.size(); ++i) hitEnts.push_back(hits[i].second); return hitEnts; }
void ActorViewerImpl::UpdatePropList() { Props.clear(); CmpPtr<ICmpVisual> cmpVisual(Simulation2, Entity); if (cmpVisual) { CUnit* unit = cmpVisual->GetUnit(); if (unit) { CModelAbstract& modelAbstract = unit->GetModel(); UpdatePropListRecursive(&modelAbstract); } } }
void ActorViewer::SetActor(const CStrW& name, const CStrW& animation, player_id_t playerID) { bool needsAnimReload = false; CStrW id = name; // Recreate the entity, if we don't have one or if the new one is different if (m.Entity == INVALID_ENTITY || id != m.CurrentUnitID) { // Delete the old entity (if any) if (m.Entity != INVALID_ENTITY) { m.Simulation2.DestroyEntity(m.Entity); m.Simulation2.FlushDestroyedEntities(); m.Entity = INVALID_ENTITY; } // Clear particles associated with deleted entity g_Renderer.GetParticleManager().ClearUnattachedEmitters(); // If there's no actor to display, return with nothing loaded if (id.empty()) return; m.Entity = m.Simulation2.AddEntity(L"preview|" + id); if (m.Entity == INVALID_ENTITY) return; CmpPtr<ICmpPosition> cmpPosition(m.Simulation2, m.Entity); if (cmpPosition) { ssize_t c = TERRAIN_TILE_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2; cmpPosition->JumpTo(entity_pos_t::FromInt(c), entity_pos_t::FromInt(c)); cmpPosition->SetYRotation(entity_angle_t::Pi()); } CmpPtr<ICmpOwnership> cmpOwnership(m.Simulation2, m.Entity); if (cmpOwnership) cmpOwnership->SetOwner(playerID); needsAnimReload = true; } if (animation != m.CurrentUnitAnim) needsAnimReload = true; if (needsAnimReload) { CStr anim = animation.ToUTF8().LowerCase(); // Emulate the typical simulation animation behaviour float speed; float repeattime = 0.f; if (anim == "walk") { CmpPtr<ICmpUnitMotion> cmpUnitMotion(m.Simulation2, m.Entity); if (cmpUnitMotion) speed = cmpUnitMotion->GetWalkSpeed().ToFloat(); else speed = 7.f; // typical unit speed m.CurrentSpeed = speed; } else if (anim == "run") { CmpPtr<ICmpUnitMotion> cmpUnitMotion(m.Simulation2, m.Entity); if (cmpUnitMotion) speed = cmpUnitMotion->GetRunSpeed().ToFloat(); else speed = 12.f; // typical unit speed m.CurrentSpeed = speed; } else if (anim == "melee") { speed = 1.f; // speed will be ignored if we have a repeattime m.CurrentSpeed = 0.f; CStr code = "var cmp = Engine.QueryInterface("+CStr::FromUInt(m.Entity)+", IID_Attack); " + "if (cmp) cmp.GetTimers(cmp.GetBestAttack()).repeat; else 0;"; m.Simulation2.GetScriptInterface().Eval(code.c_str(), repeattime); } else { // Play the animation at normal speed, but movement speed is zero speed = 1.f; m.CurrentSpeed = 0.f; } CStr sound; if (anim == "melee") sound = "attack"; else if (anim == "build") sound = "build"; else if (anim.Find("gather_") == 0) sound = anim; std::wstring soundgroup; if (!sound.empty()) { CStr code = "var cmp = Engine.QueryInterface("+CStr::FromUInt(m.Entity)+", IID_Sound); " + "if (cmp) cmp.GetSoundGroup('"+sound+"'); else '';"; m.Simulation2.GetScriptInterface().Eval(code.c_str(), soundgroup); } CmpPtr<ICmpVisual> cmpVisual(m.Simulation2, m.Entity); if (cmpVisual) { // TODO: SetEntitySelection(anim) cmpVisual->SelectAnimation(anim, false, fixed::FromFloat(speed), soundgroup); if (repeattime) cmpVisual->SetAnimationSyncRepeat(fixed::FromFloat(repeattime)); } // update prop list for new entity/animation (relies on needsAnimReload also getting called for entire entity changes) m.UpdatePropList(); } m.CurrentUnitID = id; m.CurrentUnitAnim = animation; }
std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables, int range) { PROFILE2("PickEntitiesAtPoint"); CVector3D origin, dir; camera.BuildCameraRay(screenX, screenY, origin, dir); CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY); ENSURE(cmpRangeManager); /* We try to approximate where the mouse is hovering by drawing a ray from * the center of the camera and through the mouse then taking the position * at which the ray intersects the terrain. */ // TODO: Do this smarter without being slow. CVector3D pos3d = camera.GetWorldCoordinates(screenX, screenY, true); // Change the position to 2D by removing the terrain height. CFixedVector2D pos(fixed::FromFloat(pos3d.X), fixed::FromFloat(pos3d.Z)); // Get a rough group of entities using our approximated origin. SpatialQueryArray ents; cmpRangeManager->GetSubdivision()->GetNear(ents, pos, entity_pos_t::FromInt(range)); // Filter for relevent entities and calculate precise distances. std::vector<std::pair<float, entity_id_t> > hits; // (dist^2, entity) pairs for (int i = 0; i < ents.size(); ++i) { CmpPtr<ICmpSelectable> cmpSelectable(simulation, ents[i]); if (!cmpSelectable) continue; CEntityHandle handle = cmpSelectable->GetEntityHandle(); // Check if this entity is only selectable in Atlas if (!allowEditorSelectables && cmpSelectable->IsEditorOnly()) continue; // Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld) if (cmpRangeManager->GetLosVisibility(handle, player) == ICmpRangeManager::VIS_HIDDEN) continue; CmpPtr<ICmpVisual> cmpVisual(handle); if (!cmpVisual) continue; CVector3D center; float tmin, tmax; CBoundingBoxOriented selectionBox = cmpVisual->GetSelectionBox(); if (selectionBox.IsEmpty()) { if (!allowEditorSelectables) continue; // Fall back to using old AABB selection method for decals // see: http://trac.wildfiregames.com/ticket/1032 CBoundingBoxAligned aABBox = cmpVisual->GetBounds(); if (aABBox.IsEmpty()) continue; if (!aABBox.RayIntersect(origin, dir, tmin, tmax)) continue; aABBox.GetCentre(center); } else { if (!selectionBox.RayIntersect(origin, dir, tmin, tmax)) continue; center = selectionBox.m_Center; } // Find the perpendicular distance from the object's centre to the picker ray float dist2; CVector3D closest = origin + dir * (center - origin).Dot(dir); dist2 = (closest - center).LengthSquared(); hits.push_back(std::make_pair(dist2, ents[i])); } // Sort hits by distance std::sort(hits.begin(), hits.end()); // lexicographic comparison // Extract the entity IDs std::vector<entity_id_t> hitEnts; hitEnts.reserve(hits.size()); for (size_t i = 0; i < hits.size(); ++i) hitEnts.push_back(hits[i].second); return hitEnts; }
void ActorViewer::Render() { m.Terrain.MakeDirty(RENDERDATA_UPDATE_COLOR); g_Renderer.SetClearColor(m.Background); // Disable shadows locally (avoid clobbering global state) bool oldShadows = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWS); g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, m.ShadowsEnabled); bool oldSky = g_Renderer.GetSkyManager()->m_RenderSky; g_Renderer.GetSkyManager()->m_RenderSky = false; bool oldWater = g_Renderer.GetWaterManager()->m_RenderWater; g_Renderer.GetWaterManager()->m_RenderWater = false; g_Renderer.BeginFrame(); // Find the centre of the interesting region, in the middle of the patch // and half way up the model (assuming there is one) CVector3D centre; CmpPtr<ICmpVisual> cmpVisual(m.Simulation2, m.Entity); if (!cmpVisual.null()) cmpVisual->GetBounds().GetCentre(centre); else centre.Y = 0.f; centre.X = centre.Z = CELL_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2; CCamera camera = View::GetView_Actor()->GetCamera(); camera.m_Orientation.Translate(centre.X, centre.Y, centre.Z); camera.UpdateFrustum(); g_Renderer.SetSceneCamera(camera, camera); g_Renderer.RenderScene(m); // .... glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glPushAttrib(GL_ENABLE_BIT); glEnable(GL_TEXTURE_2D); glDisable(GL_CULL_FACE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glDisable(GL_ALPHA_TEST); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable(GL_TEXTURE_2D); g_ProfileViewer.RenderProfile(); glPopAttrib(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); g_Renderer.EndFrame(); // Restore the old renderer state g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, oldShadows); g_Renderer.GetSkyManager()->m_RenderSky = oldSky; g_Renderer.GetWaterManager()->m_RenderWater = oldWater; ogl_WarnIfError(); }
std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables) { CVector3D origin, dir; camera.BuildCameraRay(screenX, screenY, origin, dir); CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY); ENSURE(cmpRangeManager); std::vector<std::pair<float, entity_id_t> > hits; // (dist^2, entity) pairs const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable); for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it) { entity_id_t ent = it->first; // Check if this entity is only selectable in Atlas if (!allowEditorSelectables && static_cast<ICmpSelectable*>(it->second)->IsEditorOnly()) continue; // Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld) if (cmpRangeManager->GetLosVisibility(ent, player) == ICmpRangeManager::VIS_HIDDEN) continue; CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent); if (!cmpVisual) continue; CVector3D center; float tmin, tmax; CBoundingBoxOriented selectionBox = cmpVisual->GetSelectionBox(); if (selectionBox.IsEmpty()) { if (!allowEditorSelectables) continue; // Fall back to using old AABB selection method for decals // see: http://trac.wildfiregames.com/ticket/1032 CBoundingBoxAligned aABBox = cmpVisual->GetBounds(); if (aABBox.IsEmpty()) continue; if (!aABBox.RayIntersect(origin, dir, tmin, tmax)) continue; aABBox.GetCentre(center); } else { if (!selectionBox.RayIntersect(origin, dir, tmin, tmax)) continue; center = selectionBox.m_Center; } // Find the perpendicular distance from the object's centre to the picker ray float dist2; CVector3D closest = origin + dir * (center - origin).Dot(dir); dist2 = (closest - center).LengthSquared(); hits.push_back(std::make_pair(dist2, ent)); } // Sort hits by distance std::sort(hits.begin(), hits.end()); // lexicographic comparison // Extract the entity IDs std::vector<entity_id_t> hitEnts; hitEnts.reserve(hits.size()); for (size_t i = 0; i < hits.size(); ++i) hitEnts.push_back(hits[i].second); return hitEnts; }
void CMapWriter::WriteXML(const VfsPath& filename, WaterManager* pWaterMan, SkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema, CPostprocManager* pPostproc, CSimulation2* pSimulation2) { XML_Start(); { XML_Element("Scenario"); XML_Attribute("version", (int)FILE_VERSION); ENSURE(pSimulation2); CSimulation2& sim = *pSimulation2; if (!sim.GetStartupScript().empty()) { XML_Element("Script"); XML_CDATA(sim.GetStartupScript().c_str()); } { XML_Element("Environment"); XML_Setting("SkySet", pSkyMan->GetSkySet()); { XML_Element("SunColor"); XML_Attribute("r", pLightEnv->m_SunColor.X); // yes, it's X/Y/Z... XML_Attribute("g", pLightEnv->m_SunColor.Y); XML_Attribute("b", pLightEnv->m_SunColor.Z); } { XML_Element("SunElevation"); XML_Attribute("angle", pLightEnv->m_Elevation); } { XML_Element("SunRotation"); XML_Attribute("angle", pLightEnv->m_Rotation); } { XML_Element("TerrainAmbientColor"); XML_Attribute("r", pLightEnv->m_TerrainAmbientColor.X); XML_Attribute("g", pLightEnv->m_TerrainAmbientColor.Y); XML_Attribute("b", pLightEnv->m_TerrainAmbientColor.Z); } { XML_Element("UnitsAmbientColor"); XML_Attribute("r", pLightEnv->m_UnitsAmbientColor.X); XML_Attribute("g", pLightEnv->m_UnitsAmbientColor.Y); XML_Attribute("b", pLightEnv->m_UnitsAmbientColor.Z); } { XML_Element("Fog"); XML_Setting("FogFactor", pLightEnv->m_FogFactor); XML_Setting("FogThickness", pLightEnv->m_FogMax); { XML_Element("FogColor"); XML_Attribute("r", pLightEnv->m_FogColor.X); XML_Attribute("g", pLightEnv->m_FogColor.Y); XML_Attribute("b", pLightEnv->m_FogColor.Z); } } { XML_Element("Water"); { XML_Element("WaterBody"); CmpPtr<ICmpWaterManager> cmpWaterManager(sim, SYSTEM_ENTITY); ENSURE(cmpWaterManager); XML_Setting("Type", pWaterMan->m_WaterType); { XML_Element("Color"); XML_Attribute("r", pWaterMan->m_WaterColor.r); XML_Attribute("g", pWaterMan->m_WaterColor.g); XML_Attribute("b", pWaterMan->m_WaterColor.b); } { XML_Element("Tint"); XML_Attribute("r", pWaterMan->m_WaterTint.r); XML_Attribute("g", pWaterMan->m_WaterTint.g); XML_Attribute("b", pWaterMan->m_WaterTint.b); } XML_Setting("Height", cmpWaterManager->GetExactWaterLevel(0, 0)); XML_Setting("Waviness", pWaterMan->m_Waviness); XML_Setting("Murkiness", pWaterMan->m_Murkiness); XML_Setting("WindAngle", pWaterMan->m_WindAngle); } } { XML_Element("Postproc"); { XML_Setting("Brightness", pLightEnv->m_Brightness); XML_Setting("Contrast", pLightEnv->m_Contrast); XML_Setting("Saturation", pLightEnv->m_Saturation); XML_Setting("Bloom", pLightEnv->m_Bloom); XML_Setting("PostEffect", pPostproc->GetPostEffect()); } } } { XML_Element("Camera"); { XML_Element("Position"); CVector3D pos = pCamera->m_Orientation.GetTranslation(); XML_Attribute("x", pos.X); XML_Attribute("y", pos.Y); XML_Attribute("z", pos.Z); } CVector3D in = pCamera->m_Orientation.GetIn(); // Convert to spherical coordinates float rotation = atan2(in.X, in.Z); float declination = atan2(sqrt(in.X*in.X + in.Z*in.Z), in.Y) - (float)M_PI/2; { XML_Element("Rotation"); XML_Attribute("angle", rotation); } { XML_Element("Declination"); XML_Attribute("angle", declination); } } { std::string settings = sim.GetMapSettingsString(); if (!settings.empty()) { XML_Element("ScriptSettings"); XML_CDATA(("\n" + settings + "\n").c_str()); } } { XML_Element("Entities"); CmpPtr<ICmpTemplateManager> cmpTemplateManager(sim, SYSTEM_ENTITY); ENSURE(cmpTemplateManager); // This will probably need to be changed in the future, but for now we'll // just save all entities that have a position CSimulation2::InterfaceList ents = sim.GetEntitiesWithInterface(IID_Position); for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it) { entity_id_t ent = it->first; // Don't save local entities (placement previews etc) if (ENTITY_IS_LOCAL(ent)) continue; XML_Element("Entity"); XML_Attribute("uid", ent); XML_Setting("Template", cmpTemplateManager->GetCurrentTemplateName(ent)); CmpPtr<ICmpOwnership> cmpOwnership(sim, ent); if (cmpOwnership) XML_Setting("Player", (int)cmpOwnership->GetOwner()); CmpPtr<ICmpPosition> cmpPosition(sim, ent); if (cmpPosition) { CFixedVector3D pos; if (cmpPosition->IsInWorld()) pos = cmpPosition->GetPosition(); CFixedVector3D rot = cmpPosition->GetRotation(); { XML_Element("Position"); XML_Attribute("x", pos.X); XML_Attribute("z", pos.Z); // TODO: height offset etc } { XML_Element("Orientation"); XML_Attribute("y", rot.Y); // TODO: X, Z maybe } } CmpPtr<ICmpObstruction> cmpObstruction(sim, ent); if (cmpObstruction) { // TODO: Currently only necessary because Atlas // does not set up control groups for its walls. cmpObstruction->ResolveFoundationCollisions(); entity_id_t group = cmpObstruction->GetControlGroup(); entity_id_t group2 = cmpObstruction->GetControlGroup2(); // Don't waste space writing the default control groups. if (group != ent || group2 != INVALID_ENTITY) { XML_Element("Obstruction"); if (group != ent) XML_Attribute("group", group); if (group2 != INVALID_ENTITY) XML_Attribute("group2", group2); } } CmpPtr<ICmpVisual> cmpVisual(sim, ent); if (cmpVisual) { u32 seed = cmpVisual->GetActorSeed(); if (seed != (u32)ent) { XML_Element("Actor"); XML_Attribute("seed", seed); } // TODO: variation/selection strings } } } const std::map<CStrW, CCinemaPath>& paths = pCinema->GetAllPaths(); std::map<CStrW, CCinemaPath>::const_iterator it = paths.begin(); { XML_Element("Paths"); for ( ; it != paths.end(); ++it ) { fixed timescale = it->second.GetTimescale(); const std::vector<SplineData>& nodes = it->second.GetAllNodes(); const std::vector<SplineData>& target_nodes = it->second.GetTargetSpline().GetAllNodes(); const CCinemaData* data = it->second.GetData(); XML_Element("Path"); XML_Attribute("name", data->m_Name); XML_Attribute("timescale", timescale); XML_Attribute("orientation", data->m_Orientation); XML_Attribute("mode", data->m_Mode); XML_Attribute("style", data->m_Style); fixed last_target = fixed::Zero(); for (size_t i = 0, j = 0; i < nodes.size(); ++i) { XML_Element("Node"); fixed distance = i > 0 ? nodes[i - 1].Distance : fixed::Zero(); last_target += distance; XML_Attribute("deltatime", distance); { XML_Element("Position"); XML_Attribute("x", nodes[i].Position.X); XML_Attribute("y", nodes[i].Position.Y); XML_Attribute("z", nodes[i].Position.Z); } { XML_Element("Rotation"); XML_Attribute("x", nodes[i].Rotation.X); XML_Attribute("y", nodes[i].Rotation.Y); XML_Attribute("z", nodes[i].Rotation.Z); } if (j >= target_nodes.size()) continue; fixed target_distance = j > 0 ? target_nodes[j - 1].Distance : fixed::Zero(); if (target_distance > last_target) continue; { XML_Element("Target"); XML_Attribute("x", target_nodes[j].Position.X); XML_Attribute("y", target_nodes[j].Position.Y); XML_Attribute("z", target_nodes[j].Position.Z); } last_target = fixed::Zero(); ++j; } } } } if (!XML_StoreVFS(g_VFS, filename)) LOGERROR("Failed to write map '%s'", filename.string8()); }
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) { switch (msg.GetType()) { case MT_Interpolate: { if (!m_Active) break; const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg); CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); if (cmpPosition.null() || !cmpPosition->IsInWorld()) { // If there's no position (this usually shouldn't happen), destroy the unit immediately GetSimContext().GetComponentManager().DestroyComponentsSoon(GetEntityId()); break; } // Compute the depth the first time this is called // (This is a bit of an ugly place to do it but at least we'll be sure // the actor component was loaded already) if (m_TotalSinkDepth < 0.f) { m_TotalSinkDepth = 1.f; // minimum so we always sink at least a little CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), GetEntityId()); if (!cmpVisual.null()) { CBound bound = cmpVisual->GetBounds(); m_TotalSinkDepth = std::max(m_TotalSinkDepth, bound[1].Y - bound[0].Y); } // If this is a floating unit, we want it to sink all the way under the terrain, // so find the difference between its current position and the terrain CFixedVector3D pos = cmpPosition->GetPosition(); CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY); if (!cmpTerrain.null()) { fixed ground = cmpTerrain->GetGroundLevel(pos.X, pos.Z); m_TotalSinkDepth += std::max(0.f, (pos.Y - ground).ToFloat()); } } m_CurrentTime += msgData.frameTime; if (m_CurrentTime > m_DelayTime) { float t = m_CurrentTime - m_DelayTime; float depth = (m_SinkRate * t) + (m_SinkAccel * t * t); cmpPosition->SetHeightOffset(entity_pos_t::FromFloat(-depth)); if (depth > m_TotalSinkDepth) GetSimContext().GetComponentManager().DestroyComponentsSoon(GetEntityId()); } break; } } }
std::vector<entity_id_t> EntitySelection::PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, player_id_t owner, bool includeOffScreen, bool matchRank, bool allowEditorSelectables, bool allowFoundations) { PROFILE2("PickSimilarEntities"); CmpPtr<ICmpTemplateManager> cmpTemplateManager(simulation, SYSTEM_ENTITY); CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY); std::vector<entity_id_t> hitEnts; const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable); for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it) { entity_id_t ent = it->first; CEntityHandle handle = it->second->GetEntityHandle(); // Check if this entity is only selectable in Atlas if (static_cast<ICmpSelectable*>(it->second)->IsEditorOnly() && !allowEditorSelectables) continue; if (matchRank) { // Exact template name matching, optionally also allowing foundations std::string curTemplateName = cmpTemplateManager->GetCurrentTemplateName(ent); bool matches = (curTemplateName == templateName || (allowFoundations && curTemplateName.substr(0, 11) == "foundation|" && curTemplateName.substr(11) == templateName)); if (!matches) continue; } // Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld) // In this case, the checking is done to avoid selecting garrisoned units if (cmpRangeManager->GetLosVisibility(handle, owner) == ICmpRangeManager::VIS_HIDDEN) continue; // Ignore entities not owned by 'owner' CmpPtr<ICmpOwnership> cmpOwnership(simulation.GetSimContext(), ent); if (owner != INVALID_PLAYER && (!cmpOwnership || cmpOwnership->GetOwner() != owner)) continue; // Ignore off screen entities if (!includeOffScreen) { // Find the current interpolated model position. CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent); if (!cmpVisual) continue; CVector3D position = cmpVisual->GetPosition(); // Reject if it's not on-screen (e.g. it's behind the camera) if (!camera.GetFrustum().IsPointVisible(position)) continue; } if (!matchRank) { // Match by selection group name // (This is relatively expensive since it involves script calls, so do it after all other tests) CmpPtr<ICmpIdentity> cmpIdentity(simulation.GetSimContext(), ent); if (!cmpIdentity || cmpIdentity->GetSelectionGroupName() != templateName) continue; } hitEnts.push_back(ent); } return hitEnts; }
// Simplistic implementation of the Scene interface virtual void EnumerateObjects(const CFrustum& frustum, SceneCollector* c) { if (GroundEnabled) { for (ssize_t pj = 0; pj < Terrain.GetPatchesPerSide(); ++pj) for (ssize_t pi = 0; pi < Terrain.GetPatchesPerSide(); ++pi) c->Submit(Terrain.GetPatch(pi, pj)); } CmpPtr<ICmpVisual> cmpVisual(Simulation2, Entity); if (cmpVisual) { // add selection box outlines manually if (SelectionBoxEnabled) { SelectionBoxOverlay.m_Color = CColor(35/255.f, 86/255.f, 188/255.f, .75f); // pretty blue SelectionBoxOverlay.m_Thickness = 2; SimRender::ConstructBoxOutline(cmpVisual->GetSelectionBox(), SelectionBoxOverlay); c->Submit(&SelectionBoxOverlay); } // add origin axis thingy if (AxesMarkerEnabled) { CMatrix3D worldSpaceAxes; // offset from the ground a little bit to prevent fighting with the floor texture (also note: SetTranslation // sets the identity 3x3 transformation matrix, which are the world axes) worldSpaceAxes.SetTranslation(cmpVisual->GetPosition() + CVector3D(0, 0.02f, 0)); SimRender::ConstructAxesMarker(worldSpaceAxes, AxesMarkerOverlays[0], AxesMarkerOverlays[1], AxesMarkerOverlays[2]); c->Submit(&AxesMarkerOverlays[0]); c->Submit(&AxesMarkerOverlays[1]); c->Submit(&AxesMarkerOverlays[2]); } // add prop point overlays if (PropPointsMode > 0 && Props.size() > 0) { PropPointOverlays.clear(); // doesn't clear capacity, but should be ok since the number of prop points is usually pretty limited for (size_t i = 0; i < Props.size(); ++i) { CModel::Prop& prop = Props[i]; if (prop.m_Model) // should always be the case { // prop point positions are automatically updated during animations etc. by CModel::ValidatePosition const CMatrix3D& propCoordSystem = prop.m_Model->GetTransform(); SOverlayLine pointGimbal; pointGimbal.m_Color = CColor(1.f, 0.f, 1.f, 1.f); SimRender::ConstructGimbal(propCoordSystem.GetTranslation(), 0.05f, pointGimbal); PropPointOverlays.push_back(pointGimbal); if (PropPointsMode > 1) { // scale the prop axes coord system down a bit to distinguish them from the main world-space axes markers CMatrix3D displayCoordSystem = propCoordSystem; displayCoordSystem.Scale(0.5f, 0.5f, 0.5f); // revert translation scaling displayCoordSystem._14 = propCoordSystem._14; displayCoordSystem._24 = propCoordSystem._24; displayCoordSystem._34 = propCoordSystem._34; // construct an XYZ axes marker for the prop's coordinate system SOverlayLine xAxis, yAxis, zAxis; SimRender::ConstructAxesMarker(displayCoordSystem, xAxis, yAxis, zAxis); PropPointOverlays.push_back(xAxis); PropPointOverlays.push_back(yAxis); PropPointOverlays.push_back(zAxis); } } } for (size_t i = 0; i < PropPointOverlays.size(); ++i) { c->Submit(&PropPointOverlays[i]); } } } // send a RenderSubmit message so the components can submit their visuals to the renderer Simulation2.RenderSubmit(*c, frustum, false); }
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) { switch (msg.GetType()) { case MT_Interpolate: { if (!m_Active) break; const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg); CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); if (!cmpPosition || !cmpPosition->IsInWorld()) { // If there's no position (this usually shouldn't happen), destroy the unit immediately GetSimContext().GetComponentManager().DestroyComponentsSoon(GetEntityId()); break; } // Compute the depth the first time this is called // (This is a bit of an ugly place to do it but at least we'll be sure // the actor component was loaded already) if (m_TotalSinkDepth < 0.f) { m_TotalSinkDepth = 1.f; // minimum so we always sink at least a little CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), GetEntityId()); if (cmpVisual) { CBoundingBoxAligned bound = cmpVisual->GetBounds(); m_TotalSinkDepth = std::max(m_TotalSinkDepth, bound[1].Y - bound[0].Y); } // If this is a floating unit, we want it to sink all the way under the terrain, // so find the difference between its current position and the terrain CFixedVector3D pos = cmpPosition->GetPosition(); CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY); if (cmpTerrain) { fixed ground = cmpTerrain->GetGroundLevel(pos.X, pos.Z); m_TotalSinkDepth += std::max(0.f, (pos.Y - ground).ToFloat()); } // Sink it further down if it sinks like a ship, as it will rotate. if (m_ShipSink) m_TotalSinkDepth += 10.f; // probably 0 in both cases but we'll remember it anyway. m_InitialXRotation = cmpPosition->GetRotation().X; m_InitialZRotation = cmpPosition->GetRotation().Z; } m_CurrentTime += msgData.deltaSimTime; if (m_CurrentTime >= m_DelayTime) { float t = m_CurrentTime - m_DelayTime; float depth = (m_SinkRate * t) + (m_SinkAccel * t * t); if (m_ShipSink) { // exponential sinking with tilting float tilt_time = t > 5.f ? 5.f : t; float tiltSink = tilt_time * tilt_time / 5.f; entity_pos_t RotX = entity_pos_t::FromFloat(((m_InitialXRotation.ToFloat() * (5.f - tiltSink)) + (1.5f * tiltSink)) / 5.f); entity_pos_t RotZ = entity_pos_t::FromFloat(((m_InitialZRotation.ToFloat() * (3.f - tilt_time)) + (-0.3f * tilt_time)) / 3.f); cmpPosition->SetXZRotation(RotX,RotZ); depth = m_SinkRate * (exp(t - 1.f) - 0.54881163609f) + (m_SinkAccel * exp(t - 4.f) - 0.01831563888f); if (depth < 0.f) depth = 0.f; } cmpPosition->SetHeightOffset(entity_pos_t::FromFloat(-depth)); if (depth > m_TotalSinkDepth) GetSimContext().GetComponentManager().DestroyComponentsSoon(GetEntityId()); } break; } } }