Ticket #2358: fast-select.diff
File fast-select.diff, 19.5 KB (added by , 10 years ago) |
---|
-
binaries/data/mods/public/gui/session/input.js
36 36 var mouseX = 0; 37 37 var mouseY = 0; 38 38 var mouseIsOverObject = false; 39 var SELECTION_SEARCH_RANGE = 20; 39 40 40 41 // Number of pixels the mouse can move before the action is considered a drag 41 42 var maxDragDelta = 4; … … 495 496 var targets = []; 496 497 var target = undefined; 497 498 if (!fromMinimap) 498 targets = Engine.PickEntitiesAtPoint(x, y );499 targets = Engine.PickEntitiesAtPoint(x, y, SELECTION_SEARCH_RANGE); 499 500 500 501 if (targets.length) 501 502 target = targets[0]; … … 1108 1109 { 1109 1110 case "mousemotion": 1110 1111 // Highlight the first hovered entity (if any) 1111 var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y );1112 var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y, SELECTION_SEARCH_RANGE); 1112 1113 if (ents.length) 1113 1114 g_Selection.setHighlightList([ents[0]]); 1114 1115 else … … 1162 1163 { 1163 1164 case "mousemotion": 1164 1165 // Highlight the first hovered entity (if any) 1165 var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y );1166 var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y, SELECTION_SEARCH_RANGE); 1166 1167 if (ents.length) 1167 1168 g_Selection.setHighlightList([ents[0]]); 1168 1169 else … … 1212 1213 return false; 1213 1214 } 1214 1215 1215 var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y );1216 var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y, SELECTION_SEARCH_RANGE); 1216 1217 g_Selection.setHighlightList(ents); 1217 1218 return false; 1218 1219 1219 1220 case "mousebuttonup": 1220 1221 if (ev.button == SDL_BUTTON_LEFT) 1221 1222 { 1222 var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y );1223 var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y, SELECTION_SEARCH_RANGE); 1223 1224 if (!ents.length) 1224 1225 { 1225 1226 if (!Engine.HotkeyIsPressed("selection.add") && !Engine.HotkeyIsPressed("selection.remove")) -
source/gui/scripting/ScriptFunctions.cpp
148 148 cmpCommandQueue->PostNetworkCommand(cmd2); 149 149 } 150 150 151 std::vector<entity_id_t> PickEntitiesAtPoint(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int x, int y )151 std::vector<entity_id_t> PickEntitiesAtPoint(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int x, int y, int range) 152 152 { 153 return EntitySelection::PickEntitiesAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y, g_Game->GetPlayerID(), false );153 return EntitySelection::PickEntitiesAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y, g_Game->GetPlayerID(), false, range); 154 154 } 155 155 156 156 std::vector<entity_id_t> PickFriendlyEntitiesInRect(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int x0, int y0, int x1, int y1, int player) … … 804 804 scriptInterface.RegisterFunction<void, CScriptVal, &PostNetworkCommand>("PostNetworkCommand"); 805 805 806 806 // Entity picking 807 scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, int, &PickEntitiesAtPoint>("PickEntitiesAtPoint");807 scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, int, int, &PickEntitiesAtPoint>("PickEntitiesAtPoint"); 808 808 scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, int, int, int, int, &PickFriendlyEntitiesInRect>("PickFriendlyEntitiesInRect"); 809 809 scriptInterface.RegisterFunction<std::vector<entity_id_t>, int, &PickFriendlyEntitiesOnScreen>("PickFriendlyEntitiesOnScreen"); 810 810 scriptInterface.RegisterFunction<std::vector<entity_id_t>, std::string, bool, bool, bool, &PickSimilarFriendlyEntities>("PickSimilarFriendlyEntities"); -
source/simulation2/components/CCmpRangeManager.cpp
97 97 * Checks whether v is in a parabolic range of (0,0,0) 98 98 * The highest point of the paraboloid is (0,range/2,0) 99 99 * and the circle of distance 'range' around (0,0,0) on height y=0 is part of the paraboloid 100 * 100 * 101 101 * Avoids sqrting and overflowing. 102 102 */ 103 static bool InParabolicRange(CFixedVector3D v, fixed range) 103 static bool InParabolicRange(CFixedVector3D v, fixed range) 104 104 { 105 105 i32 x = v.X.GetInternalValue(); // abs(x) <= 2^31 106 106 i32 z = v.Z.GetInternalValue(); 107 107 u64 xx = (u64)FIXED_MUL_I64_I32_I32(x, x); // xx <= 2^62 108 108 u64 zz = (u64)FIXED_MUL_I64_I32_I32(z, z); 109 109 i64 d2 = (xx + zz) >> 1; // d2 <= 2^62 (no overflow) 110 110 111 111 i32 y = v.Y.GetInternalValue(); 112 112 i32 c = range.GetInternalValue(); 113 113 i32 c_2 = c >> 1; … … 327 327 // will get confused when trying to run from enemies 328 328 m_LosRevealAll.resize(MAX_LOS_PLAYER_ID+2,false); 329 329 m_SharedLosMasks.resize(MAX_LOS_PLAYER_ID+2,0); 330 330 331 331 m_LosCircular = false; 332 332 m_TerrainVerticesPerSide = 0; 333 333 … … 601 601 debug_warn(L"inconsistent subdivs"); 602 602 } 603 603 604 SpatialSubdivision * GetSubdivision() 605 { 606 return & m_Subdivision; 607 } 608 604 609 // Reinitialise subdivisions and LOS data, based on entity data 605 610 void ResetDerivedData(bool skipLosState) 606 611 { … … 844 849 if (!query.enabled) 845 850 continue; 846 851 847 CmpPtr<ICmpPosition> cmpSourcePosition(query.source); 852 CmpPtr<ICmpPosition> cmpSourcePosition(query.source); 848 853 if (!cmpSourcePosition || !cmpSourcePosition->IsInWorld()) 849 854 continue; 850 855 … … 858 863 removed.clear(); 859 864 // Return the 'added' list sorted by distance from the entity 860 865 // (Don't bother sorting 'removed' because they might not even have positions or exist any more) 861 std::set_difference(results.begin(), results.end(), query.lastMatch.begin(), query.lastMatch.end(), 866 std::set_difference(results.begin(), results.end(), query.lastMatch.begin(), query.lastMatch.end(), 862 867 std::back_inserter(added)); 863 std::set_difference(query.lastMatch.begin(), query.lastMatch.end(), results.begin(), results.end(), 868 std::set_difference(query.lastMatch.begin(), query.lastMatch.end(), results.begin(), results.end(), 864 869 std::back_inserter(removed)); 865 870 if (added.empty() && removed.empty()) 866 871 continue; … … 927 932 } 928 933 } 929 934 // Not the entire world, so check a parabolic range, or a regular range 930 else if (q.parabolic) 935 else if (q.parabolic) 931 936 { 932 937 // elevationBonus is part of the 3D position, as the source is really that much heigher 933 938 CmpPtr<ICmpPosition> cmpSourcePosition(q.source); … … 944 949 945 950 if (!TestEntityQuery(q, it->first, it->second)) 946 951 continue; 947 952 948 953 CmpPtr<ICmpPosition> cmpSecondPosition(GetSimContext(), ents[i]); 949 954 if (!cmpSecondPosition || !cmpSecondPosition->IsInWorld()) 950 955 continue; … … 952 957 953 958 // Restrict based on precise distance 954 959 if (!InParabolicRange( 955 CFixedVector3D(it->second.x, secondPosition.Y, it->second.z) 956 - pos3d, 960 CFixedVector3D(it->second.x, secondPosition.Y, it->second.z) 961 - pos3d, 957 962 q.maxRange)) 958 963 continue; 959 964 … … 968 973 } 969 974 } 970 975 // check a regular range (i.e. not the entire world, and not parabolic) 971 else 976 else 972 977 { 973 978 // Get a quick list of entities that are potentially in range 974 979 SpatialQueryArray ents; 975 980 m_Subdivision.GetNear(ents, pos, q.maxRange); 976 981 977 982 for (int i = 0; i < ents.size(); ++i) 978 983 { 979 984 EntityMap<EntityData>::const_iterator it = m_EntityData.find(ents[i]); … … 1002 1007 virtual entity_pos_t GetElevationAdaptedRange(CFixedVector3D pos, CFixedVector3D rot, entity_pos_t range, entity_pos_t elevationBonus, entity_pos_t angle) 1003 1008 { 1004 1009 entity_pos_t r = entity_pos_t::Zero() ; 1005 1010 1006 1011 pos.Y += elevationBonus; 1007 1012 entity_pos_t orientation = rot.Y; 1008 1013 … … 1022 1027 { 1023 1028 r = r + CFixedVector2D(coords[2*i],coords[2*i+1]).Length() / part; 1024 1029 } 1025 1030 1026 1031 return r; 1027 1032 1028 1033 } 1029 1034 1030 virtual std::vector<entity_pos_t> getParabolicRangeForm(CFixedVector3D pos, entity_pos_t maxRange, entity_pos_t cutoff, entity_pos_t minAngle, entity_pos_t maxAngle, int numberOfSteps) 1035 virtual std::vector<entity_pos_t> getParabolicRangeForm(CFixedVector3D pos, entity_pos_t maxRange, entity_pos_t cutoff, entity_pos_t minAngle, entity_pos_t maxAngle, int numberOfSteps) 1031 1036 { 1032 1037 1033 1038 // angle = 0 goes in the positive Z direction 1034 1039 entity_pos_t precision = entity_pos_t::FromInt((int)TERRAIN_TILE_SIZE)/8; 1035 1040 1036 1041 std::vector<entity_pos_t> r; 1037 1038 1042 1043 1039 1044 CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity()); 1040 1045 CmpPtr<ICmpWaterManager> cmpWaterManager(GetSystemEntity()); 1041 1046 entity_pos_t waterLevel = cmpWaterManager->GetWaterLevel(pos.X,pos.Z); 1042 1047 entity_pos_t thisHeight = pos.Y > waterLevel ? pos.Y : waterLevel; 1043 1048 1044 if (cmpTerrain) 1049 if (cmpTerrain) 1045 1050 { 1046 1051 for (int i = 0; i < numberOfSteps; i++) 1047 1052 { … … 1064 1069 r.push_back(maxVector.Y); 1065 1070 continue; 1066 1071 } 1067 1072 1068 1073 // Loop until vectors come close enough 1069 1074 while ((maxVector - minVector).CompareLength(precision) > 0) 1070 1075 { … … 1083 1088 minVector = newVector; 1084 1089 minDistance = newDistance; 1085 1090 } 1086 else 1091 else 1087 1092 { 1088 1093 // new vector is out parabolic range, so this is a new maxVector 1089 1094 maxVector = newVector; 1090 1095 maxDistance = newDistance; 1091 1096 } 1092 1097 1093 1098 } 1094 1099 r.push_back(maxVector.X); 1095 1100 r.push_back(maxVector.Y); 1096 1101 1097 1102 } 1098 1103 r.push_back(r[0]); 1099 r.push_back(r[1]); 1104 r.push_back(r[1]); 1100 1105 1101 1106 } 1102 1107 return r; 1103 1108 1104 1109 } 1105 1110 1106 1111 Query ConstructQuery(entity_id_t source, 1107 1112 entity_pos_t minRange, entity_pos_t maxRange, 1108 1113 const std::vector<int>& owners, int requiredInterface, u8 flagsMask) … … 1150 1155 return; 1151 1156 static CColor disabledRingColour(1, 0, 0, 1); // red 1152 1157 static CColor enabledRingColour(0, 1, 0, 1); // green 1153 static CColor subdivColour(0, 0, 1, 1); // blue 1158 static CColor subdivColour(0, 0, 1, 1); // blue 1154 1159 static CColor rayColour(1, 1, 0, 0.2f); 1155 1160 1156 1161 if (m_DebugOverlayDirty) … … 1173 1178 m_DebugOverlayLines.back().m_Color = (q.enabled ? enabledRingColour : disabledRingColour); 1174 1179 SimRender::ConstructCircleOnGround(GetSimContext(), pos.X.ToFloat(), pos.Y.ToFloat(), q.maxRange.ToFloat(), m_DebugOverlayLines.back(), true); 1175 1180 } 1176 else 1177 { 1181 else 1182 { 1178 1183 // elevation bonus is part of the 3D position. As if the unit is really that much higher 1179 CFixedVector3D pos = cmpSourcePosition->GetPosition(); 1184 CFixedVector3D pos = cmpSourcePosition->GetPosition(); 1180 1185 pos.Y += q.elevationBonus; 1181 1186 1182 1187 std::vector<entity_pos_t> coords; 1183 1188 1184 1189 // Get the outline from cache if possible 1185 1190 if (ParabolicRangesOutlines.find(q.source.GetId()) != ParabolicRangesOutlines.end()) 1186 1191 { 1187 1192 EntityParabolicRangeOutline e = ParabolicRangesOutlines[q.source.GetId()]; 1188 if (e.position == pos && e.range == q.maxRange) 1193 if (e.position == pos && e.range == q.maxRange) 1189 1194 { 1190 1195 // outline is cached correctly, use it 1191 coords = e.outline; 1196 coords = e.outline; 1192 1197 } 1193 1198 else 1194 1199 { 1195 // outline was cached, but important parameters changed 1200 // outline was cached, but important parameters changed 1196 1201 // (position, elevation, range) 1197 1202 // update it 1198 1203 coords = getParabolicRangeForm(pos,q.maxRange,q.maxRange*2, entity_pos_t::Zero(), entity_pos_t::FromFloat(2.0f*3.14f),70); … … 1202 1207 ParabolicRangesOutlines[q.source.GetId()] = e; 1203 1208 } 1204 1209 } 1205 else 1210 else 1206 1211 { 1207 // outline wasn't cached (first time you enable the range overlay 1212 // outline wasn't cached (first time you enable the range overlay 1208 1213 // or you created a new entiy) 1209 1214 // cache a new outline 1210 1215 coords = getParabolicRangeForm(pos,q.maxRange,q.maxRange*2, entity_pos_t::Zero(), entity_pos_t::FromFloat(2.0f*3.14f),70); … … 1215 1220 e.outline = coords; 1216 1221 ParabolicRangesOutlines[q.source.GetId()] = e; 1217 1222 } 1218 1223 1219 1224 CColor thiscolor = q.enabled ? enabledRingColour : disabledRingColour; 1220 1221 // draw the outline (piece by piece) 1225 1226 // draw the outline (piece by piece) 1222 1227 for (size_t i = 3; i < coords.size(); i += 2) 1223 1228 { 1224 1229 std::vector<float> c; … … 1268 1273 { 1269 1274 m_DebugOverlayLines.push_back(SOverlayLine()); 1270 1275 m_DebugOverlayLines.back().m_Color = subdivColour; 1271 1276 1272 1277 float xpos = x*divSize + divSize/2; 1273 1278 float zpos = y*divSize + divSize/2; 1274 1279 SimRender::ConstructSquareOnGround(GetSimContext(), xpos, zpos, divSize, divSize, 0.0f, -
source/simulation2/components/ICmpRangeManager.h
24 24 #include "simulation2/system/Interface.h" 25 25 #include "simulation2/helpers/Position.h" 26 26 #include "simulation2/helpers/Player.h" 27 #include "simulation2/helpers/Spatial.h" 27 28 28 29 #include "graphics/Terrain.h" // for TERRAIN_TILE_SIZE 29 30 … … 73 74 typedef u32 tag_t; 74 75 75 76 /** 77 * Access the spatial subdivision kept by the range manager. 78 * @return pointer to spatial subdivision structure. 79 */ 80 virtual SpatialSubdivision * GetSubdivision() = 0; 81 82 /** 76 83 * Set the bounds of the world. 77 84 * Entities should not be outside the bounds (else efficiency will suffer). 78 85 * @param x0,z0,x1,z1 Coordinates of the corners of the world … … 118 125 entity_pos_t minRange, entity_pos_t maxRange, std::vector<int> owners, int requiredInterface, u8 flags) = 0; 119 126 120 127 /** 121 * Construct an active query of a paraboloic form around the unit. 128 * Construct an active query of a paraboloic form around the unit. 122 129 * The query will be disabled by default. 123 130 * @param source the entity around which the range will be computed. 124 131 * @param minRange non-negative minimum horizontal distance in metres (inclusive). MinRange doesn't do parabolic checks. 125 * @param maxRange non-negative maximum distance in metres (inclusive) for units on the same elevation; 126 * or -1.0 to ignore distance. 132 * @param maxRange non-negative maximum distance in metres (inclusive) for units on the same elevation; 133 * or -1.0 to ignore distance. 127 134 * For units on a different elevation, a physical correct paraboloid with height=maxRange/2 above the unit is used to query them 128 135 * @param elevationBonus extra bonus so the source can be placed higher and shoot further 129 136 * @param owners list of player IDs that matching entities may have; -1 matches entities with no owner. -
source/simulation2/helpers/Selection.cpp
27 27 #include "simulation2/components/ICmpTemplateManager.h" 28 28 #include "simulation2/components/ICmpSelectable.h" 29 29 #include "simulation2/components/ICmpVisual.h" 30 #include "simulation2/helpers/Spatial.h" 30 31 #include "ps/CLogger.h" 32 #include "ps/Profiler2.h" 31 33 32 std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables )34 std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables, int range) 33 35 { 36 PROFILE2("PickEntitiesAtPoint"); 34 37 CVector3D origin, dir; 35 38 camera.BuildCameraRay(screenX, screenY, origin, dir); 36 39 37 40 CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY); 38 41 ENSURE(cmpRangeManager); 39 42 43 SpatialQueryArray ents; 44 CVector3D pos3d = camera.GetWorldCoordinates(screenX, screenY, true); 45 // Change the position to 2D by just dropping the Z. 46 CFixedVector2D pos(fixed::FromFloat(pos3d.X), fixed::FromFloat(pos3d.Z)); 47 cmpRangeManager->GetSubdivision()->GetNear(ents, pos, entity_pos_t::FromInt(range)); // TODO, Don't just use an arbitrary number. 48 40 49 std::vector<std::pair<float, entity_id_t> > hits; // (dist^2, entity) pairs 41 42 const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable); 43 for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it) 50 for (int i = 0; i != ents.size(); ++i) 44 51 { 45 entity_id_t ent = it->first; 46 CEntityHandle handle = it->second->GetEntityHandle(); 52 CmpPtr<ICmpSelectable> cmpSelectable(simulation, ents[i]); 53 if (!cmpSelectable) 54 continue; 47 55 56 CEntityHandle handle = cmpSelectable->GetEntityHandle(); 57 48 58 // Check if this entity is only selectable in Atlas 49 if (!allowEditorSelectables && static_cast<ICmpSelectable*>(it->second)->IsEditorOnly())59 if (!allowEditorSelectables && cmpSelectable->IsEditorOnly()) 50 60 continue; 51 61 52 62 // Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld) … … 91 101 CVector3D closest = origin + dir * (center - origin).Dot(dir); 92 102 dist2 = (closest - center).LengthSquared(); 93 103 94 hits.push_back(std::make_pair(dist2, ent ));104 hits.push_back(std::make_pair(dist2, ents[i])); 95 105 } 96 106 97 107 // Sort hits by distance … … 148 158 CVector3D position = cmpVisual->GetPosition(); 149 159 150 160 // Reject if it's not on-screen (e.g. it's behind the camera) 151 152 161 if (!camera.GetFrustum().IsPointVisible(position)) 153 162 continue; 154 163 -
source/simulation2/helpers/Selection.h
43 43 * this value is ignored as the whole map is revealed. 44 44 * @param allowEditorSelectables if true, all entities with the ICmpSelectable interface 45 45 * will be selected (including decorative actors), else only those selectable ingame. 46 * @param range Approximate range to check for entity in. 46 47 * 47 48 * @return ordered list of selected entities with the closest first. 48 49 */ 49 std::vector<entity_id_t> PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables );50 std::vector<entity_id_t> PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, player_id_t player, bool allowEditorSelectables, int range); 50 51 51 52 /** 52 53 * Finds all selectable entities within the given screen coordinate rectangle, -
source/simulation2/helpers/Spatial.h
18 18 #ifndef INCLUDED_SPATIAL 19 19 #define INCLUDED_SPATIAL 20 20 21 #include "simulation2/system/Component.h" 21 22 #include "simulation2/serialization/SerializeTemplates.h" 22 23 #include "ps/CLogger.h" 23 24 -
source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp
442 442 443 443 // Normally this function would be called with a player ID to check LOS, 444 444 // but in Atlas the entire map is revealed, so just pass INVALID_PLAYER 445 std::vector<entity_id_t> ents = EntitySelection::PickEntitiesAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y, INVALID_PLAYER, msg->selectActors );445 std::vector<entity_id_t> ents = EntitySelection::PickEntitiesAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y, INVALID_PLAYER, msg->selectActors, 200); 446 446 447 447 // Multiple entities may have been picked, but they are sorted by distance, 448 448 // so only take the first one